Add alpha blending and alpha for primitives
This commit is contained in:
parent
afcd158d7d
commit
872ee9efa4
@ -33,23 +33,65 @@ typedef struct DRState
|
|||||||
DRFont font;
|
DRFont font;
|
||||||
} DRState;
|
} DRState;
|
||||||
|
|
||||||
FILE_SCOPE inline void SetPixel(PlatformRenderBuffer *const renderBuffer,
|
FILE_SCOPE inline DqnV4 PreMultiplyAlpha(DqnV4 color)
|
||||||
const i32 x, const i32 y, DqnV3 color)
|
|
||||||
{
|
{
|
||||||
|
DqnV4 result;
|
||||||
|
f32 normA = color.a / 255.0f;
|
||||||
|
result.r = color.r * normA;
|
||||||
|
result.g = color.g * normA;
|
||||||
|
result.b = color.b * normA;
|
||||||
|
result.a = color.a;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IMPORTANT(doyle): Color is expected to be premultiplied already
|
||||||
|
FILE_SCOPE inline void SetPixel(PlatformRenderBuffer *const renderBuffer,
|
||||||
|
const i32 x, const i32 y, const DqnV4 color)
|
||||||
|
{
|
||||||
|
|
||||||
if (!renderBuffer) return;
|
if (!renderBuffer) return;
|
||||||
if (x < 0 || x > renderBuffer->width - 1) return;
|
if (x < 0 || x > renderBuffer->width - 1) return;
|
||||||
if (y < 0 || y > renderBuffer->height - 1) return;
|
if (y < 0 || y > renderBuffer->height - 1) return;
|
||||||
|
|
||||||
u32 *const bitmapPtr = (u32 *)renderBuffer->memory;
|
u32 *const bitmapPtr = (u32 *)renderBuffer->memory;
|
||||||
const u32 pitchInU32 = (renderBuffer->width * renderBuffer->bytesPerPixel) / 4;
|
const u32 pitchInU32 = (renderBuffer->width * renderBuffer->bytesPerPixel) / 4;
|
||||||
u32 pixel = ((i32)color.r << 16) | ((i32)color.g << 8) | ((i32)color.b << 0);
|
|
||||||
|
f32 newA = color.a;
|
||||||
|
f32 newANorm = newA / 255.0f;
|
||||||
|
f32 newR = color.r;
|
||||||
|
f32 newG = color.g;
|
||||||
|
f32 newB = color.b;
|
||||||
|
|
||||||
|
u32 src = bitmapPtr[x + (y * pitchInU32)];
|
||||||
|
f32 srcR = (f32)((src >> 16) & 0xFF);
|
||||||
|
f32 srcG = (f32)((src >> 8) & 0xFF);
|
||||||
|
f32 srcB = (f32)((src >> 0) & 0xFF);
|
||||||
|
|
||||||
|
// NOTE(doyle): AlphaBlend equations is (alpha * new) + (1 - alpha) * src.
|
||||||
|
// IMPORTANT(doyle): We pre-multiply so we can take out the (alpha * new)
|
||||||
|
f32 invANorm = 1 - newANorm;
|
||||||
|
// f32 destA = (((1 - srcA) * newA) + srcA) * 255.0f;
|
||||||
|
f32 destR = newR + (invANorm * srcR);
|
||||||
|
f32 destG = newG + (invANorm * srcG);
|
||||||
|
f32 destB = newB + (invANorm * srcB);
|
||||||
|
|
||||||
|
// DQN_ASSERT(destA >= 0 && destA <= 255.0f);
|
||||||
|
DQN_ASSERT(destR >= 0 && destR <= 255.0f);
|
||||||
|
DQN_ASSERT(destG >= 0 && destG <= 255.0f);
|
||||||
|
DQN_ASSERT(destB >= 0 && destB <= 255.0f);
|
||||||
|
|
||||||
|
u32 pixel = ((u32)(destR) << 16 |
|
||||||
|
(u32)(destG) << 8 |
|
||||||
|
(u32)(destB) << 0);
|
||||||
bitmapPtr[x + (y * pitchInU32)] = pixel;
|
bitmapPtr[x + (y * pitchInU32)] = pixel;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE_SCOPE void DrawLine(PlatformRenderBuffer *const renderBuffer, DqnV2i a,
|
FILE_SCOPE void DrawLine(PlatformRenderBuffer *const renderBuffer, DqnV2i a,
|
||||||
DqnV2i b, DqnV3 color)
|
DqnV2i b, DqnV4 color)
|
||||||
{
|
{
|
||||||
if (!renderBuffer) return;
|
if (!renderBuffer) return;
|
||||||
|
color = PreMultiplyAlpha(color);
|
||||||
|
|
||||||
bool yTallerThanX = false;
|
bool yTallerThanX = false;
|
||||||
if (DQN_ABS(a.x - b.x) < DQN_ABS(a.y - b.y))
|
if (DQN_ABS(a.x - b.x) < DQN_ABS(a.y - b.y))
|
||||||
@ -107,9 +149,10 @@ FILE_SCOPE void DrawLine(PlatformRenderBuffer *const renderBuffer, DqnV2i a,
|
|||||||
|
|
||||||
FILE_SCOPE void DrawTriangle(PlatformRenderBuffer *const renderBuffer,
|
FILE_SCOPE void DrawTriangle(PlatformRenderBuffer *const renderBuffer,
|
||||||
const DqnV2 p1, const DqnV2 p2, const DqnV2 p3,
|
const DqnV2 p1, const DqnV2 p2, const DqnV2 p3,
|
||||||
const DqnV3 color)
|
DqnV4 color)
|
||||||
{
|
{
|
||||||
__rdtsc();
|
color = PreMultiplyAlpha(color);
|
||||||
|
|
||||||
DqnV2i max = DqnV2i_2f(DQN_MAX(DQN_MAX(p1.x, p2.x), p3.x),
|
DqnV2i max = DqnV2i_2f(DQN_MAX(DQN_MAX(p1.x, p2.x), p3.x),
|
||||||
DQN_MAX(DQN_MAX(p1.y, p2.y), p3.y));
|
DQN_MAX(DQN_MAX(p1.y, p2.y), p3.y));
|
||||||
DqnV2i min = DqnV2i_2f(DQN_MIN(DQN_MIN(p1.x, p2.x), p3.x),
|
DqnV2i min = DqnV2i_2f(DQN_MIN(DQN_MIN(p1.x, p2.x), p3.x),
|
||||||
@ -150,8 +193,15 @@ FILE_SCOPE void DrawTriangle(PlatformRenderBuffer *const renderBuffer,
|
|||||||
determine whether a point lies on the line, or is to the left or right of
|
determine whether a point lies on the line, or is to the left or right of
|
||||||
a the line.
|
a the line.
|
||||||
|
|
||||||
First forming a 3x3 matrix of our terms and deriving a 2x2 matrix by
|
Then you can imagine if we iterate over the triangle vertexes, 2 at at
|
||||||
subtracting the 1st column from the 2nd and 1st column from the third.
|
time and a last point P, being the point we want to test if it's inside
|
||||||
|
the triangle, then if the point is considered to lie on the side of the
|
||||||
|
line forming the interior of the triangle for all 3 vertexes then the
|
||||||
|
point is inside the triangle. We can do this using the determinant.
|
||||||
|
|
||||||
|
First forming a 3x3 matrix of our terms with a, b being from the triangle
|
||||||
|
and test point c, we can derive a 2x2 matrix by subtracting the 1st
|
||||||
|
column from the 2nd and 1st column from the third.
|
||||||
|
|
||||||
| ax bx cx | | (bx - ax) (cx - ax) |
|
| ax bx cx | | (bx - ax) (cx - ax) |
|
||||||
m = | ay by cy | ==> | (by - ay) (cy - ay) |
|
m = | ay by cy | ==> | (by - ay) (cy - ay) |
|
||||||
@ -163,10 +213,10 @@ FILE_SCOPE void DrawTriangle(PlatformRenderBuffer *const renderBuffer,
|
|||||||
det(m) = (bx - ax)(cy - ay) - (by - ay)(cx - ax)
|
det(m) = (bx - ax)(cy - ay) - (by - ay)(cx - ax)
|
||||||
|
|
||||||
Depending on the order of the vertices supplied, if it's
|
Depending on the order of the vertices supplied, if it's
|
||||||
- CCW and c(x,y) is outside the triangle, the signed area is negative
|
- CCW and c(x,y) is outside the line (triangle), the signed area is negative
|
||||||
- CCW and c(x,y) is inside the triangle, the signed area is positive
|
- CCW and c(x,y) is inside the line (triangle), the signed area is positive
|
||||||
- CW and c(x,y) is outside the triangle, the signed area is positive
|
- CW and c(x,y) is outside the line (triangle), the signed area is positive
|
||||||
- CW and c(x,y) is inside the triangle, the signed area is negative
|
- CW and c(x,y) is inside the line (triangle), the signed area is negative
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
// Optimising the Determinant Calculation
|
// Optimising the Determinant Calculation
|
||||||
@ -236,9 +286,9 @@ FILE_SCOPE void DrawTriangle(PlatformRenderBuffer *const renderBuffer,
|
|||||||
This is normalised to the area's sum, but we can trivially turn this into
|
This is normalised to the area's sum, but we can trivially turn this into
|
||||||
a normalised version by dividing the area of the parallelogram, i.e.
|
a normalised version by dividing the area of the parallelogram, i.e.
|
||||||
|
|
||||||
BaryCentricA(P) = (SignedArea(P) w. vertex C and B)/SignedArea(of the orig triangle)
|
BaryCentricC(P) = (SignedArea(P) with vertex A and B)/SignedArea(with the orig triangle vertex)
|
||||||
BaryCentricB(P) = (SignedArea(P) w. vertex A and C)/SignedArea(of the orig triangle)
|
BaryCentricA(P) = (SignedArea(P) with vertex B and C)/SignedArea(with the orig triangle vertex)
|
||||||
BaryCentricC(P) = (SignedArea(P) w. vertex A and B)/SignedArea(of the orig triangle)
|
BaryCentricB(P) = (SignedArea(P) with vertex C and A)/SignedArea(with the orig triangle vertex)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DqnV2i scanP = DqnV2i_2i(min.x, min.y);
|
DqnV2i scanP = DqnV2i_2i(min.x, min.y);
|
||||||
@ -254,6 +304,8 @@ FILE_SCOPE void DrawTriangle(PlatformRenderBuffer *const renderBuffer,
|
|||||||
f32 signedArea3DeltaX = c.y - a.y;
|
f32 signedArea3DeltaX = c.y - a.y;
|
||||||
f32 signedArea3DeltaY = a.x - c.x;
|
f32 signedArea3DeltaY = a.x - c.x;
|
||||||
|
|
||||||
|
f32 invSignedAreaParallelogram = 1 / ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x));
|
||||||
|
|
||||||
for (scanP.y = min.y; scanP.y < max.y; scanP.y++)
|
for (scanP.y = min.y; scanP.y < max.y; scanP.y++)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -281,12 +333,13 @@ FILE_SCOPE void DrawTriangle(PlatformRenderBuffer *const renderBuffer,
|
|||||||
|
|
||||||
FILE_SCOPE void DrawText(PlatformRenderBuffer *const renderBuffer,
|
FILE_SCOPE void DrawText(PlatformRenderBuffer *const renderBuffer,
|
||||||
const DRFont font, DqnV2 pos, const char *const text,
|
const DRFont font, DqnV2 pos, const char *const text,
|
||||||
i32 len = -1)
|
DqnV4 color = DqnV4_4f(255, 255, 255, 255), i32 len = -1)
|
||||||
{
|
{
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
if (len == -1) len = Dqn_strlen(text);
|
if (len == -1) len = Dqn_strlen(text);
|
||||||
|
|
||||||
i32 index = 0;
|
i32 index = 0;
|
||||||
|
color = PreMultiplyAlpha(color);
|
||||||
while (index < len)
|
while (index < len)
|
||||||
{
|
{
|
||||||
if (text[index] < font.codepointRange.min &&
|
if (text[index] < font.codepointRange.min &&
|
||||||
@ -335,29 +388,19 @@ FILE_SCOPE void DrawText(PlatformRenderBuffer *const renderBuffer,
|
|||||||
for (i32 x = 0; x < fontWidth; x++)
|
for (i32 x = 0; x < fontWidth; x++)
|
||||||
{
|
{
|
||||||
i32 yOffset = fontHeight - y;
|
i32 yOffset = fontHeight - y;
|
||||||
u8 fontPixel = fontPtr[x + (yOffset * fontPitch)];
|
u8 srcA = fontPtr[x + (yOffset * fontPitch)];
|
||||||
if (fontPixel == 0) continue;
|
if (srcA == 0) continue;
|
||||||
|
|
||||||
f32 srcA = (fontPixel / 255.0f);
|
f32 srcANorm = srcA / 255.0f;
|
||||||
f32 srcR = (fontPixel / 255.0f);
|
DqnV4 resultColor = {};
|
||||||
f32 srcG = (fontPixel / 255.0f);
|
resultColor.r = color.r * srcANorm;
|
||||||
f32 srcB = (fontPixel / 255.0f);
|
resultColor.g = color.g * srcANorm;
|
||||||
|
resultColor.b = color.b * srcANorm;
|
||||||
|
resultColor.a = color.a * srcANorm;
|
||||||
|
|
||||||
u32 offset = x + (y * renderBuffer->width);
|
i32 actualX = (i32)(screenRect.min.x + x);
|
||||||
u32 old = screenPtr[offset];
|
i32 actualY = (i32)(screenRect.min.y + y - fontHeightOffset);
|
||||||
f32 oldR = (f32)((old >> 16) & 0xFF) / 255.0f;
|
SetPixel(renderBuffer, actualX, actualY, resultColor);
|
||||||
f32 oldG = (f32)((old >> 8) & 0xFF) / 255.0f;
|
|
||||||
f32 oldB = (f32)((old >> 0) & 0xFF) / 255.0f;
|
|
||||||
|
|
||||||
f32 invA = 1 - srcA;
|
|
||||||
f32 destR = srcR + (invA * oldR);
|
|
||||||
f32 destG = srcG + (invA * oldG);
|
|
||||||
f32 destB = srcB + (invA * oldB);
|
|
||||||
u32 dest = ((u32)(destR * 255.0f) << 16) |
|
|
||||||
((u32)(destG * 255.0f) << 8) |
|
|
||||||
((u32)(destB * 255.0f) << 0);
|
|
||||||
|
|
||||||
screenPtr[offset] = dest;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -451,6 +494,7 @@ FILE_SCOPE void BitmapFontCreate(const PlatformAPI api,
|
|||||||
f32 alpha = (f32)(font->bitmap[index]) / 255.0f;
|
f32 alpha = (f32)(font->bitmap[index]) / 255.0f;
|
||||||
f32 color = alpha;
|
f32 color = alpha;
|
||||||
f32 preMulAlphaColor = color * alpha;
|
f32 preMulAlphaColor = color * alpha;
|
||||||
|
DQN_ASSERT(preMulAlphaColor >= 0.0f && preMulAlphaColor <= 255.0f);
|
||||||
|
|
||||||
font->bitmap[index] = (u8)(preMulAlphaColor * 255.0f);
|
font->bitmap[index] = (u8)(preMulAlphaColor * 255.0f);
|
||||||
}
|
}
|
||||||
@ -480,8 +524,8 @@ extern "C" void DR_Update(PlatformRenderBuffer *const renderBuffer,
|
|||||||
DqnV2i_2i(256, 256), DqnV2i_2i(' ', '~'), 35);
|
DqnV2i_2i(256, 256), DqnV2i_2i(' ', '~'), 35);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearRenderBuffer(renderBuffer, DqnV3_3f(0, 0, 0));
|
ClearRenderBuffer(renderBuffer, DqnV3_3f(0, 255, 0));
|
||||||
DqnV3 colorRed = DqnV3_3i(255, 0, 0);
|
DqnV4 colorRed = DqnV4_4i(50, 0, 0, 255);
|
||||||
DqnV2i bufferMidP = DqnV2i_2f(renderBuffer->width * 0.5f, renderBuffer->height * 0.5f);
|
DqnV2i bufferMidP = DqnV2i_2f(renderBuffer->width * 0.5f, renderBuffer->height * 0.5f);
|
||||||
i32 boundsOffset = 50;
|
i32 boundsOffset = 50;
|
||||||
|
|
||||||
@ -497,7 +541,7 @@ extern "C" void DR_Update(PlatformRenderBuffer *const renderBuffer,
|
|||||||
f32 maxX = 0;
|
f32 maxX = 0;
|
||||||
for (i32 i = 0; i < DQN_ARRAY_COUNT(t3); i++)
|
for (i32 i = 0; i < DQN_ARRAY_COUNT(t3); i++)
|
||||||
{
|
{
|
||||||
t3[i].x += input->deltaForFrame * 2.0f;
|
// t3[i].x += input->deltaForFrame * 2.0f;
|
||||||
minX = DQN_MIN(t3[i].x, minX);
|
minX = DQN_MIN(t3[i].x, minX);
|
||||||
maxX = DQN_MAX(t3[i].x, maxX);
|
maxX = DQN_MAX(t3[i].x, maxX);
|
||||||
}
|
}
|
||||||
@ -511,11 +555,14 @@ extern "C" void DR_Update(PlatformRenderBuffer *const renderBuffer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DqnV2 fontP = DqnV2_2i(200, 180);
|
||||||
|
DrawText(renderBuffer, state->font, fontP, "hello world!");
|
||||||
|
|
||||||
DrawTriangle(renderBuffer, t0[0], t0[1], t0[2], colorRed);
|
DrawTriangle(renderBuffer, t0[0], t0[1], t0[2], colorRed);
|
||||||
DrawTriangle(renderBuffer, t1[0], t1[1], t1[2], colorRed);
|
DrawTriangle(renderBuffer, t1[0], t1[1], t1[2], colorRed);
|
||||||
DrawTriangle(renderBuffer, t2[0], t2[1], t2[2], colorRed);
|
DrawTriangle(renderBuffer, t2[0], t2[1], t2[2], colorRed);
|
||||||
// DrawTriangle(renderBuffer, t3[0], t3[1], t3[2], colorRed);
|
|
||||||
|
|
||||||
DqnV2 fontP = DqnV2_2i(200, 180);
|
DqnV4 colorRedHalfA = DqnV4_4i(255, 0, 0, 190);
|
||||||
DrawText(renderBuffer, state->font, fontP, "hello world!");
|
DrawTriangle(renderBuffer, t3[0], t3[1], t3[2], colorRedHalfA);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,7 @@ FILE_SCOPE void Win32DisplayRenderBitmap(Win32RenderBitmap renderBitmap,
|
|||||||
HDC deviceContext, LONG width,
|
HDC deviceContext, LONG width,
|
||||||
LONG height)
|
LONG height)
|
||||||
{
|
{
|
||||||
|
#if 0
|
||||||
HDC stretchDC = CreateCompatibleDC(deviceContext);
|
HDC stretchDC = CreateCompatibleDC(deviceContext);
|
||||||
SelectObject(stretchDC, renderBitmap.handle);
|
SelectObject(stretchDC, renderBitmap.handle);
|
||||||
// DQN_ASSERT(renderBitmap.width == width);
|
// DQN_ASSERT(renderBitmap.width == width);
|
||||||
@ -117,6 +118,11 @@ FILE_SCOPE void Win32DisplayRenderBitmap(Win32RenderBitmap renderBitmap,
|
|||||||
StretchBlt(deviceContext, 0, 0, width, height, stretchDC, 0, 0,
|
StretchBlt(deviceContext, 0, 0, width, height, stretchDC, 0, 0,
|
||||||
renderBitmap.width, renderBitmap.height, SRCCOPY);
|
renderBitmap.width, renderBitmap.height, SRCCOPY);
|
||||||
DeleteDC(stretchDC);
|
DeleteDC(stretchDC);
|
||||||
|
#else
|
||||||
|
StretchDIBits(deviceContext, 0, 0, width, height, 0, 0, renderBitmap.width,
|
||||||
|
renderBitmap.height, renderBitmap.memory,
|
||||||
|
&renderBitmap.info, DIB_RGB_COLORS, SRCCOPY);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
FILETIME Win32GetLastWriteTime(const char *const srcName)
|
FILETIME Win32GetLastWriteTime(const char *const srcName)
|
||||||
|
Loading…
Reference in New Issue
Block a user