summaryrefslogtreecommitdiff
path: root/src/createcalls.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/createcalls.cpp')
-rw-r--r--src/createcalls.cpp1168
1 files changed, 1168 insertions, 0 deletions
diff --git a/src/createcalls.cpp b/src/createcalls.cpp
new file mode 100644
index 0000000..ee8888d
--- /dev/null
+++ b/src/createcalls.cpp
@@ -0,0 +1,1168 @@
+static void
+PostMsg(project_state *State, char *msg)
+{
+ State->MsgTime = 120;
+ State->Msg = msg;
+}
+
+static bool32
+File_Open(project_data *File, project_state *State, memory *Memory, char *Filename)
+{
+ SDL_RWops *FileHandle = SDL_RWFromFile(Filename, "r+b");
+ if (!FileHandle) {
+ return 0;
+ }
+ uint64 FileSize = SDL_RWseek(FileHandle, 0, RW_SEEK_END);
+ void *CompressedData = Memory_PushScratch(Memory, FileSize);
+ SDL_RWseek(FileHandle, 0, RW_SEEK_SET);
+ IO_ReadFromStream(CompressedData, FileSize, FileHandle);
+ SDL_RWclose(FileHandle);
+ Data_Decompress(Memory, CompressedData, FileSize, File, 0);
+ Memory_PopScratch(Memory, FileSize);
+
+ // Temp sources aren't cleaned out on file close, so we do it here.
+ int h = 0, c = 0, i = 0;
+ int Count = File->Source_Count;
+ while (Block_Loop(Memory, F_Sources, Count, &h, &c, &i)) {
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, i);
+ if (Source->Type == source_type_principal_temp) {
+ Source->Occupied = 0;
+ File->Source_Count--;
+ }
+ }
+ String_Copy(State->Filename, State->DummyName, 512);
+ State->Initializing = 4;
+ Memory->History.NumberOfEntries = 0;
+ Memory->History.EntryPlayhead = 0;
+ return 1;
+}
+
+static bool32
+IO_Save(project_data *File, project_state *State, memory *Memory, char *Filename)
+{
+ SDL_RWops *TestFile = SDL_RWFromFile(Filename, "wb");
+
+ if (!TestFile)
+ return 0;
+
+ uint8 *FileEndAddress = (uint8 *)Memory->Slot[F_PrincipalBitmaps].Address;
+ int h = 0, c = 0, i = 0;
+ while (Block_Loop(Memory, F_Sources, File->Source_Count, &h, &c, &i)) {
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, i);
+ if (Source->Type == source_type_principal) {
+ uint64 Size = Source->Width * Source->Height * Source->BytesPerPixel;
+ void *SourceBitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index, 0);
+ uint8 *BitmapEnd = (uint8 *)SourceBitmapAddress + Size;
+ if (BitmapEnd > FileEndAddress)
+ FileEndAddress = BitmapEnd;
+ }
+ }
+
+ Assert(FileEndAddress);
+ uint64 FileSize = FileEndAddress - (uint8 *)File;
+
+ void *CompressedLocation = Memory_PushScratch(Memory, FileSize);
+
+ uint64 CompressedSize = Data_Compress(Memory, File, FileSize, CompressedLocation, FileSize, 0); // Z_BEST_COMPRESSION);
+
+ Memory_PopScratch(Memory, FileSize);
+
+ IO_WriteToStream(CompressedLocation, CompressedSize, TestFile);
+ SDL_RWclose(TestFile);
+
+ Memory->IsFileSaved = true;
+
+ return 1;
+}
+
+static void
+File_SaveAs(project_data *File, project_state *State, memory *Memory, char *Filename)
+{
+ if (IO_Save(File, State, Memory, State->Filename)) {
+ PostMsg(State, "File saved!");
+ } else {
+ PostMsg(State, "File save failed...");
+ }
+}
+
+static void
+Playhead_Increment(int32 *Frame_Current, int32 Frame_Start, int32 Frame_End, int32 Increment)
+{
+ *Frame_Current += Increment;
+ if (*Frame_Current >= Frame_End) {
+ *Frame_Current = Frame_Start;
+ }
+ // if (*Frame_Current < Frame_Start) {
+ // }
+}
+
+static uint16
+Source_Generate_Blank(project_data *File, project_state *State, memory *Memory, uint16 Width, uint16 Height, uint16 BytesPerPixel)
+{
+ Assert(File->Source_Count < MAX_SOURCES);
+ uint16 Index = Memory_Block_AllocateNew(Memory, F_Sources);
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Index, 0);
+ History_Action_Block_Swap(Memory, F_Sources, Source);
+ Source->Occupied = 1;
+ Source->Width = Width;
+ Source->Height = Height;
+ Source->BytesPerPixel = BytesPerPixel;
+ Source->Type = source_type_principal;
+ Source->Bitmap_Index = Memory_Block_PrincipalBitmap_AllocateNew(File, State, Memory);
+ Source->Path_String_Index = String_AddToFile(Memory, "test");
+ History_Action_Swap(Memory, F_File, sizeof(File->Source_Count), &File->Source_Count);
+ File->Source_Count++;
+ return Index;
+}
+
+static bool32 Source_IsFileSupported(char *Path, bool32 *IsVideo, bool32 *HasAudio) {
+ AV_IsFileSupported(Path, IsVideo, HasAudio);
+ if (IsVideo || HasAudio) {
+ return 1;
+ } else {
+ return stbi_info(Path, NULL, NULL, NULL);
+ }
+ Assert(0);
+ return 0;
+}
+
+static void
+Source_Delete(project_data *File, memory *Memory, uint32 Index)
+{
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Index);
+ History_Action_Block_Swap(Memory, F_Sources, Source);
+ Source->Occupied = 0;
+ History_Action_Swap(Memory, F_File, sizeof(File->Source_Count), &File->Source_Count);
+ File->Source_Count--;
+}
+
+// These thumbnail textures aren't needed for anything else, so I'm just gonna
+// count on GL to retain them in GPU memory.
+static void
+Source_DumpThumbnail(memory *Memory, block_source *Source, uint32 T_Width, uint32 T_Height)
+{
+ Assert(Source->Type == source_type_principal_temp);
+ void *BitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index, 0);
+ uint32 Size = T_Height*T_Width*4;
+ uint8 *Output = (uint8 *)Memory_PushScratch(Memory, Size);
+ stbir_resize_uint8((uint8 *)BitmapAddress, Source->Width, Source->Height, 0, Output, T_Height, T_Width, 0, 4);
+
+ GL_GenAndBindTexture(&Source->ThumbnailTex, T_Height, T_Width, 4, Output);
+
+ Memory_PopScratch(Memory, Size);
+}
+
+static int16
+Source_Generate(project_data *File, project_state *State, memory *Memory, void *TempString)
+{
+ Assert(File->Source_Count < MAX_SOURCES);
+
+ bool32 IsVideo = 0, HasAudio = 0;
+ if (Source_IsFileSupported((char *)TempString, &IsVideo, &HasAudio)) {
+ uint16 Index = Memory_Block_AllocateNew(Memory, F_Sources);
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Index, 0);
+ History_Entry_Commit(Memory, "Add source");
+ History_Action_Block_Swap(Memory, F_Sources, Source);
+
+ Source->Occupied = 1;
+ Source->Path_String_Index = String_AddToFile(Memory, (char *)TempString);
+ Source->Type = source_type_file;
+ Source->HasAudio = HasAudio;
+ Source->HasVideo = IsVideo;
+
+ History_Action_Swap(Memory, F_File, sizeof(File->Source_Count), &File->Source_Count);
+ File->Source_Count++;
+ History_Entry_End(Memory);
+ return Index;
+ } else {
+ PostMsg(State, "File not supported...");
+ }
+
+ return -1;
+}
+
+
+static void
+Property_AddKeyframe(memory *Memory, memory_table_list TableName, property_channel *Property, int Frame, uint16 *ArrayLocation)
+{
+ History_Entry_Commit(Memory, "Add keyframe");
+ bezier_point Point = { 1, {(real32)Frame, Property->CurrentValue, -1, 0, 1, 0}, interpolation_type_linear, 0, {0, 0, 0}, 0 };
+ Bezier_Add(Memory, TableName, Property, Point, ArrayLocation);
+ History_Entry_End(Memory);
+}
+
+static property_channel
+Property_InitFloat(real32 Val, real32 ScrubVal, real32 MinVal, real32 MaxVal, bool32 AlwaysInteger) {
+ property_channel Property = {};
+ Property.CurrentValue = Val;
+ Property.MinVal = MinVal;
+ Property.MaxVal = MaxVal;
+ Property.AlwaysInteger = AlwaysInteger;
+ Property.ScrubVal = ScrubVal;
+ return Property;
+}
+
+static block_composition *
+Precomp_Init(project_data *File, memory *Memory)
+{
+ if (File->Comp_Count + 1 > MAX_COMPS) {
+ Assert(0);
+ }
+ block_composition *Comp = (block_composition *)Memory_Block_AllocateAddress(Memory, F_Precomps);
+ History_Action_Block_Swap(Memory, F_Precomps, Comp);
+
+ *Comp = {};
+ Comp->Occupied = 1;
+
+ Comp->Name_String_Index = Memory_Block_AllocateNew(Memory, F_Strings);
+ block_string *String = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Comp->Name_String_Index, 0);
+ sprintf(String->Char, "Comp %i", File->Comp_Count);
+ String->Occupied = 1;
+
+ History_Action_Swap(Memory, F_File, sizeof(File->Comp_Count), &File->Comp_Count);
+ File->Comp_Count++;
+ return Comp;
+}
+
+static uint32
+Effect_Init(project_state *State, memory *Memory, uint32 EffectEntryIndex, int EffectCount)
+{
+ uint16 EffectAddressIndex = Memory_Block_AllocateNew(Memory, F_Effects);
+ block_effect *Effect = (block_effect *)Memory_Block_AddressAtIndex(Memory, F_Effects, EffectAddressIndex, 0);
+ History_Action_Block_Swap(Memory, F_Effects, Effect);
+ *Effect = {};
+ Effect->Occupied = true;
+ header_effect *EffectHeader = &State->Effect[EffectEntryIndex];
+ String_Copy(Effect->ID, EffectHeader->ID, 8);
+ Effect->IsToggled = true;
+ for (int e = 0; e < EffectHeader->Property_Count; e++) {
+ Effect->Block_Property_Index[e] = Memory_Block_AllocateNew(Memory, F_Properties);
+ property_channel *Property = (property_channel *)Memory_Block_AddressAtIndex(Memory, F_Properties, Effect->Block_Property_Index[e], 0);
+ History_Action_Block_Swap(Memory, F_Properties, Property);
+ Property->Occupied = true;
+ header_property PropertyHeader = State->Property[EffectHeader->PropertyStartIndex + e];
+ Property->Identifier = -1;
+ Property->CurrentValue = PropertyHeader.DefaultValue;
+ Property->MinVal = PropertyHeader.MinVal;
+ Property->MaxVal = PropertyHeader.MaxVal;
+ }
+ return EffectAddressIndex;
+}
+
+static void
+Effect_Add(project_data *File, project_state *State, memory *Memory, uint32 EffectEntryIndex)
+{
+ History_Entry_Commit(Memory, "Add effect");
+ int h = 0, c = 0, i = 0;
+ int Selected = 0;
+ while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i))
+ {
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
+ if (Layer->IsSelected) {
+ Layer->Block_Effect_Index[Layer->Block_Effect_Count] = Effect_Init(State, Memory, EffectEntryIndex, Layer->Block_Effect_Count);
+ History_Action_Swap(Memory, F_File, sizeof(Layer->Block_Effect_Count), &Layer->Block_Effect_Count);
+ Layer->Block_Effect_Count++;
+ Selected++;
+ }
+ }
+ History_Entry_End(Memory);
+ Assert(Selected);
+}
+
+void Source_DeselectAll(project_data *File, memory *Memory)
+{
+ int h = 0, c = 0, i = 0;
+ while (Block_Loop(Memory, F_Sources, File->Source_Count, &h, &c, &i)) {
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, i);
+ Source->IsSelected = 0;
+ }
+}
+
+
+static void
+Keyframe_Commit(project_data *File, project_state *State, memory *Memory,
+ sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray,
+ sorted_property_array *SortedPropertyStart, uint16 *SortedKeyframeArray)
+{
+ History_Entry_Commit(Memory, "Move keyframe");
+ int h = 0, c = 0, i = 0;
+ while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) {
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
+ sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, Layer->Block_Composition_Index);
+ if ((State->TimelineMode == timeline_mode_graph) && !Layer->IsSelected)
+ continue;
+ sorted_property_array *InfoLocation = SortedPropertyStart + SortedLayerStart->SortedPropertyStart;
+ uint16 *ArrayLocation = SortedKeyframeArray + SortedLayerStart->SortedKeyframeStart;
+ int h = 0, c = 0, p = 0;
+ property_channel *Property = NULL;
+ block_effect *Effect = NULL;
+ while (Layer_LoopChannels(State, Memory, &InfoLocation, &ArrayLocation, Layer, &Property, &Effect, &h, &c, &p))
+ {
+ if ((State->TimelineMode != timeline_mode_graph) && !Property->IsToggled)
+ continue;
+ for (int p = 0; p < Property->Keyframe_Count; p++) {
+ int k = ArrayLocation[p];
+ bezier_point *PointAddress = Bezier_LookupAddress(Memory, Property, k);
+ if (PointAddress->IsSelected) {
+ v2 NewPos[3];
+ Bezier_Interact_Evaluate(State, PointAddress, NewPos);
+ History_Action_Swap(Memory, F_Bezier, sizeof(PointAddress->Pos), &PointAddress->Pos);
+ PointAddress->Pos[0] = NewPos[0];
+ PointAddress->Pos[1] = NewPos[1];
+ PointAddress->Pos[2] = NewPos[2];
+ }
+ }
+ }
+ }
+ History_Entry_End(Memory);
+ State->Interact_Offset[0] = 0;
+ State->Interact_Offset[1] = 0;
+ State->Interact_Offset[2] = 0;
+ State->Interact_Offset[3] = 0;
+ State->Interact_Active = interact_type_none;
+ State->Interact_Modifier = 0;
+}
+
+// NOTE(fox): This won't work with precomps!
+
+void Clipboard_Paste(project_data *File, project_state *State, memory *Memory, sorted_comp_array *SortedCompStart, sorted_layer_array *SortedLayerStart, uint16 *SortedKeyframeArray)
+{
+ clipboard_contents *Contents = (clipboard_contents *)State->ClipboardBuffer;
+ if (Contents->Type == selection_type_none)
+ return;
+ uint64 ClipboardPos = sizeof(clipboard_contents);
+ ClipboardPos = sizeof(clipboard_contents);
+ int i = SortedCompStart->LayerCount - 1;
+ block_layer *Layer = NULL;
+ clipboard_channel *Channel;
+ int b = 0;
+ int LayerCount = 0;
+
+ int NumberOfLayersFromClipboard = 1;
+ int LastOffset = 0;
+ for (int a = 0; a < Contents->ChannelCount; a++) {
+ Channel = &Contents->Channel[a];
+ if (a != 0) {
+ if (Channel->LayerOffset != LastOffset)
+ NumberOfLayersFromClipboard++;
+ }
+ LastOffset = Channel->LayerOffset;
+ }
+
+ for (;;) {
+ Channel = &Contents->Channel[b];
+ while (i >= 0)
+ {
+ sorted_layer_array SortEntry = SortedLayerStart[i];
+ uint32 Index_Physical = SortEntry.Block_Layer_Index;
+ block_layer *TestLayer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
+ if (TestLayer->IsSelected) {
+ Layer = TestLayer;
+ break;
+ }
+ i--;
+ }
+ if (Layer == NULL)
+ break;
+ // NOTE(fox): This loop assumes all layers and the clipboard have
+ // channels laid out in the same way!
+ Assert(0);
+ /*
+ for (int h = 0; h < AmountOf(Layer->Property); h++) {
+ property_channel *Property = &Layer->Property[h];
+ if (Property->Name == Channel->Name) {
+ for (int p = 0; p < Channel->KeyframeCount; p++) {
+ bezier_point PointData = *(bezier_point *)((uint8 *)State->ClipboardBuffer + ClipboardPos);
+ PointData.Pos[0].x += State->Frame_Current;
+ uint16 *ArrayLocation = Property_GetSortedArray(SortedKeyframeArray, i, h);
+ Bezier_Add(Memory, F_Layers, Property, PointData, ArrayLocation);
+ ClipboardPos += sizeof(bezier_point);
+ }
+ b++;
+ Channel = &Contents->Channel[b];
+ }
+ }
+ Layer = NULL;
+ if (b < Contents->ChannelCount) {
+ if (NumberOfLayersFromClipboard != 1)
+ break;
+ else
+ b = 0;
+ }
+ */
+ }
+}
+
+void Clipboard_Store(project_data *File, project_state *State, memory *Memory, sorted_comp_array *SortedCompStart, sorted_layer_array *SortedLayerStart, sorted_property_array *SortedPropertyStart, uint16 *SortedKeyframeArray)
+{
+ int LocalOffset = 0;
+ clipboard_contents *Contents = (clipboard_contents *)State->ClipboardBuffer;
+ *Contents = {};
+ Contents->Type = State->RecentSelectionType;
+ uint64 ClipboardPos = sizeof(clipboard_contents);
+ if (Contents->Type == selection_type_none)
+ return;
+ else if (Contents->Type == selection_type_keyframe) {
+ for (int i = SortedCompStart->LayerCount - 1; i >= 0; i--)
+ {
+ sorted_layer_array SortEntry = SortedLayerStart[i];
+ uint32 Index_Physical = SortEntry.Block_Layer_Index;
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical);
+ for (int h = 0; h < AmountOf(Layer->Property); h++) {
+ property_channel *Property = &Layer->Property[h];
+ if (Property->IsToggled || Layer->IsSelected) {
+ sorted_property_array *InfoLocation = Property_GetSortedInfo(SortedPropertyStart, i, h);
+ uint16 *ArrayLocation = Property_GetSortedArray(SortedKeyframeArray, i, h);
+ clipboard_channel *Channel = &Contents->Channel[Contents->ChannelCount];
+ bezier_point *FirstPoint = NULL;
+ int TimeOffset = 0;
+ for (int p = 0; p < Property->Keyframe_Count; p++) {
+ bezier_point *PointAddress = Bezier_LookupAddress(Memory, Property, ArrayLocation[p]);
+ if (PointAddress->IsSelected) {
+ if (!FirstPoint) {
+ FirstPoint = PointAddress;
+ TimeOffset = FirstPoint->Pos[0].x;
+ }
+ bezier_point PointToCopy = *PointAddress;
+ PointToCopy.Pos[0].x -= TimeOffset;
+ Memory_Copy((uint8 *)State->ClipboardBuffer + ClipboardPos, (uint8 *)&PointToCopy, sizeof(bezier_point));
+ ClipboardPos += sizeof(bezier_point);
+ Channel->KeyframeCount++;
+ }
+ }
+ if (Channel->KeyframeCount) {
+ if (!LocalOffset)
+ LocalOffset = i;
+ Contents->ChannelCount++;
+ Channel->LayerOffset = LocalOffset - i;
+ Assert(0);
+ // Channel->Name = Property->Name;
+ }
+ }
+ }
+ }
+ }
+ else if (Contents->Type == selection_type_layer) {
+ }
+}
+
+void Property_MinMax_X(memory *Memory, project_state *State, property_channel *Property,
+ uint16 *ArrayLocation, real32 *Min, real32 *Max)
+{
+ v2 FirstPointPos[3];
+ bezier_point *FirstPointAddress = Bezier_LookupAddress(Memory, Property, ArrayLocation[0]);
+ Bezier_Interact_Evaluate(State, FirstPointAddress, FirstPointPos);
+ *Min = FirstPointPos[0].x;
+ v2 LastPointPos[3];
+ bezier_point *LastPointAddress = Bezier_LookupAddress(Memory, Property, ArrayLocation[Property->Keyframe_Count - 1]);
+ Bezier_Interact_Evaluate(State, LastPointAddress, LastPointPos);
+ *Max = LastPointPos[0].x;
+}
+void Property_MinMax_Y(memory *Memory, project_state *State, property_channel *Property,
+ sorted_property_array *PropertyStart, real32 *Min, real32 *Max, bool32 Evaluate = 1)
+{
+ if (Evaluate) {
+ v2 MinYPointPos[3];
+ bezier_point *MinYPointAddress = Bezier_LookupAddress(Memory, Property, PropertyStart->MinYIndex);
+ Bezier_Interact_Evaluate(State, MinYPointAddress, MinYPointPos);
+ *Min = MinYPointPos[0].y;
+ v2 MaxYPointPos[3];
+ bezier_point *MaxYPointAddress = Bezier_LookupAddress(Memory, Property, PropertyStart->MaxYIndex);
+ Bezier_Interact_Evaluate(State, MaxYPointAddress, MaxYPointPos);
+ *Max = MaxYPointPos[0].y;
+ } else {
+ bezier_point *MinYPointAddress = Bezier_LookupAddress(Memory, Property, PropertyStart->MinYIndex);
+ *Min = MinYPointAddress->Pos[0].y;
+ bezier_point *MaxYPointAddress = Bezier_LookupAddress(Memory, Property, PropertyStart->MaxYIndex);
+ *Max = MaxYPointAddress->Pos[0].y;
+ }
+}
+
+inline property_channel *
+Effect_Property(memory *Memory, block_effect *Effect, int Offset)
+{
+ return (property_channel *)Memory_Block_AddressAtIndex(Memory, F_Properties, Effect->Block_Property_Index[Offset]);
+};
+
+inline v2 Effect_V2(memory *Memory, block_effect *Effect, int Offset)
+{
+ property_channel *Property_X = (property_channel *)Memory_Block_AddressAtIndex(Memory, F_Properties, Effect->Block_Property_Index[Offset]);
+ property_channel *Property_Y = (property_channel *)Memory_Block_AddressAtIndex(Memory, F_Properties, Effect->Block_Property_Index[Offset + 1]);
+ return V2(Property_X->CurrentValue, Property_Y->CurrentValue);
+}
+
+// TODO(fox): Merge with other sorting code.
+void Effect_Curves_Sort(memory *Memory, block_effect *Effect, uint16 *SortedPointStart, uint16 Which)
+{
+ int i = 0;
+ int SortedPoints = 0;
+ for (;;) {
+ property_channel *CurrentProperty = (property_channel *)Memory_Block_AddressAtIndex(Memory, F_Properties, Effect->Block_Property_Index[i]);
+ v2 Point = Effect_V2(Memory, Effect, i);
+ uint32 SortedIndex_Playhead = 0;
+ if (CurrentProperty->Identifier == Which) {
+ while (SortedIndex_Playhead < SortedPoints) {
+ uint16 TestPointEntry = SortedPointStart[SortedIndex_Playhead];
+ Assert(((property_channel *)Memory_Block_AddressAtIndex(Memory, F_Properties, Effect->Block_Property_Index[TestPointEntry]))->Identifier == Which);
+ v2 TestPoint = Effect_V2(Memory, Effect, TestPointEntry);
+ if (Point.x < TestPoint.x) {
+ break;
+ } else {
+ SortedIndex_Playhead += 1;
+ }
+ }
+ if (SortedIndex_Playhead != SortedPoints) {
+ uint8 *Address_Start = (uint8 *)(SortedPointStart + SortedIndex_Playhead);
+ uint8 *Address_End = (uint8 *)(SortedPointStart + SortedPoints) - 1;
+ Arbitrary_ShiftData(Address_Start, Address_End, sizeof(uint16), 1);
+ }
+
+ uint16 *PointEntry = SortedPointStart + SortedIndex_Playhead;
+ *PointEntry = i;
+ SortedPoints++;
+ }
+ i += 2;
+ if (i > (MAX_PROPERTIES_PER_EFFECT - 1))
+ break;
+ }
+}
+
+static void
+Interact_Evaluate_Layer(memory *Memory, project_state *State, uint16 Layer_Index_Physical, sorted_comp_array SortedCompStart, sorted_layer_array *SortedLayerStart,
+ int32 *Frame_Start, int32 *Frame_End)
+{
+ if (State->Interact_Active == interact_type_layer_move) {
+ *Frame_Start += (int32)(State->Interact_Offset[0] + 0);
+ *Frame_End += (int32)(State->Interact_Offset[0] + 0);
+ }
+ if (State->Interact_Active == interact_type_layer_timeadjust) {
+ int Side[2] = {0};
+ Assert(State->Interact_Offset[1] == 0 || State->Interact_Offset[1] == 1);
+ Side[(int)State->Interact_Offset[1]] = 1;
+ *Frame_Start += (int32)(State->Interact_Offset[0] * Side[0]);
+ if (*Frame_Start >= *Frame_End)
+ *Frame_Start = *Frame_End - 1;
+ *Frame_End += (int32)(State->Interact_Offset[0] * Side[1]);
+ if (*Frame_End <= *Frame_Start)
+ *Frame_End = *Frame_Start + 1;
+ }
+}
+
+void File_DeselectAllKeyframes(project_data *File, project_state *State, memory *Memory,
+ sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray,
+ sorted_property_array *SortedPropertyStart, uint16 *SortedKeyframeArray)
+{
+ int h = 0, c = 0, i = 0;
+ while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) {
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
+ if (!Layer->IsSelected)
+ continue;
+ sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, Layer->Block_Composition_Index);
+ sorted_property_array *InfoLocation = SortedPropertyStart + SortedLayerStart->SortedPropertyStart;
+ uint16 *ArrayLocation = SortedKeyframeArray + SortedLayerStart->SortedKeyframeStart;
+ int h = 0, c = 0, p = 0;
+ property_channel *Property = NULL;
+ block_effect *Effect = NULL;
+ while (Layer_LoopChannels(State, Memory, &InfoLocation, &ArrayLocation, Layer, &Property, &Effect, &h, &c, &p))
+ {
+ for (int p = 0; p < Property->Keyframe_Count; p++) {
+ int k = ArrayLocation[p];
+ bezier_point *PointAddress = Bezier_LookupAddress(Memory, Property, k);
+ PointAddress->IsSelected = 0;
+ }
+ }
+ }
+}
+
+
+void Layer_Evaluate_Display(project_state *State, memory *Memory, block_layer *Layer,
+ sorted_property_array *SortedPropertyStart, uint16 *SortedKeyframeArray,
+ sorted_layer_array *LayerArrayStart, sorted_comp_array *CompStart, sorted_layer_array *SortedLayerStart,
+ int i, real32 *Offset)
+{
+ int ExtraPad = 1;
+ sorted_property_array *InfoLocation = SortedPropertyStart + SortedLayerStart->SortedPropertyStart;
+ uint16 *ArrayLocation = SortedKeyframeArray + SortedLayerStart->SortedKeyframeStart;
+ int h = 0, c = 0, p = 0;
+ property_channel *Property = NULL;
+ block_effect *Effect = NULL;
+ while (Layer_LoopChannels(State, Memory, &InfoLocation, &ArrayLocation, Layer, &Property, &Effect, &h, &c, &p))
+ {
+ if (Property->IsToggled) {
+ *Offset += 1 + ExtraPad;
+ ExtraPad = 0;
+ }
+ }
+ /*
+ if (Layer->Precomp_Toggled) {
+ Assert(Layer->IsPrecomp);
+ sorted_comp_array *Layer_SortedCompStart = &CompStart[Layer->Block_Source_Index];
+ sorted_layer_array *Layer_SortedLayerStart = Sorted_GetLayerStart(LayerArrayStart, CompStart, Layer->Block_Source_Index);
+ sorted_layer_array *TopLayerEntry = &Layer_SortedLayerStart[0];
+ sorted_layer_array *BottomLayerEntry = &Layer_SortedLayerStart[Layer_SortedCompStart->LayerCount - 1];
+ *Offset += TopLayerEntry->SortedOffset - BottomLayerEntry->SortedOffset + 2;
+ }
+ */
+}
+
+static void
+Project_Layer_Delete(project_data *File, project_state *State, memory *Memory)
+{
+ bool32 CommitAction = 0;
+ int h = 0, c = 0, i = 0;
+ int LayerCount = File->Layer_Count;
+ while (Block_Loop(Memory, F_Layers, LayerCount, &h, &c, &i)) {
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
+ if (Layer->IsSelected) {
+ if (!CommitAction) {
+ History_Entry_Commit(Memory, "Delete layer");
+ CommitAction = 1;
+ }
+ Layer_Delete(File, State, Memory, i);
+ }
+ }
+ if (CommitAction) {
+ History_Entry_End(Memory);
+ State->UpdateFrame = true;
+ State->MostRecentlySelectedLayer = -1;
+ }
+}
+
+static bool32
+Property_IsGraphSelected(memory *Memory, property_channel *Property, uint16 *ArrayLocation)
+{
+ for (int p = 0; p < Property->Keyframe_Count; p++) {
+ int k = ArrayLocation[p];
+ bezier_point *Point = Bezier_LookupAddress(Memory, Property, k);
+ if (Point->IsSelected)
+ return 1;
+ }
+ return 0;
+}
+
+static void
+Project_PaintLayer_New(project_data *File, project_state *State, memory *Memory)
+{
+ int TopOffset = Layer_GetTopOffset(File, Memory);
+ block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
+ Arbitrary_Zero((uint8 *)State->Brush.TransientBitmap, 2048*2048*4);
+ State->Interact_Active = interact_type_brush;
+ Layer_DeselectAll(File, State, Memory);
+ History_Entry_Commit(Memory, "Paint new layer");
+ uint16 i = Source_Generate_Blank(File, State, Memory, MainComp->Width, MainComp->Height, MainComp->BytesPerPixel);
+ block_layer *Layer = Layer_Init(File, Memory);
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index);
+ State->Brush.LayerToPaint_Index = Memory_Block_LazyIndexAtAddress(Memory, F_Layers, (void *)Layer);
+ Layer->Block_Source_Index = i;
+ Layer->x.CurrentValue = MainComp->Width / 2;
+ Layer->y.CurrentValue = MainComp->Height / 2;
+ if (File->Layer_Count == 1) {
+ Layer->Vertical_Offset = 11 - 1;
+ } else {
+ Layer->Vertical_Offset = TopOffset - 1;
+ }
+ Layer->Frame_Start = MainComp->Frame_Start;
+ Layer->Frame_End = MainComp->Frame_End;
+ Layer_Select(Memory, State, Memory_Block_LazyIndexAtAddress(Memory, F_Layers, Layer));
+ History_Entry_End(Memory);
+ State->UpdateFrame = true;
+}
+
+void Source_UICreateButton(project_data *File, project_state *State, memory *Memory)
+{
+ block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
+ int TopOffset = Layer_GetTopOffset(File, Memory);
+ bool32 CommitAction = 0;
+ int h = 0, c = 0, i = 0;
+ while (Block_Loop(Memory, F_Sources, File->Source_Count, &h, &c, &i)) {
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, i);
+ if (Source->IsSelected) {
+ if (!CommitAction) {
+ History_Entry_Commit(Memory, "Create layer from source");
+ CommitAction = 1;
+ }
+ if (Source->Type == source_type_principal_temp) {
+ History_Action_Swap(Memory, F_File, sizeof(Source->Type), &Source->Type);
+ Source->Type = source_type_principal;
+ }
+ block_layer *Layer = Layer_Init(File, Memory);
+ Layer->Block_Source_Index = i;
+ Layer->x.CurrentValue = Comp->Width/2;
+ Layer->y.CurrentValue = Comp->Height/2;
+ Layer->Vertical_Offset = TopOffset-1;
+ Layer->Frame_Start = Comp->Frame_Start;
+ Layer->Frame_End = Comp->Frame_End;
+ }
+ State->UpdateFrame = true;
+ }
+ if (CommitAction)
+ History_Entry_End(Memory);
+ Source_DeselectAll(File, Memory);
+}
+
+void Precomp_UIDuplicate(project_data *File, project_state *State, memory *Memory, uint16 CompIndex,
+ sorted_comp_array SortedCompStart, sorted_layer_array *SortedLayerStart)
+{
+ block_layer *Layer = NULL;
+ for (int i = SortedCompStart.LayerCount - 1; i >= 0; i--)
+ {
+ sorted_layer_array SortEntry = SortedLayerStart[i];
+ uint32 Index_Physical = SortEntry.Block_Layer_Index;
+ Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical);
+ if (Layer->IsSelected) {
+ break;
+ }
+ }
+ if (Layer) {
+ block_layer *DupeLayer = Layer_Init(File, Memory);
+ *DupeLayer = *Layer;
+ DupeLayer->Vertical_Offset += 1;
+ for (int h = 0; h < AmountOf(Layer->Property); h++) {
+ property_channel *Property = &Layer->Property[h];
+ if (Property->Block_Bezier_Count) {
+ property_channel *DupeProperty = &DupeLayer->Property[h];
+ DupeProperty->Block_Bezier_Index[0] = Memory_Block_AllocateNew(Memory, F_Bezier);
+ block_bezier *Bezier = (block_bezier *)Memory_Block_AddressAtIndex(Memory, F_Bezier, Property->Block_Bezier_Index[0]);
+ block_bezier *DupeBezier = (block_bezier *)Memory_Block_AddressAtIndex(Memory, F_Bezier, DupeProperty->Block_Bezier_Index[0], 0);
+ Bezier->Occupied = true;
+ *DupeBezier = *Bezier;
+ }
+ }
+ }
+}
+
+void Precomp_UIDelete(project_data *File, project_state *State, memory *Memory, uint16 CompIndex,
+ sorted_comp_array SortedCompStart, sorted_layer_array *SortedLayerStart)
+{
+ block_layer *Layer = NULL;
+ for (int i = SortedCompStart.LayerCount - 1; i >= 0; i--)
+ {
+ sorted_layer_array SortEntry = SortedLayerStart[i];
+ uint32 Index_Physical = SortEntry.Block_Layer_Index;
+ Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical);
+ if (Layer->IsSelected) {
+ }
+ }
+}
+
+void Precomp_UICreateButton(project_data *File, project_state *State, memory *Memory, uint16 CompIndex,
+ sorted_comp_array SortedCompStart, sorted_layer_array *SortedLayerStart)
+{
+ block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, CompIndex);
+ History_Entry_Commit(Memory, "Pre-compose layer");
+ block_composition *NewComp = Precomp_Init(File, Memory);
+ NewComp->Width = Comp->Width;
+ NewComp->Height = Comp->Height;
+ NewComp->FPS = Comp->FPS;
+ NewComp->BytesPerPixel = Comp->BytesPerPixel;
+ NewComp->Frame_Count = Comp->Frame_Count;
+ NewComp->Frame_Start = Comp->Frame_Start;
+ NewComp->Frame_End = Comp->Frame_End;
+ int32 TopOffset = 0;
+ for (int i = SortedCompStart.LayerCount - 1; i >= 0; i--)
+ {
+ sorted_layer_array SortEntry = SortedLayerStart[i];
+ uint32 Index_Physical = SortEntry.Block_Layer_Index;
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical);
+ if (Layer->IsSelected) {
+ TopOffset = Layer->Vertical_Offset;
+ break;
+ }
+ }
+ for (int i = SortedCompStart.LayerCount - 1; i >= 0; i--)
+ {
+ sorted_layer_array SortEntry = SortedLayerStart[i];
+ uint32 Index_Physical = SortEntry.Block_Layer_Index;
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical);
+ if (Layer->IsSelected) {
+ History_Action_Swap(Memory, F_File, sizeof(Layer->Block_Composition_Index), &Layer->Block_Composition_Index);
+ Layer->Block_Composition_Index = File->Comp_Count - 1;
+ }
+ }
+ block_layer *PrecompLayer = Layer_Init(File, Memory);
+ bezier_point Point0 = { 1, {0, 0, 1, 0, 1, 0}, interpolation_type_linear, 0, {0, 0, 0}, 0 };
+ bezier_point Point1 = { 1, {(real32)NewComp->Frame_End, (real32)NewComp->Frame_End, 1, 0, 1, 0}, interpolation_type_linear, 0, {0, 0, 0}, 0 };
+ Bezier_Add(Memory, F_Layers, &PrecompLayer->time, Point0, NULL);
+ Bezier_Add(Memory, F_Layers, &PrecompLayer->time, Point1, NULL);
+ PrecompLayer->IsPrecomp = true;
+ Layer_Select(Memory, State, Memory_Block_LazyIndexAtAddress(Memory, F_Layers, PrecompLayer));
+ PrecompLayer->Block_Source_Index = File->Comp_Count - 1;
+ PrecompLayer->Block_Composition_Index = CompIndex;
+ PrecompLayer->Vertical_Offset = TopOffset;
+ PrecompLayer->Frame_End = NewComp->Frame_End;
+ PrecompLayer->ColIndex = 3;
+ PrecompLayer->x.CurrentValue = Comp->Width/2;
+ PrecompLayer->y.CurrentValue = Comp->Height/2;
+ History_Entry_End(Memory);
+ State->UpdateFrame = true;
+}
+
+
+// Helper for working with different bit depths.
+static render_byte_info
+Bitmap_ByteInfo(uint32 BytesPerPixel) {
+ render_byte_info Byte = {};
+ if (BytesPerPixel == 4) {
+ Byte.MaskPixel = 0xFF;
+ Byte.ByteOffset = 1;
+ Byte.Normalized = 1 / 255.0f;
+ Byte.Bits = 255;
+ } else if (BytesPerPixel == 8) {
+ Byte.MaskPixel = 0xFFFF;
+ Byte.ByteOffset = 2;
+ Byte.Normalized = 1 / 65535.0f;
+ Byte.Bits = 65535;
+ } else {
+ Byte.MaskPixel = 0xFFFFFFFF;
+ Byte.ByteOffset = 4;
+ Byte.Normalized = 1 / 4294967295.0f;
+ Byte.Bits = 4294967295;
+ Assert(0);
+ }
+ return Byte;
+}
+
+// TODO(fox): Make separate full-size bitmap that gets scaled on the GPU instead of this
+static void
+State_BindBrushTexture(memory *Memory, brush_state *Brush, uint32 BytesPerPixel)
+{
+ GL_GenAndBindTexture(&Brush->GLTexture, Brush->Size, Brush->Size, BytesPerPixel, Brush->PaintBuffer);
+}
+
+static void
+Brush_CalcBitmapAlphaFromSize(memory *Memory, brush_state *Brush, uint32 BytesPerPixel)
+{
+ int32 BrushLength = Brush->Size;
+ if (BrushLength > 128) {
+ BrushLength = BrushLength + (16 - (BrushLength % 16));
+ }
+ real32 BrushCenter = (real32)BrushLength / 2;
+ real32 MaxLength = BrushCenter;
+ void *BrushAddress = Brush->PaintBuffer;
+
+ render_byte_info Byte = Bitmap_ByteInfo(BytesPerPixel);
+
+ for (int Y = 0; Y < BrushLength; Y++) {
+ for (int X = 0; X < BrushLength; X++) {
+ uint8 *PixelAddress = (uint8 *)BrushAddress + (Y * BytesPerPixel*BrushLength) + (X * BytesPerPixel);
+ uint32 *R_DestAddress = (uint32 *)(PixelAddress + Byte.ByteOffset*0);
+ // uint32 *G_DestAddress = (uint32 *)(PixelAddress + Byte.ByteOffset*1);
+ // uint32 *B_DestAddress = (uint32 *)(PixelAddress + Byte.ByteOffset*2);
+ uint32 *A_DestAddress = (uint32 *)(PixelAddress + Byte.ByteOffset*3);
+ v2 Pos = V2(BrushCenter - X, BrushCenter - Y);
+ real32 L = sqrt(LengthSq(Pos));
+ real32 Gradient = Ceil(L, MaxLength) / MaxLength;
+ Gradient = pow(Gradient, Brush->Hardness);
+ // Gradient = (Gradient >= 0.04045) ? pow((Gradient + 0.055) / (1 + 0.055), 2.4) : Gradient / 12.92;
+ // Gradient = (Gradient >= 0.0031308) ? (1.055) * pow(Gradient, (1.0/2.4)) - 0.055 : 12.92 * Gradient;
+ *R_DestAddress = (*R_DestAddress & ~Byte.MaskPixel) | Byte.Bits; // brush preview is red
+ *A_DestAddress = (*A_DestAddress & ~Byte.MaskPixel) | (uint32)((1.0f - Gradient)*Byte.Bits);
+ }
+ }
+}
+
+// Imported bitmaps are stored in linear, and all ops are also done in linear.
+static void
+Bitmap_SRGBToLinear(void *Buffer, uint16 Width, uint16 Height, uint16 BytesPerPixel, bool32 ToLinear)
+{
+ uint8 *Row = (uint8 *)Buffer;
+ uint64 TotalBytes = Width * Height * BytesPerPixel;
+
+ render_byte_info LayerBits = Bitmap_ByteInfo(BytesPerPixel);
+
+ uint64 bytes = 0;
+ while (bytes < TotalBytes) {
+ uint8 *LayerPixel = Row + bytes;
+ uint32 *R_DestAddress = (uint32 *)(LayerPixel + LayerBits.ByteOffset * 0);
+ uint32 *G_DestAddress = (uint32 *)(LayerPixel + LayerBits.ByteOffset * 1);
+ uint32 *B_DestAddress = (uint32 *)(LayerPixel + LayerBits.ByteOffset * 2);
+ uint32 *A_DestAddress = (uint32 *)(LayerPixel + LayerBits.ByteOffset * 3);
+
+ real32 TexR = (real32)(*R_DestAddress & LayerBits.MaskPixel) * LayerBits.Normalized;
+ real32 TexG = (real32)(*G_DestAddress & LayerBits.MaskPixel) * LayerBits.Normalized;
+ real32 TexB = (real32)(*B_DestAddress & LayerBits.MaskPixel) * LayerBits.Normalized;
+ real32 TexA = (real32)(*A_DestAddress & LayerBits.MaskPixel) * LayerBits.Normalized;
+
+ if (ToLinear) {
+ TexR = (TexR >= 0.04045) ? pow((TexR + 0.055) / (1 + 0.055), 2.4) : TexR / 12.92;
+ TexG = (TexG >= 0.04045) ? pow((TexG + 0.055) / (1 + 0.055), 2.4) : TexG / 12.92;
+ TexB = (TexB >= 0.04045) ? pow((TexB + 0.055) / (1 + 0.055), 2.4) : TexB / 12.92;
+ TexA = (TexA >= 0.04045) ? pow((TexA + 0.055) / (1 + 0.055), 2.4) : TexA / 12.92;
+ } else {
+ TexR = (TexR >= 0.0031308) ? (1.055) * pow(TexR, (1.0/2.4)) - 0.055 : 12.92 * TexR;
+ TexG = (TexG >= 0.0031308) ? (1.055) * pow(TexG, (1.0/2.4)) - 0.055 : 12.92 * TexG;
+ TexB = (TexB >= 0.0031308) ? (1.055) * pow(TexB, (1.0/2.4)) - 0.055 : 12.92 * TexB;
+ TexA = (TexA >= 0.0031308) ? (1.055) * pow(TexA, (1.0/2.4)) - 0.055 : 12.92 * TexA;
+ }
+
+ uint32 R_Out = (uint32)(Normalize(TexR) * LayerBits.Bits);
+ uint32 G_Out = (uint32)(Normalize(TexG) * LayerBits.Bits);
+ uint32 B_Out = (uint32)(Normalize(TexB) * LayerBits.Bits);
+ uint32 A_Out = (uint32)(Normalize(TexA) * LayerBits.Bits);
+
+ *R_DestAddress = (*R_DestAddress & ~LayerBits.MaskPixel) | R_Out;
+ *G_DestAddress = (*G_DestAddress & ~LayerBits.MaskPixel) | G_Out;
+ *B_DestAddress = (*B_DestAddress & ~LayerBits.MaskPixel) | B_Out;
+ *A_DestAddress = (*A_DestAddress & ~LayerBits.MaskPixel) | A_Out;
+ bytes += BytesPerPixel;
+ }
+}
+
+static void
+Brush_Info(brush_info *B, brush_state *Brush, block_source *Source, void *SourceBuffer, v2 LayerPos, v4 Color)
+{
+ B->BrushLength = (uint32)Brush->Size;
+ if (B->BrushLength > 128) {
+ B->BrushLength = B->BrushLength + (16 - (B->BrushLength % 16));
+ }
+
+ rectangle RenderRegion = { 0, 0, Source->Width, Source->Height };
+ rectangle BrushPos = { (int32)(LayerPos.x - (B->BrushLength / 2)),
+ (int32)(LayerPos.y - (B->BrushLength / 2)),
+ (int32)(LayerPos.x + (B->BrushLength / 2)),
+ (int32)(LayerPos.y + (B->BrushLength / 2)) };
+
+ B->LayerBounds = ClipRectangle(BrushPos, RenderRegion);
+
+ if (BrushPos.Min.x < Brush->CacheBounds.Min.x)
+ Brush->CacheBounds.Min.x = BrushPos.Min.x;
+ if (BrushPos.Min.y < Brush->CacheBounds.Min.y)
+ Brush->CacheBounds.Min.y = BrushPos.Min.y;
+ if (BrushPos.Max.x > Brush->CacheBounds.Max.x)
+ Brush->CacheBounds.Max.x = BrushPos.Max.x;
+ if (BrushPos.Max.y > Brush->CacheBounds.Max.y)
+ Brush->CacheBounds.Max.y = BrushPos.Max.y;
+
+ Assert(Source->Type == source_type_principal);
+
+ B->BytesPerPixel = 4;
+ B->SourceWidth = Source->Width;
+ B->SourceBytesPerPixel = Source->BytesPerPixel;
+ B->SourceBuffer = SourceBuffer;
+
+ Assert(Source->BytesPerPixel == 4);
+
+ B->LayerPitch = Source->Width*Source->BytesPerPixel;
+ B->BrushPitch = (int)B->BrushLength * B->BytesPerPixel;
+
+ B->LayerBits = Bitmap_ByteInfo(Source->BytesPerPixel);
+ B->BrushBits = Bitmap_ByteInfo(B->BytesPerPixel);
+
+ B->ExtraX = 0;
+ B->ExtraY = 0;
+ if (BrushPos.Min.x < 0) {
+ B->ExtraX = BrushPos.Min.x;
+ }
+ if (BrushPos.Min.y < 0) {
+ B->ExtraY = BrushPos.Min.y;
+ }
+
+ B->BrushBuffer = Brush->PaintBuffer;
+ B->EraseMode = Brush->EraseMode;
+
+ B->R_Brush = (Color.r >= 0.04045) ? pow((Color.r + 0.055) / (1 + 0.055), 2.4) : Color.r / 12.92;
+ B->G_Brush = (Color.g >= 0.04045) ? pow((Color.g + 0.055) / (1 + 0.055), 2.4) : Color.g / 12.92;
+ B->B_Brush = (Color.b >= 0.04045) ? pow((Color.b + 0.055) / (1 + 0.055), 2.4) : Color.b / 12.92;
+ B->A_Brush = (Color.a >= 0.04045) ? pow((Color.a + 0.055) / (1 + 0.055), 2.4) : Color.a / 12.92;
+
+ B->BrushRow = (uint8 *)B->BrushBuffer;
+}
+
+void Bitmap_SwapData(uint8 *Address_0, uint8 *Address_1, uint64 Size, uint16 BytesPerPixel)
+{
+ uint64 i = 0;
+ uint16 ByteOffset = Bitmap_ByteInfo(BytesPerPixel).ByteOffset;
+ uint64 RemainderBytes = Size % ByteOffset;
+ Assert(BytesPerPixel == 4);
+
+ while (i < Size) {
+ uint32 *Pixel_0 = (uint32 *)(Address_0 + i);
+ uint32 *Pixel_1 = (uint32 *)(Address_1 + i);
+ if (*Pixel_0 != 0x00000000) {
+ uint32 Temp = *Pixel_1;
+ *Pixel_1 = *Pixel_0;
+ *Pixel_0 = Temp;
+ }
+ i += sizeof(uint32);
+ }
+}
+
+
+static void
+PaintTest(brush_info B, void *CacheBuffer, rectangle RenderRegion)
+{
+ 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*B.LayerPitch + X*B.SourceBytesPerPixel;
+ uint8 *LayerPixel = (uint8 *)CacheBuffer + Offset;
+
+ uint32 *R_DestAddress = (uint32 *)(LayerPixel + B.LayerBits.ByteOffset * 0);
+ uint32 *G_DestAddress = (uint32 *)(LayerPixel + B.LayerBits.ByteOffset * 1);
+ uint32 *B_DestAddress = (uint32 *)(LayerPixel + B.LayerBits.ByteOffset * 2);
+ uint32 *A_DestAddress = (uint32 *)(LayerPixel + B.LayerBits.ByteOffset * 3);
+
+ real32 R_Layer = (real32)(*R_DestAddress & B.LayerBits.MaskPixel) * B.LayerBits.Normalized;
+ real32 G_Layer = (real32)(*G_DestAddress & B.LayerBits.MaskPixel) * B.LayerBits.Normalized;
+ real32 B_Layer = (real32)(*B_DestAddress & B.LayerBits.MaskPixel) * B.LayerBits.Normalized;
+ real32 A_Layer = (real32)(*A_DestAddress & B.LayerBits.MaskPixel) * B.LayerBits.Normalized;
+
+ int32 TrueX = (X - B.LayerBounds.Min.x) - B.ExtraX;
+ int32 TrueY = (Y - B.LayerBounds.Min.y) - B.ExtraY;
+
+ uint8 *SourcePixel = (uint8 *)B.SourceBuffer + Offset;
+ Assert(B.SourceBytesPerPixel == 4);
+ bool32 IsEmpty = (*(uint32 *)SourcePixel == 0x00000000);
+ if (IsEmpty)
+ *(uint32 *)SourcePixel = 0x00010101;
+
+ // Assert(TrueX >= 0 && TrueX < BrushLength);
+ // Assert(TrueY >= 0 && TrueY < BrushLength);
+
+ uint8 *BrushPixel = (uint8 *)B.BrushRow + (TrueY * B.BrushPitch) + (TrueX * B.BytesPerPixel);
+
+ real32 Brush_BitmapAlpha = (real32)((*(uint32 *)(BrushPixel + B.BrushBits.ByteOffset*3)) & B.BrushBits.MaskPixel) * B.BrushBits.Normalized;
+
+ if (!Brush_BitmapAlpha)
+ continue;
+
+ real32 BrushAlpha = Brush_BitmapAlpha;
+ real32 LayerAlpha = A_Layer;
+
+ real32 A_Blend = 0;
+ real32 R_Blend = 0;
+ real32 G_Blend = 0;
+ real32 B_Blend = 0;
+
+ if (!B.EraseMode) {
+ if (IsEmpty) {
+ R_Blend = B.R_Brush;
+ G_Blend = B.G_Brush;
+ B_Blend = B.B_Brush;
+ A_Blend = LayerAlpha + BrushAlpha;
+ } else {
+ R_Blend = B.R_Brush;
+ G_Blend = B.G_Brush;
+ B_Blend = B.B_Brush;
+ A_Blend = LayerAlpha + ((1.0f - LayerAlpha) * BrushAlpha);
+ real32 Blend = BrushAlpha;
+ // R_Blend = (R_Layer * (1.0f - Blend)) + (B.R_Brush * Blend);
+ // G_Blend = (G_Layer * (1.0f - Blend)) + (B.G_Brush * Blend);
+ // B_Blend = (B_Layer * (1.0f - Blend)) + (B.B_Brush * Blend);
+ }
+ // A_Blend = BrushAlpha;
+ } else {
+ A_Blend = A_Layer * (1.0f - BrushAlpha);
+ R_Blend = R_Layer;
+ G_Blend = G_Layer;
+ B_Blend = B_Layer;
+ }
+
+ /*
+ R_Blend = (R_Brush * (1.0f - LayerAlpha)) + (R_Blend * LayerAlpha);
+ G_Blend = (G_Brush * (1.0f - LayerAlpha)) + (G_Blend * LayerAlpha);
+ B_Blend = (B_Brush * (1.0f - LayerAlpha)) + (B_Blend * LayerAlpha);
+ */
+
+ Assert(R_Blend <= 1.0f);
+
+ uint32 R_Out = (uint32)(Normalize(R_Blend) * B.LayerBits.Bits);
+ uint32 G_Out = (uint32)(Normalize(G_Blend) * B.LayerBits.Bits);
+ uint32 B_Out = (uint32)(Normalize(B_Blend) * B.LayerBits.Bits);
+ uint32 A_Out = (uint32)(Normalize(A_Blend) * B.LayerBits.Bits);
+
+ *R_DestAddress = (*R_DestAddress & ~B.LayerBits.MaskPixel) | R_Out;
+ *G_DestAddress = (*G_DestAddress & ~B.LayerBits.MaskPixel) | G_Out;
+ *B_DestAddress = (*B_DestAddress & ~B.LayerBits.MaskPixel) | B_Out;
+ *A_DestAddress = (*A_DestAddress & ~B.LayerBits.MaskPixel) | A_Out;
+ }
+ }
+}
+
+#if ARM
+#else
+static void
+PaintTest_AVX2(brush_info B, void *Buffer, rectangle RenderRegion)
+{
+ __m256 One = _mm256_set1_ps(1);
+ __m256 Zero = _mm256_set1_ps(0);
+ __m256 Eight = _mm256_set1_ps(8);
+ __m256i FF = _mm256_set1_epi32(0xFF);
+ __m256 R_Brush =_mm256_set1_ps(B.R_Brush);
+ __m256 G_Brush =_mm256_set1_ps(B.G_Brush);
+ __m256 B_Brush =_mm256_set1_ps(B.B_Brush);
+ __m256 A_Brush =_mm256_set1_ps(B.A_Brush);
+ __m256 Norm255 = _mm256_set1_ps(1/255.0f);
+ __m256 Real255 = _mm256_set1_ps(255.0f);
+ __m256 LayerBoundsMaxX = _mm256_set1_ps(B.SourceWidth);
+
+ for (int32 Y = RenderRegion.Min.y; Y < RenderRegion.Max.y; Y++) {
+ __m256 PixelX = _mm256_setr_ps((real32)RenderRegion.Min.x,
+ (real32)RenderRegion.Min.x+1,
+ (real32)RenderRegion.Min.x+2,
+ (real32)RenderRegion.Min.x+3,
+ (real32)RenderRegion.Min.x+4,
+ (real32)RenderRegion.Min.x+5,
+ (real32)RenderRegion.Min.x+6,
+ (real32)RenderRegion.Min.x+7);
+ for (int32 X = RenderRegion.Min.x; X < RenderRegion.Max.x; X += 8) {
+
+ __m256i TileBarrier = _mm256_cvtps_epi32(_mm256_cmp_ps(PixelX, LayerBoundsMaxX, 1));
+
+ uint32 Offset = Y*B.LayerPitch + X*B.SourceBytesPerPixel;
+ uint8 *LayerPixelAddress = (uint8 *)Buffer + Offset;
+
+ __m256i LayerPixels = _mm256_loadu_si256((const __m256i *)LayerPixelAddress);
+
+ __m256 R_Layer = _mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_and_si256( LayerPixels, FF)), Norm255);
+ __m256 G_Layer = _mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_and_si256(_mm256_srli_epi32(LayerPixels, 8), FF)), Norm255);
+ __m256 B_Layer = _mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_and_si256(_mm256_srli_epi32(LayerPixels, 16), FF)), Norm255);
+ __m256 A_Layer = _mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_and_si256(_mm256_srli_epi32(LayerPixels, 24), FF)), Norm255);
+
+ int32 TrueX = (X - B.LayerBounds.Min.x) - B.ExtraX;
+ int32 TrueY = (Y - B.LayerBounds.Min.y) - B.ExtraY;
+ uint8 *BrushPixelAddress = (uint8 *)B.BrushRow + (TrueY * B.BrushPitch) + (TrueX * B.BytesPerPixel);
+ __m256i BrushPixels = _mm256_loadu_si256((const __m256i *)BrushPixelAddress);
+
+ __m256 Brush_BitmapAlpha = _mm256_mul_ps(_mm256_cvtepi32_ps(_mm256_and_si256(_mm256_srli_epi32(BrushPixels, 24), FF)), Norm255);
+
+ __m256 A_BrushMultiplied = _mm256_mul_ps(Brush_BitmapAlpha, A_Brush);
+
+ __m256 A_Blend = _mm256_add_ps(A_Layer, A_BrushMultiplied);
+
+ __m256 R_Blend = _mm256_add_ps(_mm256_mul_ps(R_Layer, _mm256_sub_ps(One, A_BrushMultiplied)), _mm256_mul_ps(R_Brush, A_BrushMultiplied));
+ __m256 G_Blend = _mm256_add_ps(_mm256_mul_ps(G_Layer, _mm256_sub_ps(One, A_BrushMultiplied)), _mm256_mul_ps(G_Brush, A_BrushMultiplied));
+ __m256 B_Blend = _mm256_add_ps(_mm256_mul_ps(B_Layer, _mm256_sub_ps(One, A_BrushMultiplied)), _mm256_mul_ps(B_Brush, A_BrushMultiplied));
+
+ __m256i R_Out = _mm256_cvtps_epi32(_mm256_mul_ps(_mm256_max_ps(_mm256_min_ps(One, R_Blend), Zero), Real255));
+ __m256i G_Out = _mm256_cvtps_epi32(_mm256_mul_ps(_mm256_max_ps(_mm256_min_ps(One, G_Blend), Zero), Real255));
+ __m256i B_Out = _mm256_cvtps_epi32(_mm256_mul_ps(_mm256_max_ps(_mm256_min_ps(One, B_Blend), Zero), Real255));
+ __m256i A_Out = _mm256_cvtps_epi32(_mm256_mul_ps(_mm256_max_ps(_mm256_min_ps(One, A_Blend), Zero), 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 *)LayerPixelAddress, OutputPixel);
+ _mm256_maskstore_epi32((int *)LayerPixelAddress, TileBarrier, OutputPixel);
+ PixelX = _mm256_add_ps(PixelX, Eight);
+ }
+ }
+}
+#endif
+
+static void
+RenderQueue_AddBrush(project_state *State, v2 LayerPos)
+{
+ State->Queue.Item[State->Queue.CurrentIdx].Pos = LayerPos;
+ State->Queue.Item[State->Queue.CurrentIdx].Type = 1;
+ State->Queue.CurrentIdx++;
+ State->Brush.PrevPos = LayerPos;
+}
+
+static void
+RenderQueue_AddBlit(project_state *State)
+{
+ State->Queue.Item[State->Queue.CurrentIdx].Type = 2;
+ State->Queue.CurrentIdx++;
+}