All render paths go through srgb->linear pipeline

This commit is contained in:
Doyle Thai 2017-05-19 02:21:44 +10:00
parent f040c377e9
commit 1d0e9b8a07
5 changed files with 173 additions and 105 deletions

View File

@ -94,10 +94,13 @@ FILE_SCOPE bool BitmapFontCreate(const PlatformAPI api,
u32 index = x + (y * bitmapDim.w); u32 index = x + (y * bitmapDim.w);
f32 alpha = (f32)(loadedFont.bitmap[index]) / 255.0f; f32 alpha = (f32)(loadedFont.bitmap[index]) / 255.0f;
f32 color = alpha; f32 color = alpha;
f32 preMulAlphaColor = color * alpha;
DQN_ASSERT(preMulAlphaColor >= 0.0f && preMulAlphaColor <= 255.0f);
loadedFont.bitmap[index] = (u8)(preMulAlphaColor * 255.0f); color = DTRRender_SRGB1ToLinearSpacef(color);
color = color * alpha;
color = DTRRender_LinearToSRGB1Spacef(color) * 255.0f;
DQN_ASSERT(color >= 0.0f && color <= 255.0f);
loadedFont.bitmap[index] = (u8)color;
} }
} }
@ -152,7 +155,10 @@ FILE_SCOPE bool BitmapLoad(const PlatformAPI api, DTRBitmap *bitmap,
color.b = (f32)((pixel >> 16) & 0xFF); color.b = (f32)((pixel >> 16) & 0xFF);
color.g = (f32)((pixel >> 8) & 0xFF); color.g = (f32)((pixel >> 8) & 0xFF);
color.r = (f32)((pixel >> 0) & 0xFF); color.r = (f32)((pixel >> 0) & 0xFF);
color = DTRRender_PreMultiplyAlpha255(color);
color *= DTRRENDER_INV_255;
color = DTRRender_PreMultiplyAlphaSRGB1WithLinearConversion(color);
color *= 255.0f;
pixel = (((u32)color.a << 24) | pixel = (((u32)color.a << 24) |
((u32)color.b << 16) | ((u32)color.b << 16) |
@ -1074,20 +1080,25 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const renderBuffer,
DTRRender_Triangle(renderBuffer, t1[0], t1[1], t1[2], colorRed); DTRRender_Triangle(renderBuffer, t1[0], t1[1], t1[2], colorRed);
DTRRender_Triangle(renderBuffer, t2[0], t2[1], t2[2], colorRed); DTRRender_Triangle(renderBuffer, t2[0], t2[1], t2[2], colorRed);
DqnV4 colorRedHalfA = DqnV4_4f(1, 0, 0, 0.25f); DqnV4 colorRedHalfA = DqnV4_4f(1, 0, 0, 0.1f);
LOCAL_PERSIST f32 rotation = 0; LOCAL_PERSIST f32 rotation = 0;
rotation += input->deltaForFrame * 0.25f; rotation += input->deltaForFrame * 0.25f;
DTRRenderTransform triTransform = DTRRender_DefaultTriangleTransform();
triTransform.rotation = rotation;
DTRRender_Triangle(renderBuffer, t3[0], t3[1], t3[2], colorRedHalfA, triTransform);
DTRRenderTransform defaultTransform = DTRRender_DefaultTransform(); DTRRenderTransform defaultTransform = DTRRender_DefaultTransform();
defaultTransform.rotation = rotation + 45; defaultTransform.rotation = rotation + 45;
DTRRender_Rectangle(renderBuffer, DqnV2_1f(300.0f), DqnV2_1f(300 + 20.0f), colorRed, DTRRender_Rectangle(renderBuffer, DqnV2_1f(300.0f), DqnV2_1f(300 + 20.0f), DqnV4_4f(0, 1.0f, 1.0f, 1.0f),
defaultTransform); defaultTransform);
// Rotating triangle
{
DTRRenderTransform triTransform = DTRRender_DefaultTriangleTransform();
triTransform.rotation = rotation;
DTRRender_Triangle(renderBuffer, t3[0], t3[1], t3[2], colorRedHalfA, triTransform);
}
DqnV2 fontP = DqnV2_2i(200, 180); DqnV2 fontP = DqnV2_2i(200, 180);
DTRRender_Text(renderBuffer, state->font, fontP, "hello world!"); DTRRender_Text(renderBuffer, state->font, fontP, "hello world!", DqnV4_4f(0, 0, 0, 1));
DTRRenderTransform transform = DTRRender_DefaultTransform(); DTRRenderTransform transform = DTRRender_DefaultTransform();
transform.rotation = rotation * 2.0f; transform.rotation = rotation * 2.0f;
@ -1096,7 +1107,11 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const renderBuffer,
LOCAL_PERSIST DqnV2 bitmapP = DqnV2_2f(300, 250); LOCAL_PERSIST DqnV2 bitmapP = DqnV2_2f(300, 250);
bitmapP.x += 3.0f * sinf((f32)input->timeNowInS * 0.5f); bitmapP.x += 3.0f * sinf((f32)input->timeNowInS * 0.5f);
DTRRender_Bitmap(renderBuffer, &state->bitmap, bitmapP, transform); f32 cAngle = (f32)input->timeNowInS;
DqnV4 color = DqnV4_4f(0.5f + 0.5f * sinf(cAngle), 0.5f + 0.5f * sinf(2.9f * cAngle),
0.5f + 0.5f * cosf(9.9f * cAngle), 1.0f);
DTRRender_Bitmap(renderBuffer, &state->bitmap, bitmapP, transform, color);
#else #else
CompAssignment(renderBuffer, input, memory); CompAssignment(renderBuffer, input, memory);
#endif #endif

View File

@ -22,7 +22,8 @@ void DTRDebug_PushText(const char *const formatStr, ...)
} }
va_end(argList); va_end(argList);
DTRRender_Text(debug->renderBuffer, *debug->font, debug->displayP, str); DTRRender_Text(debug->renderBuffer, *debug->font, debug->displayP, str,
debug->displayColor);
debug->displayP.y += globalDebug.displayYOffset; debug->displayP.y += globalDebug.displayYOffset;
} }
} }
@ -57,7 +58,7 @@ FILE_SCOPE void PushMemBufferText(const char *const name,
totalUsed, totalSize); totalUsed, totalSize);
DTRRender_Text(globalDebug.renderBuffer, *globalDebug.font, DTRRender_Text(globalDebug.renderBuffer, *globalDebug.font,
globalDebug.displayP, str); globalDebug.displayP, str, globalDebug.displayColor);
globalDebug.displayP.y += globalDebug.displayYOffset; globalDebug.displayP.y += globalDebug.displayYOffset;
} }
} }
@ -72,6 +73,7 @@ void DTRDebug_Update(DTRState *const state,
debug->renderBuffer = renderBuffer; debug->renderBuffer = renderBuffer;
debug->font = &state->font; debug->font = &state->font;
debug->displayColor = DqnV4_4f(1, 1, 1, 1);
if (debug->font->bitmap && debug->renderBuffer) if (debug->font->bitmap && debug->renderBuffer)
{ {
debug->displayYOffset = -(i32)(state->font.sizeInPt + 0.5f); debug->displayYOffset = -(i32)(state->font.sizeInPt + 0.5f);
@ -85,7 +87,8 @@ void DTRDebug_Update(DTRState *const state,
{ {
char str[128] = {}; char str[128] = {};
Dqn_sprintf(str, "%s: %'lld", "TotalSetPixels", debug->totalSetPixels); Dqn_sprintf(str, "%s: %'lld", "TotalSetPixels", debug->totalSetPixels);
DTRRender_Text(debug->renderBuffer, *debug->font, debug->displayP, str); DTRRender_Text(debug->renderBuffer, *debug->font, debug->displayP, str,
debug->displayColor);
debug->displayP.y += globalDebug.displayYOffset; debug->displayP.y += globalDebug.displayYOffset;
} }
@ -93,7 +96,8 @@ void DTRDebug_Update(DTRState *const state,
{ {
char str[128] = {}; char str[128] = {};
Dqn_sprintf(str, "%s: %'lld", "SetPixelsPerFrame", debug->setPixelsPerFrame); Dqn_sprintf(str, "%s: %'lld", "SetPixelsPerFrame", debug->setPixelsPerFrame);
DTRRender_Text(debug->renderBuffer, *debug->font, debug->displayP, str); DTRRender_Text(debug->renderBuffer, *debug->font, debug->displayP, str,
debug->displayColor);
debug->displayP.y += globalDebug.displayYOffset; debug->displayP.y += globalDebug.displayYOffset;
} }

View File

@ -4,6 +4,9 @@
#include "dqn.h" #include "dqn.h"
#define DTR_DEBUG 1 #define DTR_DEBUG 1
#ifdef DTR_DEBUG
#define DTR_DEBUG_RENDER 0
#endif
typedef struct PlatformRenderBuffer PlatformRenderBuffer; typedef struct PlatformRenderBuffer PlatformRenderBuffer;
typedef struct DTRFont DTRFont; typedef struct DTRFont DTRFont;
@ -16,6 +19,7 @@ typedef struct DTRDebug
DTRFont *font; DTRFont *font;
PlatformRenderBuffer *renderBuffer; PlatformRenderBuffer *renderBuffer;
DqnV4 displayColor;
DqnV2 displayP; DqnV2 displayP;
i32 displayYOffset; i32 displayYOffset;

View File

@ -12,9 +12,9 @@
#include "external/stb_image_write.h" #include "external/stb_image_write.h"
#endif #endif
#define DTRRENDER_INV_255 0.00392156862f; FILE_SCOPE const f32 COLOR_EPSILON = 0.9f;
inline DqnV4 DTRRender_PreMultiplyAlpha1(const DqnV4 color) FILE_SCOPE inline DqnV4 PreMultiplyAlpha1(const DqnV4 color)
{ {
DqnV4 result; DqnV4 result;
result.r = color.r * color.a; result.r = color.r * color.a;
@ -29,10 +29,11 @@ inline DqnV4 DTRRender_PreMultiplyAlpha1(const DqnV4 color)
return result; return result;
} }
inline DqnV4 DTRRender_PreMultiplyAlpha255(const DqnV4 color) FILE_SCOPE inline DqnV4 PreMultiplyAlpha255(const DqnV4 color)
{ {
DqnV4 result; DqnV4 result;
f32 normA = color.a * DTRRENDER_INV_255; f32 normA = color.a * DTRRENDER_INV_255;
DQN_ASSERT(normA >= 0.0f && normA <= 1.0f + COLOR_EPSILON);
result.r = color.r * normA; result.r = color.r * normA;
result.g = color.g * normA; result.g = color.g * normA;
result.b = color.b * normA; result.b = color.b * normA;
@ -41,56 +42,68 @@ inline DqnV4 DTRRender_PreMultiplyAlpha255(const DqnV4 color)
return result; return result;
} }
enum ColourSpace enum ColorSpace
{ {
ColourSpace_SRGB, ColorSpace_SRGB,
ColourSpace_Linear, ColorSpace_Linear,
}; };
// NOTE(doyle): We are approximating the actual gamma correct value 2.2 to 2 as // NOTE(doyle): We are approximating the actual gamma correct value 2.2 to 2 as
// a compromise. // a compromise.
FILE_SCOPE inline f32 SRGB1ToLinearSpacef(f32 val) inline f32 DTRRender_SRGB1ToLinearSpacef(f32 val)
{ {
DQN_ASSERT(val >= 0.0f && val <= 1.0f + COLOR_EPSILON);
f32 result = DQN_SQUARED(val); f32 result = DQN_SQUARED(val);
return result; return result;
} }
FILE_SCOPE inline DqnV4 SRGB1ToLinearSpaceV4(DqnV4 color) inline DqnV4 DTRRender_SRGB1ToLinearSpaceV4(DqnV4 color)
{ {
DqnV4 result = {}; DqnV4 result = {};
result.r = SRGB1ToLinearSpacef(color.r); result.r = DTRRender_SRGB1ToLinearSpacef(color.r);
result.g = SRGB1ToLinearSpacef(color.g); result.g = DTRRender_SRGB1ToLinearSpacef(color.g);
result.b = SRGB1ToLinearSpacef(color.b); result.b = DTRRender_SRGB1ToLinearSpacef(color.b);
result.a = SRGB1ToLinearSpacef(color.a); result.a = color.a;
return result; return result;
} }
FILE_SCOPE inline f32 LinearToSRGB1Spacef(f32 val) inline f32 DTRRender_LinearToSRGB1Spacef(f32 val)
{ {
DQN_ASSERT(val >= 0.0f && val <= 1.0f + COLOR_EPSILON);
if (val == 0) return 0; if (val == 0) return 0;
f32 result = DqnMath_Sqrtf(val); f32 result = DqnMath_Sqrtf(val);
return result; return result;
} }
FILE_SCOPE inline DqnV4 LinearToSRGB1SpaceV4(DqnV4 color) inline DqnV4 DTRRender_LinearToSRGB1SpaceV4(DqnV4 color)
{ {
DqnV4 result = {}; DqnV4 result = {};
result.r = LinearToSRGB1Spacef(color.r); result.r = DTRRender_LinearToSRGB1Spacef(color.r);
result.g = LinearToSRGB1Spacef(color.g); result.g = DTRRender_LinearToSRGB1Spacef(color.g);
result.b = LinearToSRGB1Spacef(color.b); result.b = DTRRender_LinearToSRGB1Spacef(color.b);
result.a = LinearToSRGB1Spacef(color.a); result.a = color.a;
return result;
}
inline DqnV4 DTRRender_PreMultiplyAlphaSRGB1WithLinearConversion(DqnV4 color)
{
DqnV4 result = color;
result = DTRRender_SRGB1ToLinearSpaceV4(result);
result = PreMultiplyAlpha1(result);
result = DTRRender_LinearToSRGB1SpaceV4(result);
return result; return result;
} }
// IMPORTANT(doyle): Color is expected to be premultiplied already // IMPORTANT(doyle): Color is expected to be premultiplied already
FILE_SCOPE inline void SetPixel(PlatformRenderBuffer *const renderBuffer, const i32 x, const i32 y, FILE_SCOPE inline void SetPixel(PlatformRenderBuffer *const renderBuffer, const i32 x, const i32 y,
DqnV4 color, const enum ColourSpace colourSpace = ColourSpace_SRGB) DqnV4 color, const enum ColorSpace colorSpace = ColorSpace_SRGB)
{ {
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;
@ -99,11 +112,8 @@ FILE_SCOPE inline void SetPixel(PlatformRenderBuffer *const renderBuffer, const
// new pixel is totally opaque or invisible then we're just flat out // new pixel is totally opaque or invisible then we're just flat out
// overwriting/keeping the state of the pixel so we can save cycles by skipping. // overwriting/keeping the state of the pixel so we can save cycles by skipping.
#if 1 #if 1
bool needGammaFix = (color.a > 0.0f || color.a < 1.0f) && (colourSpace == ColourSpace_SRGB); bool needGammaFix = (color.a > 0.0f || color.a < 1.0f + COLOR_EPSILON) && (colorSpace == ColorSpace_SRGB);
if (needGammaFix) if (needGammaFix) color = DTRRender_SRGB1ToLinearSpaceV4(color);
{
color = SRGB1ToLinearSpaceV4(color);
}
#endif #endif
u32 src = bitmapPtr[x + (y * pitchInU32)]; u32 src = bitmapPtr[x + (y * pitchInU32)];
@ -111,9 +121,9 @@ FILE_SCOPE inline void SetPixel(PlatformRenderBuffer *const renderBuffer, const
f32 srcG = (f32)((src >> 8) & 0xFF) * DTRRENDER_INV_255; f32 srcG = (f32)((src >> 8) & 0xFF) * DTRRENDER_INV_255;
f32 srcB = (f32)((src >> 0) & 0xFF) * DTRRENDER_INV_255; f32 srcB = (f32)((src >> 0) & 0xFF) * DTRRENDER_INV_255;
srcR = SRGB1ToLinearSpacef(srcR); srcR = DTRRender_SRGB1ToLinearSpacef(srcR);
srcG = SRGB1ToLinearSpacef(srcG); srcG = DTRRender_SRGB1ToLinearSpacef(srcG);
srcB = SRGB1ToLinearSpacef(srcB); srcB = DTRRender_SRGB1ToLinearSpacef(srcB);
// NOTE(doyle): AlphaBlend equations is (alpha * new) + (1 - alpha) * src. // NOTE(doyle): AlphaBlend equations is (alpha * new) + (1 - alpha) * src.
// IMPORTANT(doyle): We pre-multiply so we can take out the (alpha * new) // IMPORTANT(doyle): We pre-multiply so we can take out the (alpha * new)
@ -122,24 +132,14 @@ FILE_SCOPE inline void SetPixel(PlatformRenderBuffer *const renderBuffer, const
f32 destG = color.g + (invANorm * srcG); f32 destG = color.g + (invANorm * srcG);
f32 destB = color.b + (invANorm * srcB); f32 destB = color.b + (invANorm * srcB);
#if 1 destR = DTRRender_LinearToSRGB1Spacef(destR) * 255.0f;
if (needGammaFix || colourSpace == ColourSpace_Linear) destG = DTRRender_LinearToSRGB1Spacef(destG) * 255.0f;
{ destB = DTRRender_LinearToSRGB1Spacef(destB) * 255.0f;
destR = LinearToSRGB1Spacef(destR);
destG = LinearToSRGB1Spacef(destG);
destB = LinearToSRGB1Spacef(destB);
}
#endif
destR *= 255.0f;
destG *= 255.0f;
destB *= 255.0f;
DQN_ASSERT(destR >= 0); DQN_ASSERT(destR >= 0);
DQN_ASSERT(destG >= 0); DQN_ASSERT(destG >= 0);
DQN_ASSERT(destB >= 0); DQN_ASSERT(destB >= 0);
const f32 COLOR_EPSILON = 0.1f;
if (destR > 255.0f) if (destR > 255.0f)
{ {
DQN_ASSERT((destR - 255.0f) < COLOR_EPSILON); DQN_ASSERT((destR - 255.0f) < COLOR_EPSILON);
@ -176,7 +176,8 @@ void DTRRender_Text(PlatformRenderBuffer *const renderBuffer,
if (len == -1) len = Dqn_strlen(text); if (len == -1) len = Dqn_strlen(text);
i32 index = 0; i32 index = 0;
color = DTRRender_PreMultiplyAlpha1(color); color = DTRRender_SRGB1ToLinearSpaceV4(color);
color = PreMultiplyAlpha1(color);
while (index < len) while (index < len)
{ {
if (text[index] < font.codepointRange.min && if (text[index] < font.codepointRange.min &&
@ -237,7 +238,7 @@ void DTRRender_Text(PlatformRenderBuffer *const renderBuffer,
i32 actualX = (i32)(screenRect.min.x + x); i32 actualX = (i32)(screenRect.min.x + x);
i32 actualY = (i32)(screenRect.min.y + y - fontHeightOffset); i32 actualY = (i32)(screenRect.min.y + y - fontHeightOffset);
SetPixel(renderBuffer, actualX, actualY, resultColor); SetPixel(renderBuffer, actualX, actualY, resultColor, ColorSpace_Linear);
} }
} }
} }
@ -265,7 +266,8 @@ void DTRRender_Line(PlatformRenderBuffer *const renderBuffer, DqnV2i a,
DqnV2i b, DqnV4 color) DqnV2i b, DqnV4 color)
{ {
if (!renderBuffer) return; if (!renderBuffer) return;
color = DTRRender_PreMultiplyAlpha1(color); color = DTRRender_SRGB1ToLinearSpaceV4(color);
color = PreMultiplyAlpha1(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))
@ -310,7 +312,7 @@ void DTRRender_Line(PlatformRenderBuffer *const renderBuffer, DqnV2i a,
for (i32 iterateX = 0; iterateX < numIterations; iterateX++) for (i32 iterateX = 0; iterateX < numIterations; iterateX++)
{ {
newX = a.x + iterateX; newX = a.x + iterateX;
SetPixel(renderBuffer, *plotX, *plotY, color); SetPixel(renderBuffer, *plotX, *plotY, color, ColorSpace_Linear);
distAccumulator += distFromPixelOrigin; distAccumulator += distFromPixelOrigin;
if (distAccumulator > run) if (distAccumulator > run)
@ -384,7 +386,8 @@ void DTRRender_Rectangle(PlatformRenderBuffer *const renderBuffer, DqnV2 min, Dq
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Transform vertexes // Transform vertexes
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
color = DTRRender_PreMultiplyAlpha1(color); color = DTRRender_SRGB1ToLinearSpaceV4(color);
color = PreMultiplyAlpha1(color);
RectPoints rectPoints = TransformRectPoints(min, max, transform); RectPoints rectPoints = TransformRectPoints(min, max, transform);
DqnV2 *const pList = &rectPoints.pList[0]; DqnV2 *const pList = &rectPoints.pList[0];
@ -430,7 +433,7 @@ void DTRRender_Rectangle(PlatformRenderBuffer *const renderBuffer, DqnV2 min, Dq
} }
} }
if (pIsInside) SetPixel(renderBuffer, bufferX, bufferY, color); if (pIsInside) SetPixel(renderBuffer, bufferX, bufferY, color, ColorSpace_Linear);
} }
} }
} }
@ -442,7 +445,7 @@ void DTRRender_Rectangle(PlatformRenderBuffer *const renderBuffer, DqnV2 min, Dq
for (i32 x = 0; x < clippedSize.w; x++) for (i32 x = 0; x < clippedSize.w; x++)
{ {
i32 bufferX = (i32)clippedRect.min.x + x; i32 bufferX = (i32)clippedRect.min.x + x;
SetPixel(renderBuffer, bufferX, bufferY, color); SetPixel(renderBuffer, bufferX, bufferY, color, ColorSpace_Linear);
} }
} }
} }
@ -450,7 +453,7 @@ void DTRRender_Rectangle(PlatformRenderBuffer *const renderBuffer, DqnV2 min, Dq
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Debug // Debug
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
if (DTR_DEBUG) if (DTR_DEBUG_RENDER)
{ {
// Draw Bounding box // Draw Bounding box
{ {
@ -491,7 +494,8 @@ void DTRRender_Triangle(PlatformRenderBuffer *const renderBuffer, DqnV2 p1, DqnV
p2 = pList[1]; p2 = pList[1];
p3 = pList[2]; p3 = pList[2];
color = DTRRender_PreMultiplyAlpha1(color); color = DTRRender_SRGB1ToLinearSpaceV4(color);
color = PreMultiplyAlpha1(color);
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Calculate Bounding Box // Calculate Bounding Box
@ -647,7 +651,7 @@ void DTRRender_Triangle(PlatformRenderBuffer *const renderBuffer, DqnV2 p1, DqnV
{ {
if (signedArea1Row >= 0 && signedArea2Row >= 0 && signedArea3Row >= 0) if (signedArea1Row >= 0 && signedArea2Row >= 0 && signedArea3Row >= 0)
{ {
SetPixel(renderBuffer, scanP.x, scanP.y, color); SetPixel(renderBuffer, scanP.x, scanP.y, color, ColorSpace_Linear);
} }
signedArea1Row += signedArea1DeltaX; signedArea1Row += signedArea1DeltaX;
@ -663,7 +667,7 @@ void DTRRender_Triangle(PlatformRenderBuffer *const renderBuffer, DqnV2 p1, DqnV
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Debug // Debug
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
if (DTR_DEBUG) if (DTR_DEBUG_RENDER)
{ {
// Draw Bounding box // Draw Bounding box
{ {
@ -698,9 +702,13 @@ void DTRRender_Triangle(PlatformRenderBuffer *const renderBuffer, DqnV2 p1, DqnV
void DTRRender_Bitmap(PlatformRenderBuffer *const renderBuffer, void DTRRender_Bitmap(PlatformRenderBuffer *const renderBuffer,
DTRBitmap *const bitmap, DqnV2 pos, DTRBitmap *const bitmap, DqnV2 pos,
const DTRRenderTransform transform) const DTRRenderTransform transform, DqnV4 color)
{ {
if (!bitmap || !bitmap->memory || !renderBuffer) return; if (!bitmap || !bitmap->memory || !renderBuffer) return;
DQN_ASSERT(color.a >= 0 && color.a <= 1.0f);
DQN_ASSERT(color.r >= 0 && color.r <= 1.0f);
DQN_ASSERT(color.g >= 0 && color.g <= 1.0f);
DQN_ASSERT(color.b >= 0 && color.b <= 1.0f);
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Transform vertexes // Transform vertexes
@ -717,6 +725,8 @@ void DTRRender_Bitmap(PlatformRenderBuffer *const renderBuffer,
min = bounds.min; min = bounds.min;
max = bounds.max; max = bounds.max;
color = DTRRender_SRGB1ToLinearSpaceV4(color);
color = PreMultiplyAlpha1(color);
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Clip drawing space // Clip drawing space
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
@ -828,10 +838,10 @@ void DTRRender_Bitmap(PlatformRenderBuffer *const renderBuffer,
color3 *= DTRRENDER_INV_255; color3 *= DTRRENDER_INV_255;
color4 *= DTRRENDER_INV_255; color4 *= DTRRENDER_INV_255;
color1 = SRGB1ToLinearSpaceV4(color1); color1 = DTRRender_SRGB1ToLinearSpaceV4(color1);
color2 = SRGB1ToLinearSpaceV4(color2); color2 = DTRRender_SRGB1ToLinearSpaceV4(color2);
color3 = SRGB1ToLinearSpaceV4(color3); color3 = DTRRender_SRGB1ToLinearSpaceV4(color3);
color4 = SRGB1ToLinearSpaceV4(color4); color4 = DTRRender_SRGB1ToLinearSpaceV4(color4);
DqnV4 color12; DqnV4 color12;
color12.a = DqnMath_Lerp(color1.a, texelFractionalX, color2.a); color12.a = DqnMath_Lerp(color1.a, texelFractionalX, color2.a);
@ -845,22 +855,40 @@ void DTRRender_Bitmap(PlatformRenderBuffer *const renderBuffer,
color34.g = DqnMath_Lerp(color3.g, texelFractionalX, color4.g); color34.g = DqnMath_Lerp(color3.g, texelFractionalX, color4.g);
color34.r = DqnMath_Lerp(color3.r, texelFractionalX, color4.r); color34.r = DqnMath_Lerp(color3.r, texelFractionalX, color4.r);
DqnV4 color; DqnV4 blend;
color.a = DqnMath_Lerp(color12.a, texelFractionalY, color34.a); blend.a = DqnMath_Lerp(color12.a, texelFractionalY, color34.a);
color.b = DqnMath_Lerp(color12.b, texelFractionalY, color34.b); blend.b = DqnMath_Lerp(color12.b, texelFractionalY, color34.b);
color.g = DqnMath_Lerp(color12.g, texelFractionalY, color34.g); blend.g = DqnMath_Lerp(color12.g, texelFractionalY, color34.g);
color.r = DqnMath_Lerp(color12.r, texelFractionalY, color34.r); blend.r = DqnMath_Lerp(color12.r, texelFractionalY, color34.r);
DQN_ASSERT(color.a >= 0 && color.a <= 1.0f);
DQN_ASSERT(color.r >= 0 && color.r <= 1.0f);
DQN_ASSERT(color.g >= 0 && color.g <= 1.0f);
DQN_ASSERT(color.b >= 0 && color.b <= 1.0f);
SetPixel(renderBuffer, bufferX, bufferY, color, ColourSpace_Linear); DQN_ASSERT(blend.a >= 0 && blend.a <= 1.0f);
DQN_ASSERT(blend.r >= 0 && blend.r <= 1.0f);
DQN_ASSERT(blend.g >= 0 && blend.g <= 1.0f);
DQN_ASSERT(blend.b >= 0 && blend.b <= 1.0f);
// TODO(doyle): Color modulation does not work!!! By supplying
// colors [0->1] it'll reduce some of the coverage of a channel
// and once alpha blending is applied that reduced coverage will
// blend with the background and cause the bitmap to go
// transparent when it shouldn't.
blend.a *= color.a;
blend.r *= color.r;
blend.g *= color.g;
blend.b *= color.b;
#if 0
blend.a = DqnMath_Clampf(blend.a, 0.0f, 1.0f);
blend.r = DqnMath_Clampf(blend.r, 0.0f, 1.0f);
blend.g = DqnMath_Clampf(blend.g, 0.0f, 1.0f);
blend.b = DqnMath_Clampf(blend.b, 0.0f, 1.0f);
#endif
SetPixel(renderBuffer, bufferX, bufferY, blend, ColorSpace_Linear);
} }
} }
} }
if (DTR_DEBUG) if (DTR_DEBUG_RENDER)
{ {
// Draw Bounding box // Draw Bounding box
{ {
@ -901,20 +929,23 @@ void DTRRender_Bitmap(PlatformRenderBuffer *const renderBuffer,
} }
void DTRRender_Clear(PlatformRenderBuffer *const renderBuffer, void DTRRender_Clear(PlatformRenderBuffer *const renderBuffer,
const DqnV3 color) DqnV3 color)
{ {
if (!renderBuffer) return; if (!renderBuffer) return;
DQN_ASSERT(color.r >= 0.0f && color.r <= 1.0f); DQN_ASSERT(color.r >= 0.0f && color.r <= 1.0f);
DQN_ASSERT(color.g >= 0.0f && color.g <= 1.0f); DQN_ASSERT(color.g >= 0.0f && color.g <= 1.0f);
DQN_ASSERT(color.b >= 0.0f && color.b <= 1.0f); DQN_ASSERT(color.b >= 0.0f && color.b <= 1.0f);
color *= 255.0f;
u32 *const bitmapPtr = (u32 *)renderBuffer->memory; u32 *const bitmapPtr = (u32 *)renderBuffer->memory;
for (i32 y = 0; y < renderBuffer->height; y++) for (i32 y = 0; y < renderBuffer->height; y++)
{ {
for (i32 x = 0; x < renderBuffer->width; x++) for (i32 x = 0; x < renderBuffer->width; x++)
{ {
u32 pixel = ((i32)color.r << 16) | ((i32)color.g << 8) | u32 pixel = ((i32)0 << 24) |
((i32)color.r << 16) |
((i32)color.g << 8) |
((i32)color.b << 0); ((i32)color.b << 0);
bitmapPtr[x + (y * renderBuffer->width)] = pixel; bitmapPtr[x + (y * renderBuffer->width)] = pixel;
} }

View File

@ -3,9 +3,14 @@
#include "dqn.h" #include "dqn.h"
#define DTRRENDER_INV_255 0.00392156862f;
typedef struct PlatformRenderBuffer PlatformRenderBuffer; typedef struct PlatformRenderBuffer PlatformRenderBuffer;
typedef struct DTRBitmap DTRBitmap; typedef struct DTRBitmap DTRBitmap;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Utility
////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct DTRRenderTransform typedef struct DTRRenderTransform
{ {
f32 rotation = 0; // Rotation in degrees f32 rotation = 0; // Rotation in degrees
@ -27,23 +32,32 @@ inline DTRRenderTransform DTRRender_DefaultTriangleTransform()
return result; return result;
} }
// NOTE(doyle): 1 & 255 suffix represent the range the color is being sent in. // NOTE(doyle): 1 suffix represents the range the color is being sent in which
// 255 means we expect that all the colors are from [0-255] and 1 means all the // means all the colors should be in the range of [0-1]
// colors should be in the range of [0-1] inline f32 DTRRender_SRGB1ToLinearSpacef (f32 val);
inline DqnV4 DTRRender_PreMultiplyAlpha1 (const DqnV4 color); inline DqnV4 DTRRender_SRGB1ToLinearSpaceV4(DqnV4 color);
inline DqnV4 DTRRender_PreMultiplyAlpha255(const DqnV4 color); inline f32 DTRRender_LinearToSRGB1Spacef (f32 val);
inline DqnV4 DTRRender_LinearToSRGB1SpaceV4(DqnV4 color);
// NOTE: All specified colors should be in the range of [0->1], rgba, respectively. // Takes SRGB in [0->1], converts to linear space, premultiplies alpha and returns
// color back in SRGB space.
inline DqnV4 DTRRender_PreMultiplyAlphaSRGB1WithLinearConversion(DqnV4 color);
////////////////////////////////////////////////////////////////////////////////////////////////////
// Rendering
////////////////////////////////////////////////////////////////////////////////////////////////////
// NOTE: All colors should be in the range of [0->1] where DqnV4 is a struct with 4 floats, rgba
// Leaving len = -1 for text will make the system use strlen to determine len. // Leaving len = -1 for text will make the system use strlen to determine len.
void DTRRender_Text (PlatformRenderBuffer *const renderBuffer, const DTRFont font, DqnV2 pos, void DTRRender_Text (PlatformRenderBuffer *const renderBuffer, const DTRFont font, DqnV2 pos, const char *const text, DqnV4 color = DqnV4_1f(1), i32 len = -1);
const char *const text, DqnV4 color = DqnV4_1f(1), i32 len = -1);
void DTRRender_Line (PlatformRenderBuffer *const renderBuffer, DqnV2i a, DqnV2i b, DqnV4 color); void DTRRender_Line (PlatformRenderBuffer *const renderBuffer, DqnV2i a, DqnV2i b, DqnV4 color);
void DTRRender_Rectangle(PlatformRenderBuffer *const renderBuffer, DqnV2 min, DqnV2 max,
DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTransform()); void DTRRender_Rectangle(PlatformRenderBuffer *const renderBuffer, DqnV2 min, DqnV2 max, DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTransform());
void DTRRender_Triangle (PlatformRenderBuffer *const renderBuffer, DqnV2 p1, DqnV2 p2, DqnV2 p3,
DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTriangleTransform()); void DTRRender_Triangle (PlatformRenderBuffer *const renderBuffer, DqnV2 p1, DqnV2 p2, DqnV2 p3, DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTriangleTransform());
void DTRRender_Bitmap (PlatformRenderBuffer *const renderBuffer, DTRBitmap *const bitmap, DqnV2 pos,
const DTRRenderTransform transform = DTRRender_DefaultTransform()); void DTRRender_Bitmap (PlatformRenderBuffer *const renderBuffer, DTRBitmap *const bitmap, DqnV2 pos, const DTRRenderTransform transform = DTRRender_DefaultTransform(), DqnV4 color = DqnV4_4f(1, 1, 1, 1));
void DTRRender_Clear (PlatformRenderBuffer *const renderBuffer, const DqnV3 color);
void DTRRender_Clear (PlatformRenderBuffer *const renderBuffer, DqnV3 color);
#endif #endif