summaryrefslogtreecommitdiff
path: root/src/effects_software.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/effects_software.cpp')
-rw-r--r--src/effects_software.cpp209
1 files changed, 209 insertions, 0 deletions
diff --git a/src/effects_software.cpp b/src/effects_software.cpp
new file mode 100644
index 0000000..2fde6b9
--- /dev/null
+++ b/src/effects_software.cpp
@@ -0,0 +1,209 @@
+
+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++;
+ }
+}