#if SPECIAL #include "main.h" #endif static void Effect_Software_DrawColor(int Width, int Height, int BytesPerPixel, void *EffectBitmapAddress, v4 Color, blend_mode BlendMode) { render_byte_info Bits = Bitmap_ByteInfo(BytesPerPixel); rectangle RenderRegion = {0, 0, Width, Height}; transform_info T; T.BlendMode = BlendMode; for (int32 Y = RenderRegion.Min.y; Y < RenderRegion.Max.y; Y++) { for (int32 X = RenderRegion.Min.x; X < RenderRegion.Max.x; X++) { uint32 Offset = Y*Width*BytesPerPixel + X*BytesPerPixel; uint8 *LayerPixel = (uint8 *)EffectBitmapAddress + Offset; uint32 *R_DestAddress = (uint32 *)(LayerPixel + Bits.ByteOffset * 0); uint32 *G_DestAddress = (uint32 *)(LayerPixel + Bits.ByteOffset * 1); uint32 *B_DestAddress = (uint32 *)(LayerPixel + Bits.ByteOffset * 2); uint32 *A_DestAddress = (uint32 *)(LayerPixel + Bits.ByteOffset * 3); real32 R_Dest = (real32)(*R_DestAddress & Bits.MaskPixel) * Bits.Normalized; real32 G_Dest = (real32)(*G_DestAddress & Bits.MaskPixel) * Bits.Normalized; real32 B_Dest = (real32)(*B_DestAddress & Bits.MaskPixel) * Bits.Normalized; real32 A_Dest = (real32)(*A_DestAddress & Bits.MaskPixel) * Bits.Normalized; real32 R_Col = Color.r; real32 G_Col = Color.g; real32 B_Col = Color.b; real32 A_Col = Color.a; real32 LayerAlpha = A_Col * 1; real32 R_Blend = R_Col; real32 G_Blend = G_Col; real32 B_Blend = B_Col; real32 A_Blend = A_Col; Fallback_Blend(); uint32 R_Out = (uint32)(Normalize(R_Blend) * Bits.Bits); uint32 G_Out = (uint32)(Normalize(G_Blend) * Bits.Bits); uint32 B_Out = (uint32)(Normalize(B_Blend) * Bits.Bits); uint32 A_Out = (uint32)(Normalize(A_Blend) * Bits.Bits); *R_DestAddress = (*R_DestAddress & ~Bits.MaskPixel) | R_Out; *G_DestAddress = (*G_DestAddress & ~Bits.MaskPixel) | G_Out; *B_DestAddress = (*B_DestAddress & ~Bits.MaskPixel) | B_Out; *A_DestAddress = (*A_DestAddress & ~Bits.MaskPixel) | A_Out; } } } static void CurvesSolver(real32 *LUT, v2 Point_P1, v2 Point_P2, v2 m1, v2 m2, int i) { real32 Precision = ((real32)1 / 256) * 0.1; real32 Point_Span = Point_P2.x - Point_P1.x; v2 Cache[256]; if (i == 0) { // Don't know how to fix this, so I'm just gonna linear interpolate // until I try quadratic solving. real32 Count_Start = (Point_P1.x * 256); real32 Count_End = (Point_P2.x * 256); real32 Count_Total = Count_End - Count_Start; real32 Width = Point_P2.x - Point_P1.x; real32 Height = Point_P2.y - Point_P1.y; real32 Count = Count_Start; real32 t = 1; while (Count < Count_End) { LUT[(uint32)Count] = Normalize(Point_P1.y + (Height*((Count-Count_Start)/(Count_End - (Count_End - 256)))/Width)); Count++; } } else { real32 Count_Start = (Point_P1.x * 256); real32 Count_End = (Point_P2.x * 256); real32 Count_Total = Count_End - Count_Start; real32 Count = Count_Start; real32 t = 0; int Timeout = 0; // This solver actually works kinda decently when the graph isn't that // complex, taking less than 10 iterations per LUT value. It fails // towards the edges and with harsh curves, going into the hundreds. The // 1000 condition should only be hit when the solver is locked, which can // happen when two points are close together on X. while (Count < Count_End) { real32 c = 2*t*t*t - 3*t*t; real32 c0 = c + 1; real32 c1 = t*t*t - 2*t*t + t; real32 c2 = -c; real32 c3 = t*t*t - t*t; v2 Point = (c0 * Point_P1) + (c1 * m1) + (c2 * Point_P2) + (c3 * m2); real32 TargetX = Count / 256; if (Timeout == 1000) { Point.x = TargetX; printf("Solve between %.1f and %.1f reached 1000 iterations at %.f!\n", Count_Start, Count_End, Count); } // Only record the value if it's within a certain precision. if (Point.x <= TargetX - Precision || Point.x >= TargetX + Precision) { t = t * TargetX / Point.x; Timeout++; } else { if (Point.y > 1.0f) { LUT[(uint32)Count] = 1.0f; } else if (Point.y < 0.0f) LUT[(uint32)Count] = 0.0f; else { LUT[(uint32)Count] = Point.y; } t += (Point_Span / Count_Total); Count++; Timeout = 0; } } } } static void Effect_Software_Curves(int Width, int Height, int BytesPerPixel, void *EffectBitmapAddress, v2 *PointData, real32 PointCount, v4 PointCount_Col) { real32 LUT[5][256] = {}; for (int a = 0; a < 5; a++) { int Num = (a == 0) ? (int)PointCount : (int)PointCount_Col.E[a-1]; v2 *CurvePoint = PointData + (MAX_PROPERTIES_PER_EFFECT / 5 * a); for (int i = 0; i < Num; i++) { v2 Point_P1 = CurvePoint[i]; v2 Point_P2 = CurvePoint[i + 1]; v2 Point_P0 = (i != 0) ? CurvePoint[i - 1] : V2(0, 0); v2 Point_P3 = (i != (Num - 2)) ? CurvePoint[i + 2] : V2(1, 1); v2 m1 = (Point_P2 - Point_P0) / (2 * Tau); v2 m2 = (Point_P3 - Point_P1) / (2 * Tau); CurvesSolver(LUT[a], Point_P1, Point_P2, m1, m2, i); } if (CurvePoint[0].x > 0.0f) { real32 Count_Start = 0; real32 Count_End = (CurvePoint[0].x * 255); real32 Count = Count_Start; while (Count < Count_End) { LUT[a][(uint32)Count] = LUT[a][(uint32)Count_End]; Count++; } } if (CurvePoint[Num-1].x < 1.0f) { real32 Count_Start = (CurvePoint[Num-1].x * 255) - 0.5; real32 Count_End = 255; real32 Count = Count_Start; while (Count < Count_End) { LUT[a][(uint32)Count] = LUT[a][(uint32)Count_Start]; Count++; } } for (int i = 0; i < Num; i++) { if (CurvePoint[i].y == 1.0f) LUT[a][255] = 1.0f; } } uint64 Size = Width*Height; int i = 0; Assert(BytesPerPixel == 4); while (i < Size) { uint32 *Pixel = (uint32 *)EffectBitmapAddress + i; uint8 A = (*Pixel >> 24); uint8 B = (*Pixel >> 16); uint8 G = (*Pixel >> 8); uint8 R = (*Pixel >> 0); #if 1 real32 R_Lookup = LUT[1][R]; real32 G_Lookup = LUT[2][G]; real32 B_Lookup = LUT[3][B]; real32 A_Lookup = LUT[4][A]; real32 R_Lookup_All = LUT[0][(uint32)(R_Lookup*255)]; real32 G_Lookup_All = LUT[0][(uint32)(G_Lookup*255)]; real32 B_Lookup_All = LUT[0][(uint32)(B_Lookup*255)]; #else real32 R_Lookup_All = LUT[0][(uint32)(t.r)]; real32 G_Lookup_All = LUT[0][(uint32)(t.g)]; real32 B_Lookup_All = LUT[0][(uint32)(t.b)]; #endif uint32 Result = (((uint32)((A_Lookup * 255.0f) + 0.5) << 24) | ((uint32)((B_Lookup_All * 255.0f) + 0.5) << 16) | ((uint32)((G_Lookup_All * 255.0f) + 0.5) << 8) | ((uint32)((R_Lookup_All * 255.0f) + 0.5) << 0)); *Pixel = Result; i++; } }