#include "effects.h" #include "effects_software.cpp" static void Effect_DrawColor(source *Source, layer_bitmap_info *BitmapInfo, memory *Memory, property_channel Property[]) { Effect_DrawColor_Software(Source, BitmapInfo, Memory, Property); } static void Effect_Levels(source *Source, layer_bitmap_info *BitmapInfo, memory *Memory, property_channel Property[]) { #if 0 real32 All_Start = Property[0].CurrentValue.f; real32 All_Mid = Property[1].CurrentValue.f; real32 All_End = Property[2].CurrentValue.f; v4 Start = Property[3].CurrentValue.col; v4 Mid = Property[4].CurrentValue.col; v4 End = Property[5].CurrentValue.col; if (!BitmapInfo->HistogramVals) { uint64 Size = Bitmap_CalcUnpackedBytes(Source->Info.Width, Source->Info.Height, Source->Info.BytesPerPixel); BitmapInfo->HistogramVals = AllocateMemory(Memory, (sizeof(uint32) * 5 * 256), P_MiscCache); Bitmap_CalcHistogram(BitmapInfo->HistogramVals, BitmapInfo->BitmapBuffer, Source->Info.BytesPerPixel, Size); } Assert(&BitmapInfo->Test); gl_effect_layer Test = BitmapInfo->Test; // glBindRenderbuffer(GL_RENDERBUFFER, Test.RBO); /* glUseProgram(TGL.ShaderProgram); int vertexColorLocation = glGetUniformLocation(TGL.ShaderProgram, "Start"); glUniform1f(vertexColorLocation, All_Start); vertexColorLocation = glGetUniformLocation(TGL.ShaderProgram, "Mid"); glUniform1f(vertexColorLocation, All_Mid); vertexColorLocation = glGetUniformLocation(TGL.ShaderProgram, "End"); glUniform1f(vertexColorLocation, All_End); vertexColorLocation = glGetUniformLocation(TGL.ShaderProgram, "StartCol"); glUniform4f(vertexColorLocation, Start.r, Start.g, Start.b, Start.a); vertexColorLocation = glGetUniformLocation(TGL.ShaderProgram, "MidCol"); glUniform4f(vertexColorLocation, Mid.r, Mid.g, Mid.b, Mid.a); vertexColorLocation = glGetUniformLocation(TGL.ShaderProgram, "EndCol"); glUniform4f(vertexColorLocation, End.r, End.g, End.b, End.a); */ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glReadBuffer(GL_COLOR_ATTACHMENT0); uint16 Width = Source->Info.Width; uint16 Height = Source->Info.Height; uint8 *Data = (uint8 *)BitmapInfo->BitmapBuffer; glReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, &Data[0]); glBindFramebuffer(GL_FRAMEBUFFER, 0); #endif } static void Effect_GaussianBlur(source *Source, layer_bitmap_info *BitmapInfo, memory *Memory, property_channel Property[]) { #if 0 real32 Radius = Property[0].CurrentValue.f; gl_effect_layer *Test = &BitmapInfo->Test; // glBindRenderbuffer(GL_RENDERBUFFER, Test->RBO); /* glUseProgram(TGL.ShaderProgram); int vertexColorLocation = glGetUniformLocation(TGL.ShaderProgram, "Radius"); glUniform1f(vertexColorLocation, Radius + 1.60f); vertexColorLocation = glGetUniformLocation(TGL.ShaderProgram, "Direction"); glUniform2f(vertexColorLocation, 1.0f, 0.0f); */ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glReadBuffer(GL_COLOR_ATTACHMENT0); uint16 Width = Source->Info.Width; uint16 Height = Source->Info.Height; uint8 *Data = (uint8 *)BitmapInfo->BitmapBuffer; glReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, &Data[0]); GL_UpdateTexture(&BitmapInfo->Test, Data, Width, Height, 0); // glBindRenderbuffer(GL_RENDERBUFFER, Test->RBO); /* glUseProgram(TGL.ShaderProgram); vertexColorLocation = glGetUniformLocation(TGL.ShaderProgram, "Direction"); glUniform2f(vertexColorLocation, 0.0f, 1.0f); */ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, &Data[0]); glBindFramebuffer(GL_FRAMEBUFFER, 0); #endif } #if 0 { "Solid Color", &DrawColor, standard, { {"Color", {.col = V4(0.5f, 1.0f, 0.4f, 0.5f)}, color}, } }, { "Test Grid", &DrawGrid, standard, { {"Color 1", {.col = V4(0.5f, 1.0f, 0.4f, 1.0f)}, color}, {"Color 2", {.col = V4(0.0f, 0.0f, 0.0f, 1.0f)}, color} } }, { "Canny edges", &Canny, standard, { {"Blur Radius", {1.0f}, real}, {"Threshold", {5.0f}, real}, } }, { "Levels", &Levels, levels, { {"Start point", {0.0f}, real}, {"Mid point", {1.0f}, real}, {"End point", {1.0f}, real}, {"Start Col", {.col = V4(0.0f)}, color}, {"Mid Col", {.col = V4(1.0f)}, color}, {"End Col", {.col = V4(1.0f)}, color}, } }, { "Kernel", &SpacialFilter, standard, { {"V1", {-1.0f}, real}, {"V2", {0.0f}, real}, {"V3", {1.0f}, real}, {"V4", {-2.0f}, real}, {"V5", {0.0f}, real}, {"V6", {2.0f}, real}, {"V7", {-1.0f}, real}, {"V8", {0.0f}, real}, {"V9", {1.0f}, real}, } }, { "Invert", &Invert, 0, standard, { } } #endif static void AddEffect(project_layer *Layer, memory *Memory, uint16 EffectListIndex) { if (Layer == NULL) return; Layer->Effect[Layer->NumberOfEffects] = (effect *)AllocateMemory(Memory, sizeof(effect), F_Effects); effect *Effect = Layer->Effect[Layer->NumberOfEffects]; effect_header EffectHeader = EffectList[EffectListIndex]; Effect->Name = EffectHeader.Name; Effect->func = EffectHeader.func; Effect->NumberOfProperties = EffectHeader.NumberOfProperties; Effect->DisplayType = EffectHeader.DisplayType; Effect->IsActive = true; for (int16 i = 0; i < Effect->NumberOfProperties; i++) { Effect->Property[i].Name = EffectHeader.PropertyHeader[i].Name; Effect->Property[i].CurrentValue = EffectHeader.PropertyHeader[i].Value; Effect->Property[i].MinVal = EffectHeader.PropertyHeader[i].MinVal; Effect->Property[i].MaxVal = EffectHeader.PropertyHeader[i].MaxVal; Effect->Property[i].VarType = EffectHeader.PropertyHeader[i].VarType; } Layer->NumberOfEffects++; } #if 0 static void DrawColor(pixel_buffer *Buffer, memory *Memory, property_channel Property[]) { v4 FloatColor = Property[0].CurrentValue.col; uint32 Color = ColToUint32(FloatColor); uint8 *Row = ((uint8 *)Buffer->EffectBuffer); v2 Origin = {(real32)Buffer->Width / 2.0f, (real32)Buffer->Height / 2.0f}; real32 MaxLength = sqrt(LengthSq(Origin)); for(int Y = 0; Y < Buffer->Height; ++Y) { uint32 *Pixel = (uint32 *)Row; for(int X = 0; X < Buffer->Width; ++X) { RenderAlpha(Pixel, Color); Pixel++; } Row += Buffer->Pitch; } } static void Invert(pixel_buffer *Buffer, memory *Memory, property_channel Property[]) { uint8 *Row = ((uint8 *)Buffer->EffectBuffer); for(int Y = 0; Y < Buffer->Height; ++Y) { uint32 *Pixel = (uint32 *)Row; for(int X = 0; X < Buffer->Width; ++X) { v4 col = Uint32ToCol8(*Pixel); col.r = 255 - col.r; col.g = 255 - col.g; col.b = 255 - col.b; *Pixel++ = Col8ToUint32(col); } Row += Buffer->Pitch; } } static void DrawGradient(pixel_buffer *Buffer, memory *Memory, property_channel Property[]) { v4 StartColor = Property[0].CurrentValue.col; v4 EndColor = Property[1].CurrentValue.col; real32 Alpha = Property[2].CurrentValue.f; uint8 *Row = ((uint8 *)Buffer->EffectBuffer + Buffer->BytesPerPixel + Buffer->Pitch); for(int Y = 0; Y < Buffer->Height; ++Y) { uint32 *Pixel = (uint32 *)Row; for(int X = 0; X < Buffer->Width; ++X) { real32 PlusAlpha = ((real32)X / Buffer->Width); v4 PL = V4(V3(PlusAlpha), 1.0f); v4 C1 = ClipV4((StartColor - PL)); v4 C2 = ClipV4( (EndColor - (1 - PL) ) ); v4 FloatColor = ClipV4( C1 + C2 ); uint32 Color = ColToUint32(FloatColor); *(uint32 *)Pixel++ = Color; } Row += Buffer->Pitch; } } static void DrawGrid(pixel_buffer *Buffer, memory *Memory, property_channel Property[]) { v4 StartColor = Property[0].CurrentValue.col; v4 EndColor = Property[1].CurrentValue.col; uint32 Color1 = ColToUint32(StartColor); uint32 Color2 = ColToUint32(EndColor); uint8 *Row = ((uint8 *)Buffer->EffectBuffer); for(int Y = 0; Y < Buffer->Height; ++Y) { uint32 *Pixel = (uint32 *)Row; for(int X = 0; X < Buffer->Width; ++X) { if (X & 4 || Y & 4) { *(uint32 *)Pixel++ = Color1; } else { *(uint32 *)Pixel++ = Color2; } } Row += Buffer->Pitch; } } static real32 KernLoop(pixel_buffer *Buffer, int16 Xp, int16 Yp, real32 Value[8]) { real32 P[9]; uint8 *Row = ((uint8 *)Buffer->EffectBuffer + (Buffer->Pitch*Yp)); Row -= Buffer->Pitch; int16 n = 0; for(int Y = 0; Y < 3; ++Y) { uint32 *Pixel = (uint32 *)Row + Xp; for(int X = 0; X < 3; ++X) { real32 BW = Uint32ToNormalizedBW(*Pixel); P[n] = BW * Value[n]; Pixel++; n++; } Row += Buffer->Pitch; } real32 Sum = P[0] + P[1] + P[2] + P[3] + P[4] + P[5] + P[6] + P[7] + P[8] ; return Sum; } static void SpacialFilter(pixel_buffer *Buffer, memory *Memory, property_channel Property[]) { real32 P[9]; P[0] = Property[0].CurrentValue.f; P[1] = Property[1].CurrentValue.f; P[2] = Property[2].CurrentValue.f; P[3] = Property[3].CurrentValue.f; P[4] = Property[4].CurrentValue.f; P[5] = Property[5].CurrentValue.f; P[6] = Property[6].CurrentValue.f; P[7] = Property[7].CurrentValue.f; P[8] = Property[8].CurrentValue.f; if (!Buffer->Scratch) { Buffer->Scratch = (uint64 *)Memory->Address + Memory->CurrentPosition; Memory->CurrentPosition += Buffer->Width * Buffer->Height * Buffer->BytesPerPixel; } for(int Y = 1; Y < Buffer->Height - 1; ++Y) { for(int X = 1; X < Buffer->Width - 1; ++X) { real32 Sum = KernLoop(Buffer, X, Y, P); uint8 *FloatRow = ((uint8 *)Buffer->Scratch + Buffer->BytesPerPixel + Buffer->Pitch + (Buffer->Pitch*Y)); real32 *FloatValue = (real32 *)FloatRow + X; *(real32 *)FloatValue++ = Sum; } } for(int Y = 1; Y < Buffer->Height - 1; ++Y) { for(int X = 1; X < Buffer->Width - 1; ++X) { uint8 *Row = ((uint8 *)Buffer->EffectBuffer + Buffer->BytesPerPixel + Buffer->Pitch + (Buffer->Pitch*Y)); uint32 *Pixel = (uint32 *)Row + X; uint8 *RowR = ((uint8 *)Buffer->Scratch + Buffer->BytesPerPixel + Buffer->Pitch + (Buffer->Pitch*Y)); real32 *PixelR = (real32 *)RowR + X; *(uint32 *)Pixel= ColToUint32(abs(*PixelR / 4.0f)); PixelR++; } } } static void Gaussian(pixel_buffer *Buffer, void *FloatStorage, real32 Radius) { if (Radius < 1.0f) Radius = 1.0f; real32 Omega = Radius / 2; real32 Total = pow((Radius + Radius + 1), 2) / 2; int32 ColorPitch = Buffer->Pitch * 2; real32 P2 = 2*(Omega*Omega); for(int16 Y = Radius; Y < Buffer->Height - Radius; ++Y) { uint8 *Row = ((uint8 *)Buffer->EffectBuffer + Buffer->BytesPerPixel + Buffer->Pitch + Buffer->Pitch*(Y)); for(int16 X = Radius; X < Buffer->Width - Radius; ++X) { uint32 *Pixel = (uint32 *)Row + X; v4 FloatCol = Uint32ToNormalizedCol(*Pixel); for(int16 Y2 = -Radius; Y2 <= Radius; ++Y2) { uint16 *TempRow = ((uint16 *)FloatStorage + Buffer->BytesPerPixel + ColorPitch + (ColorPitch*(Y + Y2))); for(int16 X2 = -Radius; X2 <= Radius; ++X2) { v4 *TempValue = (v4 *)TempRow + (X + X2); real32 P1 = ((X2 * X2) + (Y2 * Y2)); real32 G = exp(-(P1/P2)); *TempValue = *TempValue + (FloatCol*V4(G) / V4(Total)); } } } } for(int Y = Radius; Y < Buffer->Height - Radius; ++Y) { for(int X = Radius; X < Buffer->Width - Radius; ++X) { uint8 *Row = ((uint8 *)Buffer->EffectBuffer + Buffer->BytesPerPixel + Buffer->Pitch + (Buffer->Pitch*Y)); uint32 *Pixel = (uint32 *)Row + X; uint16 *TempRow = ((uint16 *)FloatStorage + Buffer->BytesPerPixel + ColorPitch + (ColorPitch*Y)); v4 *TempValue = (v4 *)TempRow + X; TempValue->a = 1.0f; uint32 Color = ColToUint32(Clamp(0.0, *TempValue, 1.0)); *Pixel = Color; *TempValue = {0}; } } } static void Canny(pixel_buffer *Buffer, memory *Memory, property_channel Property[]) { real32 SH[9] = { -1, 0, 1, -2, 0, 2, -1, 0, 1 }; real32 SV[9] = { -1, -2, -1, 0, 0, 0, 1, 2, 1 }; real32 Radius = Property[0].CurrentValue.f; real32 Threshold = Property[1].CurrentValue.f / 100; real32 UpperThreshold = Threshold * 1.5; int32 ColorPitch = Buffer->Pitch * 2; if (!Buffer->Scratch) { Buffer->Scratch = (uint64 *)Memory->Address + Memory->CurrentPosition; // NOTE(fox): this buffer is four times as large to store four real32s Memory->CurrentPosition += Buffer->Width * Buffer->Height * Buffer->BytesPerPixel * 4; } Gaussian(Buffer, Buffer->Scratch, Radius); for(int Y = 1; Y < Buffer->Height - 1; ++Y) { uint16 *TempRow = ((uint16 *)Buffer->Scratch + Buffer->BytesPerPixel + ColorPitch + (ColorPitch*(Y))); for(int X = 1; X < Buffer->Width - 1; ++X) { real32 HSum = KernLoop(Buffer, X, Y, SH); real32 VSum = KernLoop(Buffer, X, Y, SV); real32 Mag = sqrt((HSum*HSum) + (VSum*VSum)); real32 Angle = atan(VSum/HSum) * (180 / PI); v4 *TempValue = (v4 *)TempRow + (X); TempValue->r = Mag; TempValue->g = Angle; } } for(int Y = 1; Y < Buffer->Height - 1; ++Y) { uint16 *TempRow = ((uint16 *)Buffer->Scratch + Buffer->BytesPerPixel + ColorPitch + (ColorPitch*(Y))); uint16 *Row = ((uint16 *)Buffer->EffectBuffer + Buffer->BytesPerPixel + Buffer->Pitch + (Buffer->Pitch*Y)); for(int X = 1; X < Buffer->Width - 1; ++X) { uint32 *Pixel = (uint32 *)Row + X; v4 *TempValue = (v4 *)TempRow + X; if (TempValue->g < 45 && TempValue->g > -45) { v4 *Mag1 = (v4 *)TempRow + X + 1; v4 *Mag2 = (v4 *)TempRow + X - 1; if (TempValue->r > Mag1->r && TempValue->r > Mag2->r) TempValue->b = 1; } if (TempValue->g < 90 && TempValue->g > 45) { v4 *Mag1 = (v4 *)(TempRow + ColorPitch) + X + 1; v4 *Mag2 = (v4 *)(TempRow - ColorPitch) + X - 1; if (TempValue->r > Mag1->r && TempValue->r > Mag2->r) TempValue->b = 1; } if (TempValue->g < -45 && TempValue->g > -90) { v4 *Mag1 = (v4 *)(TempRow - ColorPitch) + X + 1; v4 *Mag2 = (v4 *)(TempRow + ColorPitch) + X - 1; if (TempValue->r > Mag1->r && TempValue->r > Mag2->r) TempValue->b = 1; } else { v4 *Mag1 = (v4 *)(TempRow + ColorPitch) + X; v4 *Mag2 = (v4 *)(TempRow - ColorPitch) + X; if (TempValue->r > Mag1->r && TempValue->r > Mag2->r) TempValue->b = 1; } } for(int Y = 1; Y < Buffer->Height - 1; ++Y) { uint16 *TempRow = ((uint16 *)Buffer->Scratch + Buffer->BytesPerPixel + ColorPitch + (ColorPitch*(Y))); uint8 *Row = ((uint8 *)Buffer->EffectBuffer + Buffer->BytesPerPixel + Buffer->Pitch + (Buffer->Pitch*Y)); for(int X = 1; X < Buffer->Width - 1; ++X) { uint32 *Pixel = (uint32 *)Row + X; v4 *TempValue = (v4 *)TempRow + (X); if (TempValue->b == 1) { if (TempValue->r > UpperThreshold) *Pixel = 0xFF0000FF; } else if (TempValue->r > Threshold) { bool32 pp = false; uint16 *TempRow2 = TempRow - ColorPitch; for(int Y2 = 0; Y2 < 3; ++Y2) { v4 *TempValue2 = (v4 *)TempRow + (X - 1); for(int X2 = 0; X2 < 3; ++X2) { if (TempValue2->r > UpperThreshold) pp = true; TempValue2++; } TempRow2 += ColorPitch; } if (pp) *Pixel = 0xFFFFFF00; } } } } } static void Levels(pixel_buffer *Buffer, memory *Memory, property_channel Property[]) { real32 Min = Property[0].CurrentValue.f; real32 Mid = Property[1].CurrentValue.f; real32 Max = Property[2].CurrentValue.f; v4 ColMin = Property[3].CurrentValue.col; v4 ColMid = Property[4].CurrentValue.col; v4 ColMax = Property[5].CurrentValue.col; if (!Property[0].Scratch) { Property[0].Scratch = (uint64 *)Memory->Address + Memory->CurrentPosition; Memory->CurrentPosition += Buffer->Width * Buffer->Height * Buffer->BytesPerPixel; uint16 *Levels = (uint16 *)Property[0].Scratch; uint8 *Row = ((uint8 *)Buffer->OriginalBuffer); for(int Y = 0; Y < Buffer->Height; ++Y) { uint32 *Pixel = (uint32 *)Row; for(int X = 0; X < Buffer->Width; ++X) { v4 Col = Uint32ToCol8(*Pixel); uint16 Global = (uint16)(RoundReal32ToUint32((Col.r + Col.g + Col.b)/3)); *(Levels + Global) += 1; *(Levels + 256 + (uint16)Col.r) += 1; *(Levels + 256*2 + (uint16)Col.g) += 1; *(Levels + 256*3 + (uint16)Col.b) += 1; *(Levels + 256*4 + (uint16)Col.a) += 1; Pixel++; } Row += Buffer->Pitch; } } uint8 *Row = ((uint8 *)Buffer->EffectBuffer); for(int Y = 0; Y < Buffer->Height; ++Y) { uint32 *Pixel = (uint32 *)Row; for(int X = 0; X < Buffer->Width; ++X) { // individual channels v4 ColorI = powv4(Uint32ToNormalizedCol(*Pixel), ColMid); v4 ValI = 1.0f/(ColMax-ColMin) * (ColorI - ColMin); // global channel v4 ColorG = powv4(ValI, Mid); v4 ValG = 1.0f/(Max-Min) * (ColorG - Min); *Pixel++ = ColToUint32(Clamp(0.0f, ValG, 1.0f)); } Row += Buffer->Pitch; } } static void GaussianBlur(pixel_buffer *Buffer, memory *Memory, property_channel Property[]) { real32 Radius = Property[0].CurrentValue.f; if (!Buffer->Scratch) { Buffer->Scratch = (uint64 *)Memory->Address + Memory->CurrentPosition; Memory->CurrentPosition += Buffer->Width * Buffer->Height * Buffer->BytesPerPixel; } Gaussian(Buffer, Buffer->Scratch, Radius); } #endif // AVX2 effect example code /* __m256 Fraction255 = _mm256_set1_ps(1/255.0f); __m256 Real255 = _mm256_set1_ps(255); __m256 One = _mm256_set1_ps(1); __m256i FF = _mm256_set1_epi32(0xFF); __m256 ZeroReal = _mm256_set1_ps(0); __m256i Int255 = _mm256_set1_epi8((uint8)255); for (int16 Y = 0; Y < Source->Info.Height; Y += 2) { for (int16 X = 0; X < Source->Info.Width; X += 4) { uint32 XLookup = (X >> 2)*16 + (X % 4); uint32 YLookup = (Y >> 2)*(Source->Info.Width*4) + (Y % 4)*4; uint32 PixelToSeek = XLookup + YLookup; uint8 *Pixel = (uint8 *)BitmapInfo->BitmapBuffer + PixelToSeek*Source->Info.BytesPerPixel; __m256i DestPixel = _mm256_loadu_si256((const __m256i *)Pixel); __m256 R_Dest = _mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_and_si256( DestPixel, FF)), Fraction255); __m256 G_Dest = _mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_and_si256(_mm256_srli_epi32(DestPixel, 8), FF)), Fraction255); __m256 B_Dest = _mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_and_si256(_mm256_srli_epi32(DestPixel, 16), FF)), Fraction255); __m256i A_Out = _mm256_and_si256(_mm256_srli_epi32(DestPixel, 24), FF); __m256 R_Blend = R_Dest; __m256 G_Blend = G_Dest; __m256 B_Blend = B_Dest; R_Blend = _mm256_max_ps(_mm256_min_ps(One, R_Blend), ZeroReal); G_Blend = _mm256_max_ps(_mm256_min_ps(One, G_Blend), ZeroReal); B_Blend = _mm256_max_ps(_mm256_min_ps(One, B_Blend), ZeroReal); __m256i R_Out = _mm256_cvttps_epi32(_mm256_mul_ps(R_Blend, Real255)); __m256i G_Out = _mm256_cvttps_epi32(_mm256_mul_ps(G_Blend, Real255)); __m256i B_Out = _mm256_cvttps_epi32(_mm256_mul_ps(B_Blend, Real255)); __m256i OutputPixel = _mm256_or_si256( _mm256_or_si256(R_Out, _mm256_slli_epi32(G_Out, 8)), _mm256_or_si256(_mm256_slli_epi32(B_Out, 16), _mm256_slli_epi32(A_Out, 24))); _mm256_storeu_si256((__m256i *)Pixel, OutputPixel); } } */