diff options
Diffstat (limited to 'src/createcalls.cpp')
-rw-r--r-- | src/createcalls.cpp | 1168 |
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++; +} |