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, 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); 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) { return stbi_info(Path, NULL, NULL, NULL); // AV_IsFileSupported(Path, IsVideo) } 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; if (Source_IsFileSupported((char *)TempString, &IsVideo)) { 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); Assert(!IsVideo); Source->Type = source_type_file; 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 bezier_point * Bezier_LookupAddress(memory *Memory, property_channel *Property, uint16 Index, bool32 AssertExists) { Assert(Index < MAX_KEYFRAMES_PER_BLOCK); block_bezier *Bezier = (block_bezier *)Memory_Block_AddressAtIndex(Memory, F_Bezier, Property->Block_Bezier_Index[0], AssertExists); return &Bezier->Point[Index]; } // NOTE(fox): It's not required for the Y to be set correctly in i.e. sorting. static void Bezier_EvaluateValue(project_state *State, bezier_point *PointAddress, v2 *Pos, real32 GraphZoomHeight = 1, real32 Y_Increment = 1) { Pos[0] = PointAddress->Pos[0]; Pos[1] = PointAddress->Pos[1]; Pos[2] = PointAddress->Pos[2]; if (PointAddress->IsSelected) { if (State->Interact_Active == interact_type_keyframe_move) { Pos[PointAddress->IsSelected - 1].x += (int32)State->Interact_Offset[0]; if (State->Interact_Modifier != 1) Pos[PointAddress->IsSelected - 1].y -= (State->Interact_Offset[1] / GraphZoomHeight / Y_Increment); } else if (State->Interact_Active == interact_type_keyframe_scale) { Pos[1].x += State->Interact_Offset[0]; Pos[2].x -= State->Interact_Offset[0]; } else if (State->Interact_Active == interact_type_keyframe_rotate) { // how do I do this?? Assert(0); } } } static void Bezier_Add(memory *Memory, property_channel *Property, bezier_point PointData) { if (!Property->Block_Bezier_Count) { Property->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], 0); Bezier->Occupied = true; // NOTE(fox): Effects will change this! History_Action_Swap(Memory, F_Layers, sizeof(Property->Block_Bezier_Count), &Property->Block_Bezier_Count); Property->Block_Bezier_Count++; } int k = 0; for (;;) { bezier_point *Point = Bezier_LookupAddress(Memory, Property, k, 0); if (!Point->Occupied) { History_Action_Swap(Memory, F_Bezier, sizeof(*Point), Point); *Point = PointData; History_Action_Swap(Memory, F_Layers, sizeof(Property->Keyframe_Count), &Property->Keyframe_Count); Property->Keyframe_Count++; return; } k++; } } // static void // Property_InitFloat(char *Name, real32 Val, real32 ScrubVal, real32 MinVal = PROPERTY_REAL_MIN, real32 MaxVal = PROPERTY_REAL_MAX, bool32 AlwaysInteger = 0) { // { // } static property_channel Property_InitFloat(char *Name, real32 Val, real32 ScrubVal, real32 MinVal = PROPERTY_REAL_MIN, real32 MaxVal = PROPERTY_REAL_MAX, bool32 AlwaysInteger = 0) { property_channel Property = {}; Property.Name = Name; 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 layer_transforms Layer_GetTransforms(block_layer *Layer) { return { Layer->x.CurrentValue, Layer->y.CurrentValue, Layer->ax.CurrentValue, Layer->ay.CurrentValue, Layer->rotation.CurrentValue, Layer->scale.CurrentValue }; } static void Layer_Interact_Evaluate(memory *Memory, project_state *State, uint16 Layer_Index_Physical, sorted_comp_info SortedCompInfo, sorted_layer *SortedLayerInfo, 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; } } 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); Effect->Occupied = true; header_effect *EffectHeader = &State->Effect[EffectEntryIndex]; String_Copy(Effect->ID, EffectHeader->ID, 8); Effect->IsToggled = true; Effect->Index = EffectCount; 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); Property->Occupied = true; header_property PropertyHeader = State->Property[EffectHeader->PropertyStartIndex + e]; Property->Name = PropertyHeader.Name; Property->CurrentValue = PropertyHeader.DefaultValue; Property->MinVal = PropertyHeader.MinVal; Property->MaxVal = PropertyHeader.MaxVal; } return EffectAddressIndex; } static void Layer_UpdateMasksEffects(project_state *State, block_layer *Layer, memory *Memory, void *EffectBitmapAddress, int Width, int Height, int BytesPerPixel) { uint64 Size = Width*Height*BytesPerPixel; // We need two of these: one with multisampling enabled and a // non-multisampled one that we can blit to. gl_effect_layer TestL = {}; gl_effect_layer TestM = {}; GL_UpdateTexture(&TestL, EffectBitmapAddress, Width, Height, BytesPerPixel, 0); GL_UpdateTexture(&TestM, EffectBitmapAddress, Width, Height, BytesPerPixel, 1); for (int i = 0; i < Layer->Block_Effect_Count; i++) { block_effect Effect = *(block_effect *)Memory_Block_AddressAtIndex(Memory, F_Effects, Layer->Block_Effect_Index[i]); header_effect *EffectEntry = Effect_EntryFromID(State, Effect.ID); if (Effect.IsToggled) { real32 *Data = (real32 *)Memory_PushScratch(Memory, sizeof(real32) * 50); for (int c = 0; c < EffectEntry->Property_Count; c++) { property_channel *Property = (property_channel *)Memory_Block_AddressAtIndex(Memory, F_Properties, Effect.Block_Property_Index[c]); Data[c] = Property->CurrentValue; } EffectEntry->func(Data, Width, Height, BytesPerPixel, EffectBitmapAddress, EffectEntry->GLShaderIndex); Memory_PopScratch(Memory, sizeof(real32) * 50); } } /* if (Layer->NumberOfMasks) { for (int i = 0; i < Layer->NumberOfMasks; i++) { file_mask_header *MaskHeader = (file_mask_header *)((uint8 *)Layer + sizeof(file_layer) + MaskOffset); if (MaskHeader->IsClosed && MaskHeader->IsToggled) { mask_point *Point = (mask_point *)((uint8 *)MaskHeader + sizeof(file_mask_header)); Mask_TriangulateAndRasterize(TestM, TestL, Memory, MaskHeader, Point, Source->Width, Source->Height, Source->BytesPerPixel, EffectBitmapAddress); } } Bitmap_StencilAlpha(SourceBitmapAddress, EffectBitmapAddress, Source->BytesPerPixel, Size); } Layer->OutputBitmapLocation = EffectBitmapAddress; */ GL_DeleteHWBuffer(&TestL); GL_DeleteHWBuffer(&TestM); } static void Layer_ToggleChannel(project_data *File, memory *Memory, int32 a) { 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) Layer->Property[a].IsToggled ^= 1; } } static void Layer_Select(memory *Memory, project_state *State, int32 i) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); Layer->IsSelected = true; State->MostRecentlySelectedLayer = i; // State->RecentSelectionType = selection_layer; } void Layer_DeselectAll(project_data *File, project_state *State, memory *Memory) { 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); Layer->IsSelected = false; } State->MostRecentlySelectedLayer = -1; } 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; } } // NOTE(fox): This won't work with precomps! void Clipboard_Paste(project_data *File, project_state *State, memory *Memory, sorted_comp_info *SortedCompInfo, sorted_layer *SortedLayerInfo) { clipboard_contents *Contents = (clipboard_contents *)State->ClipboardBuffer; if (Contents->Type == selection_none) return; uint64 ClipboardPos = sizeof(clipboard_contents); ClipboardPos = sizeof(clipboard_contents); int i = SortedCompInfo->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 SortEntry = SortedLayerInfo[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! 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; Bezier_Add(Memory, Property, PointData); ClipboardPos += sizeof(bezier_point); } b++; Channel = &Contents->Channel[b]; } } Layer = NULL; if (b < Contents->ChannelCount) { if (NumberOfLayersFromClipboard != 1) break; else b = 0; } } } inline sorted_property_info * Property_GetSortedInfo(sorted_property_info *SortedPropertyInfo, int i, int h) { return SortedPropertyInfo + (i * 8) + h; } inline uint16 * Property_GetSortedArray(uint16 *SortedPropertyArray, int i, int h) { return SortedPropertyArray + (i * 8 * MAX_KEYFRAMES_PER_BLOCK) + (h * MAX_KEYFRAMES_PER_BLOCK); } static void Bezier_Commit(project_data *File, project_state *State, memory *Memory, uint16 *SortedPropertyArray) { 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); if (!Layer->IsSelected) continue; for (int h = 0; h < AmountOf(Layer->Property); h++) { uint16 *ArrayLocation = Property_GetSortedArray(SortedPropertyArray, i, h); property_channel *Property = &Layer->Property[h]; 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_EvaluateValue(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; } void Clipboard_Store(project_data *File, project_state *State, memory *Memory, sorted_comp_info *SortedCompInfo, sorted_layer *SortedLayerInfo, sorted_property_info *SortedPropertyInfo, uint16 *SortedPropertyArray) { int LocalOffset = 0; clipboard_contents *Contents = (clipboard_contents *)State->ClipboardBuffer; *Contents = {}; uint64 ClipboardPos = sizeof(clipboard_contents); for (int i = SortedCompInfo->LayerCount - 1; i >= 0; i--) { sorted_layer SortEntry = SortedLayerInfo[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_info *InfoLocation = Property_GetSortedInfo(SortedPropertyInfo, i, h); uint16 *ArrayLocation = Property_GetSortedArray(SortedPropertyArray, 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; Channel->Name = Property->Name; } } } } } static sorted_layer * Layer_GetSortedArray(sorted_layer *LayerArrayStart, sorted_comp_info *SortedCompStart, uint32 TargetComp) { uint32 LayerOffset = 0; int s = 0; while (s < TargetComp) { LayerOffset += SortedCompStart[s].LayerCount; s++; } return LayerArrayStart + LayerOffset; } int32 Layer_TestSelection(memory *Memory, project_state *State, ui *UI, sorted_comp_info *SortedCompArray, sorted_layer *SortedLayerArray, uint16 PrincipalIndex) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, PrincipalIndex); sorted_comp_info SortedCompInfo = SortedCompArray[PrincipalIndex]; sorted_layer *SortedLayerInfo = Layer_GetSortedArray(SortedLayerArray, SortedCompArray, PrincipalIndex); int32 LayerIndex = -1; // for (int i = 0; i < SortedCompInfo.LayerCount; i++) { for (int i = SortedCompInfo.LayerCount - 1; i >= 0; i--) { sorted_layer SortEntry = SortedLayerInfo[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); layer_transforms T = Layer_GetTransforms(Layer); v2 UV = T_CompUVToLayerUV(T, Comp->Width, Comp->Height, Source->Width, Source->Height, State->TempZoomRatio); if (UV.x <= 1.0f && UV.x >= 0.0f && UV.y <= 1.0f && UV.y >= 0.0f && !Layer->IsSelected) { LayerIndex = Index_Physical; break; } // if (Layer->IsPrecomp) { // Layer_RecursiveDeselect(Memory, SortedCompArray, SortedLayerArray, TargetIndex, Layer->Block_Source_Index); // } // if (Layer->Block_Composition_Index != TargetIndex) { // Layer->IsSelected = false; // } } return LayerIndex; } void Layer_RecursiveDeselect(memory *Memory, sorted_comp_info *SortedCompArray, sorted_layer *SortedLayerArray, uint16 TargetIndex, uint16 PrincipalIndex) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, PrincipalIndex); sorted_comp_info SortedCompInfo = SortedCompArray[PrincipalIndex]; sorted_layer *SortedLayerInfo = Layer_GetSortedArray(SortedLayerArray, SortedCompArray, PrincipalIndex); for (int i = 0; i < SortedCompInfo.LayerCount; i++) { sorted_layer SortEntry = SortedLayerInfo[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); if (Layer->IsPrecomp) { Layer_RecursiveDeselect(Memory, SortedCompArray, SortedLayerArray, TargetIndex, Layer->Block_Source_Index); } if (Layer->Block_Composition_Index != TargetIndex) { Layer->IsSelected = false; } } } 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_EvaluateValue(State, FirstPointAddress, FirstPointPos); *Min = FirstPointPos[0].x; v2 LastPointPos[3]; bezier_point *LastPointAddress = Bezier_LookupAddress(Memory, Property, ArrayLocation[Property->Keyframe_Count - 1]); Bezier_EvaluateValue(State, LastPointAddress, LastPointPos); *Max = LastPointPos[0].x; } void Property_MinMax_Y(memory *Memory, project_state *State, property_channel *Property, sorted_property_info *PropertyInfo, real32 *Min, real32 *Max, bool32 Evaluate = 1) { if (Evaluate) { v2 MinYPointPos[3]; bezier_point *MinYPointAddress = Bezier_LookupAddress(Memory, Property, PropertyInfo->MinYIndex); Bezier_EvaluateValue(State, MinYPointAddress, MinYPointPos); *Min = MinYPointPos[0].y; v2 MaxYPointPos[3]; bezier_point *MaxYPointAddress = Bezier_LookupAddress(Memory, Property, PropertyInfo->MaxYIndex); Bezier_EvaluateValue(State, MaxYPointAddress, MaxYPointPos); *Max = MaxYPointPos[0].y; } else { bezier_point *MinYPointAddress = Bezier_LookupAddress(Memory, Property, PropertyInfo->MinYIndex); *Min = MinYPointAddress->Pos[0].y; bezier_point *MaxYPointAddress = Bezier_LookupAddress(Memory, Property, PropertyInfo->MaxYIndex); *Max = MaxYPointAddress->Pos[0].y; } } // The sorting algorithm is straightforward: read every point, evaluate it if // it's currently being interacted with by the user, record index in a sorted // list, and repeat, shiftig the list as necessary. void Property_SortAll(memory *Memory, project_state *State, property_channel *Property, sorted_property_info *PropertyInfo, uint16 *PropertyArrayStart) { int h = 0, c = 0, i = 0; uint32 CurrentSortIndex = 0; real32 MinY = FLT_MAX; real32 MaxY = FLT_MIN; int32 Offset = (State->Interact_Active == interact_type_keyframe_move) ? (int32)State->Interact_Offset[0] : 0; while (Block_Loop(Memory, Property, Property->Keyframe_Count, &h, &c, &i)) { v2 PointPos[3]; bezier_point *PointAddress = Bezier_LookupAddress(Memory, Property, i); if (PointAddress->IsSelected) PropertyInfo->IsGraphSelected = true; Bezier_EvaluateValue(State, PointAddress, PointPos); if (MinY > PointAddress->Pos[0].y) { MinY = PointAddress->Pos[0].y; PropertyInfo->MinYIndex = i; } if (MaxY < PointAddress->Pos[0].y) { MaxY = PointAddress->Pos[0].y; PropertyInfo->MaxYIndex = i; } uint32 SortedIndex_Playhead = 0; while (SortedIndex_Playhead < CurrentSortIndex) { uint16 TestPointEntry = PropertyArrayStart[SortedIndex_Playhead]; v2 TestPointPos[3]; bezier_point *TestPointAddress = Bezier_LookupAddress(Memory, Property, TestPointEntry); Bezier_EvaluateValue(State, TestPointAddress, TestPointPos); if (PointPos[0].x < TestPointPos[0].x ) { break; } else { SortedIndex_Playhead++; } } if (SortedIndex_Playhead != CurrentSortIndex) { uint8 *Address_Start = (uint8 *)(PropertyArrayStart + SortedIndex_Playhead); uint8 *Address_End = (uint8 *)(PropertyArrayStart + CurrentSortIndex) - 1; Assert(CurrentSortIndex != Property->Keyframe_Count); Arbitrary_ShiftData(Address_Start, Address_End, sizeof(uint16), 1); } uint16 *PointEntry = PropertyArrayStart + SortedIndex_Playhead; *PointEntry = i; CurrentSortIndex++; } } void Property_DeselectAll(project_data *File, memory *Memory, uint16 *SortedPropertyArray) { 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; for (int h = 0; h < AmountOf(Layer->Property); h++) { property_channel *Property = &Layer->Property[h]; uint16 *ArrayLocation = SortedPropertyArray + (i * 7 * MAX_KEYFRAMES_PER_BLOCK) + (h * MAX_KEYFRAMES_PER_BLOCK); 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_Sort_CheckPrev(memory *Memory, int i, int Direction, sorted_layer *SortedLayerInfo, sorted_comp_info SortedCompInfo, int *EntriesPassed, sorted_layer *LayerEntry, bool32 AltMethod) { int PrevOffsetIndex = i + Direction + (*EntriesPassed * Direction); bool32 OutOfBounds = (Direction > 0) ? (PrevOffsetIndex > (SortedCompInfo.LayerCount - 1)) : (PrevOffsetIndex < 0); if (!OutOfBounds) { sorted_layer *PrevLayerEntry = &SortedLayerInfo[PrevOffsetIndex]; real32 PrevOffset = PrevLayerEntry->SortedOffset; if (PrevOffset == (LayerEntry->SortedOffset - Direction)) { (*EntriesPassed)++; if (!AltMethod) { PrevLayerEntry->SortedOffset += Direction; } else { LayerEntry->SortedOffset -= Direction; Layer_Sort_CheckPrev(Memory, i, Direction, SortedLayerInfo, SortedCompInfo, EntriesPassed, LayerEntry, AltMethod); } } } } void Layer_Evaluate_Display(block_layer *Layer, sorted_layer *LayerArrayStart, sorted_comp_info *CompStart, sorted_layer *SortedLayerInfo, int i, real32 *Offset) { int ExtraPad = 1; for (int h = 0; h < AmountOf(Layer->Property); h++) { property_channel *Property = &Layer->Property[h]; if (Property->IsToggled) { *Offset += 1 + ExtraPad; ExtraPad = 0; } } /* if (Layer->Precomp_Toggled) { Assert(Layer->IsPrecomp); sorted_comp_info *Layer_SortedCompInfo = &CompStart[Layer->Block_Source_Index]; sorted_layer *Layer_SortedLayerInfo = Layer_GetSortedArray(LayerArrayStart, CompStart, Layer->Block_Source_Index); sorted_layer *TopLayerEntry = &Layer_SortedLayerInfo[0]; sorted_layer *BottomLayerEntry = &Layer_SortedLayerInfo[Layer_SortedCompInfo->LayerCount - 1]; *Offset += TopLayerEntry->SortedOffset - BottomLayerEntry->SortedOffset + 2; } */ } void TempSource_SortAll(project_data *File, project_state *State, memory *Memory, uint16 *SourceArrayStart, uint16 *TempSourceCount) { int count = 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->Type == source_type_principal_temp) { uint32 Playhead = 0; while (Playhead < count) { block_source *TestSource = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, SourceArrayStart[Playhead]); Assert(TestSource->Type == source_type_principal_temp); if (TestSource->RelativeTimestamp > Source->RelativeTimestamp) { break; } else { Playhead++; } } if (Playhead != count) { uint8 *Address_Start = (uint8 *)(SourceArrayStart + Playhead); uint8 *Address_End = (uint8 *)(SourceArrayStart + count) - 1; Arbitrary_ShiftData(Address_Start, Address_End, sizeof(uint16), 1); } SourceArrayStart[Playhead] = i; count++; } } *TempSourceCount = count; } // The first loop is for counting how many layers are in each precomp, the // second is for sorting the layers by offset, and the third is for applying // interactivity if the user is moving any layers. void Layer_SortAll(project_data *File, project_state *State, memory *Memory, sorted_layer *LayerArrayStart, sorted_comp_info *CompStart, sorted_property_info *SortedPropertyInfo, uint16 *SortedPropertyArray, uint32 LayerCount, uint32 CompCount) { 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); Assert(Layer->Block_Composition_Index < CompCount); CompStart[Layer->Block_Composition_Index].LayerCount++; for (int h = 0; h < AmountOf(Layer->Property); h++) { property_channel *Property = &Layer->Property[h]; if (Property->Block_Bezier_Count) { sorted_property_info *InfoLocation = Property_GetSortedInfo(SortedPropertyInfo, i, h); uint16 *ArrayLocation = Property_GetSortedArray(SortedPropertyArray, i, h); Property_SortAll(Memory, State, Property, InfoLocation, ArrayLocation); int x = 0; } } } 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_comp_info *SortedCompInfo = &CompStart[Layer->Block_Composition_Index]; sorted_layer *SortedLayerInfo = Layer_GetSortedArray(LayerArrayStart, CompStart, Layer->Block_Composition_Index); uint32 SortedIndex_Playhead = 0; while (SortedIndex_Playhead < SortedCompInfo->CurrentSortIndex) { sorted_layer LayerEntry = SortedLayerInfo[SortedIndex_Playhead]; block_layer *TestLayer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, LayerEntry.Block_Layer_Index); if (-Layer->Vertical_Offset < -TestLayer->Vertical_Offset) { break; } else { SortedIndex_Playhead++; } } if (SortedIndex_Playhead != SortedCompInfo->CurrentSortIndex) { uint8 *Address_Start = (uint8 *)(SortedLayerInfo + SortedIndex_Playhead); uint8 *Address_End = (uint8 *)(SortedLayerInfo + SortedCompInfo->CurrentSortIndex) - 1; Assert(SortedCompInfo->CurrentSortIndex != SortedCompInfo->LayerCount); Arbitrary_ShiftData(Address_Start, Address_End, sizeof(sorted_layer), 1); } sorted_layer *LayerEntry = SortedLayerInfo + SortedIndex_Playhead; LayerEntry->Block_Layer_Index = i; LayerEntry->SortedOffset = Layer->Vertical_Offset; SortedCompInfo->CurrentSortIndex++; } if (State->Interact_Active == interact_type_layer_move) { int32 Offset = (int32)State->Interact_Offset[1]; bool32 Direction = (Offset > 0) ? 1 : -1; for (uint32 c = 0; c < CompCount; c++) { sorted_comp_info *SortedCompInfo = &CompStart[c]; if (!SortedCompInfo->LayerCount) continue; sorted_layer *SortedLayerInfo = Layer_GetSortedArray(LayerArrayStart, CompStart, c); int i = (Direction > 0) ? SortedCompInfo->LayerCount - 1 : 0; bool32 Case = 1; while (Case) { int32 EntriesPassed = 0; sorted_layer *LayerEntry = &SortedLayerInfo[i]; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, LayerEntry->Block_Layer_Index); Assert(LayerEntry->SortedOffset == Layer->Vertical_Offset); if (Layer->IsSelected) { int32 SpacesToMove = Offset * Direction; while (SpacesToMove) { Layer_Sort_CheckPrev(Memory, i, Direction, SortedLayerInfo, *SortedCompInfo, &EntriesPassed, LayerEntry, 0); LayerEntry->SortedOffset -= Direction; SpacesToMove--; } } int b = 0; while (b < EntriesPassed) { sorted_layer *FrontEntry = &SortedLayerInfo[i+(b*Direction)]; sorted_layer *BackEntry = &SortedLayerInfo[i+((b+1)*Direction)]; sorted_layer Swap = *FrontEntry; *FrontEntry = *BackEntry; *BackEntry = Swap; b++; } i -= Direction; Case = (Direction > 0) ? (i >= 0) : (i < SortedCompInfo->LayerCount); } } } } sorted_file File_Sort_Push(project_data *File, project_state *State, memory *Memory) { sorted_file Sorted = {0}; Sorted.Layer_SortSize = (sizeof(sorted_comp_info) * File->Comp_Count) + (sizeof(sorted_layer) * File->Layer_Count); void *Layer_SortedArray = Memory_PushScratch(Memory, Sorted.Layer_SortSize); Arbitrary_Zero((uint8 *)Layer_SortedArray, Sorted.Layer_SortSize); Sorted.CompArray = (sorted_comp_info *)Layer_SortedArray; Sorted.LayerArray = (sorted_layer *)((uint8 *)Layer_SortedArray + (sizeof(sorted_comp_info) * File->Comp_Count)); uint64 PropertyArraySize = sizeof(uint16) * 8 * MAX_KEYFRAMES_PER_BLOCK * File->Layer_Count; uint64 PropertyInfoSize = sizeof(sorted_property_info) * 8 * File->Layer_Count; Sorted.Property_SortSize = PropertyArraySize + PropertyInfoSize; void *Property_SortedArray = Memory_PushScratch(Memory, Sorted.Property_SortSize); Arbitrary_Zero((uint8 *)Property_SortedArray, Sorted.Property_SortSize); Sorted.PropertyInfo = (sorted_property_info *)Property_SortedArray; Sorted.PropertyArray = (uint16 *)((uint8 *)Property_SortedArray + PropertyInfoSize); uint64 SourceArraySize = sizeof(uint16) * File->Source_Count; Sorted.Source_SortSize = SourceArraySize; void *Source_SortedArray = Memory_PushScratch(Memory, Sorted.Source_SortSize); Arbitrary_Zero((uint8 *)Source_SortedArray, Sorted.Source_SortSize); Sorted.SourceArray = (uint16 *)Source_SortedArray; TempSource_SortAll(File, State, Memory, Sorted.SourceArray, &Sorted.TempSourceCount); Layer_SortAll(File, State, Memory, Sorted.LayerArray, Sorted.CompArray, Sorted.PropertyInfo, Sorted.PropertyArray, File->Layer_Count, File->Comp_Count); return Sorted; } void File_Sort_Pop(memory *Memory, uint64 Layer_SortSize, uint64 Property_SortSize, uint64 Source_SortSize) { Memory_PopScratch(Memory, Source_SortSize); Memory_PopScratch(Memory, Property_SortSize); Memory_PopScratch(Memory, Layer_SortSize); } static void Layer_Delete(project_data *File, memory *Memory, uint32 Index) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index); History_Action_Block_Swap(Memory, F_Layers, Layer); Layer->Occupied = 0; History_Action_Swap(Memory, F_File, sizeof(File->Layer_Count), &File->Layer_Count); File->Layer_Count--; } block_layer * Layer_Init(project_data *File, memory *Memory) { if (File->Layer_Count + 1 > MAX_LAYERS) { Assert(0); } block_layer *Layer = (block_layer *)Memory_Block_AllocateAddress(Memory, F_Layers); History_Action_Block_Swap(Memory, F_Layers, Layer); *Layer = {}; Layer->Occupied = 1; Layer->Block_String_Index = Memory_Block_AllocateNew(Memory, F_Strings); block_string *String = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Layer->Block_String_Index, 0); sprintf(String->Char, "Layer %i", File->Layer_Count + 1); // CSbros... String->Occupied = 1; Layer->x = Property_InitFloat("X Position", 0.0f, 1.0f); Layer->y = Property_InitFloat("Y Position", 0.0f, 1.0f); Layer->ax = Property_InitFloat("Anchor X", 0.5f, 0.005f); Layer->ay = Property_InitFloat("Anchor Y", 0.5f, 0.005f); Layer->scale = Property_InitFloat("Scale", 1.0f, 0.005f); Layer->rotation = Property_InitFloat("Rotation", 0.0f, 1.0f); Layer->opacity = Property_InitFloat("Opacity", 1.0f, 0.005f, 0.0f, 1.0f); Layer->time = Property_InitFloat("Frame Number", 0.0f, 1.0f, 0, 100000, 1); Layer->IsVisible = 1; History_Action_Swap(Memory, F_File, sizeof(File->Layer_Count), &File->Layer_Count); File->Layer_Count++; return Layer; } void Source_UICreateButton(project_data *File, project_state *State, memory *Memory, sorted_comp_info *SortedCompArray, sorted_layer *SortedLayerArray) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex); sorted_comp_info *SortedCompInfo = &SortedCompArray[File->PrincipalCompIndex]; int32 TopOffset; if (!File->Layer_Count) { TopOffset = 11; } else { sorted_layer *SortedLayerInfo = Layer_GetSortedArray(SortedLayerArray, SortedCompArray, File->PrincipalCompIndex); block_layer *TopLayer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, SortedLayerInfo[SortedCompInfo->LayerCount - 1].Block_Layer_Index); TopOffset = TopLayer->Vertical_Offset; } 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; TopOffset--; } 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_info SortedCompInfo, sorted_layer *SortedLayerInfo) { block_layer *Layer = NULL; for (int i = SortedCompInfo.LayerCount - 1; i >= 0; i--) { sorted_layer SortEntry = SortedLayerInfo[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_UICreateButton(project_data *File, project_state *State, memory *Memory, uint16 CompIndex, sorted_comp_info SortedCompInfo, sorted_layer *SortedLayerInfo) { 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 = SortedCompInfo.LayerCount - 1; i >= 0; i--) { sorted_layer SortEntry = SortedLayerInfo[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 = SortedCompInfo.LayerCount - 1; i >= 0; i--) { sorted_layer SortEntry = SortedLayerInfo[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, &PrecompLayer->time, Point0); Bezier_Add(Memory, &PrecompLayer->time, Point1); 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); *R_DestAddress = (*R_DestAddress & ~Byte.MaskPixel) | Byte.Bits; // brush preview is red *A_DestAddress = (*A_DestAddress & ~Byte.MaskPixel) | (uint32)((1.0f - Gradient)*Byte.Bits); } } } static void Brush_Info(brush_info *B, brush_state *Brush, block_source *Source, 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; B->BytesPerPixel = 4; B->SourceWidth = Source->Width; B->SourceBytesPerPixel = Source->BytesPerPixel; 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; // ImGui's color picker works in sRGB, so we need to convert to linear. // real32 R_Brush = (Color.r >= 0.04045) ? pow((Color.r + 0.055) / (1 + 0.055), 2.4) : Color.r / 12.92; // real32 G_Brush = (Color.g >= 0.04045) ? pow((Color.g + 0.055) / (1 + 0.055), 2.4) : Color.g / 12.92; // real32 B_Brush = (Color.b >= 0.04045) ? pow((Color.b + 0.055) / (1 + 0.055), 2.4) : Color.b / 12.92; // real32 A_Brush = (Color.a >= 0.04045) ? pow((Color.a + 0.055) / (1 + 0.055), 2.4) : Color.a / 12.92; B->R_Brush = Color.r; B->G_Brush = Color.g; B->B_Brush = Color.b; B->A_Brush = Color.a; 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 == 8); #if ARM Assert(InstructionMode != instruction_mode_neon); #else if (BytesPerPixel == 4) { uint32 Temp = 0; while (i < Size) { uint32 *Pixel_0 = (uint32 *)(Address_0 + i); uint32 *Pixel_1 = (uint32 *)(Address_1 + i); if (*Pixel_0 != 0x00000000) { Temp = *Pixel_1; *Pixel_1 = *Pixel_0; *Pixel_0 = Temp; } i += sizeof(uint32); } } else if (BytesPerPixel == 8) { uint64 Temp = 0; __m256i Zero = _mm256_set1_epi64x(0); __m256i Max32 = _mm256_set1_epi32(0xFFFFFFFF); if (InstructionMode == instruction_mode_avx) { while (i < Size - RemainderBytes) { uint8 *Pixel_0_Address = (Address_0 + i); __m256i Pixel_0 = _mm256_loadu_si256((__m256i *)Pixel_0_Address); __m256i AlphaMask = _mm256_cmpeq_epi64(Pixel_0, Zero); if (_mm256_movemask_epi8(AlphaMask) != 0xFFFFFFFF) { uint8 *Pixel_1_Address = (Address_1 + i); __m256i Pixel_1 = _mm256_loadu_si256((__m256i *)Pixel_1_Address); AlphaMask = _mm256_andnot_si256(AlphaMask, Max32); _mm256_maskstore_epi64((long long *)Pixel_1_Address, AlphaMask, Pixel_0); _mm256_maskstore_epi64((long long *)Pixel_0_Address, AlphaMask, Pixel_1); } i += sizeof(uint64)*4; } } while (i < Size) { uint64 *Pixel_0 = (uint64 *)(Address_0 + i); uint64 *Pixel_1 = (uint64 *)(Address_1 + i); if (*Pixel_0 != 0x0000000000000000) { Temp = *Pixel_1; *Pixel_1 = *Pixel_0; *Pixel_0 = Temp; } i += sizeof(uint64); } } else { Assert(0); } #endif } static void PaintTest(brush_info B, void *Buffer, 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 *)Buffer + 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; // 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; real32 BrushAlpha = Brush_BitmapAlpha * B.A_Brush; real32 LayerAlpha = A_Layer; real32 A_Blend = 0; real32 R_Blend = 0; real32 G_Blend = 0; real32 B_Blend = 0; if (!B.EraseMode) { A_Blend = LayerAlpha + BrushAlpha; // R_Blend = (R_Layer * (1.0f - BrushAlpha)) + (B.R_Brush * BrushAlpha); // G_Blend = (G_Layer * (1.0f - BrushAlpha)) + (B.G_Brush * BrushAlpha); // B_Blend = (B_Layer * (1.0f - BrushAlpha)) + (B.B_Brush * BrushAlpha); R_Blend = B.R_Brush; G_Blend = B.G_Brush; B_Blend = B.B_Brush; } 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 Brush_Render(project_state *State, ui *UI, layer_transforms T_Layer, block_composition *MainComp, block_source *Source, void *BitmapAddress, ImVec2 ViewportMin, ImVec2 MousePos) { brush_info B; v2 LayerPos = Transform_ScreenSpaceToLocal(T_Layer, MainComp->Width, MainComp->Height, Source->Width, Source->Height, UI->CompPos, UI->CompZoom, ViewportMin, MousePos); Brush_Info(&B, &State->Brush, Source, LayerPos, UI->Color); if (State->Brush.Size >= 128) { Render_Main((void *)&B, BitmapAddress, render_type_brush, B.LayerBounds); } else { PaintTest(B, BitmapAddress, B.LayerBounds); } } #if 0 static void IncrementFrame(project_data *File, int16 Amount) { if ((File->CurrentFrame <= 0 && Amount < File->StartFrame) || (File->CurrentFrame >= File->EndFrame)) { File->CurrentFrame = 0; } else { File->CurrentFrame += Amount; } } static void CreateKeyframeBlock(property_channel *Property, memory *Memory) { int16 a = Property->NumberOfKeyframeBlocks++; Assert(a < MAX_KEYFRAME_BLOCKS); Property->KeyframeBlock[a] = (keyframe_block *)AllocateMemory(Memory, sizeof(keyframe_block), F_Keyframes); } // Note we use total bytes here so we can use this memory for both packed and unpacked bitmaps. void * Layer_AllocateBitmap(memory *Memory, uint16 Width, uint16 Height, uint16 BytesPerPixel) { uint64 TotalBytes = Bitmap_CalcTotalBytes(Width, Height, BytesPerPixel); void *Address = AllocateMemory(Memory, TotalBytes, B_LayerBitmaps); return Address; } static cached_bitmap * Cache_CheckBitmap(source *Source, layer_bitmap_info *BitmapInfo, memory *Memory, int32 TimelineFrame) { if (!Source || !BitmapInfo) return 0; memory_table *Table = &Memory->Slot[B_LoadedBitmaps]; int32 FrameToSeek = TimelineFrame - BitmapInfo->FrameOffset; if (FrameToSeek < 0) { FrameToSeek = 0; } // Stills have a frame index of one. if (Source->SourceType == source_type_image) { FrameToSeek = 0; } for (int i = 0; i < Table->NumberOfPointers; i++) { cached_bitmap *Bitmap = &Memory->Bitmap[i]; if (Bitmap->Frame == FrameToSeek && Bitmap->SourceOwner == Source) { return Bitmap; } } return 0; } static cached_bitmap * STB_LoadStill(source *Source, layer_bitmap_info *BitmapInfo, memory *Memory) { int n = 0; int h, w; void *temp = stbi_load(Source->Path, &w, &h, &n, 4); Assert(temp); cached_bitmap *Bitmap = Memory_RollingBitmap(Memory, Source, 0); // Note the use of Unpacked since we haven't packed the bitmap and don't have any padding. uint64 Size = Bitmap_CalcUnpackedBytes(Source->Info.Width, Source->Info.Height, Source->Info.BytesPerPixel); Bitmap_CopyToPointer(temp, Bitmap->Data, Source->Info.BytesPerPixel, Size); return Bitmap; } static void Layer_InitSource(project_layer *Layer, source *Source, memory *Memory) { uint16 Width = Source->Info.Width; uint16 Height = Source->Info.Height; uint16 BytesPerPixel = Source->Info.BytesPerPixel; Layer->BitmapInfo.BitmapBuffer = Layer_AllocateBitmap(Memory, Width, Height, BytesPerPixel); } static void Layer_PositionCenter(project_layer *Layer, uint16 Width, uint16 Height) { Layer->x.CurrentValue.f = Width/2; Layer->y.CurrentValue.f = Height/2; } static void Layer_CreateFromSource(project_data *File, project_state *State, memory *Memory, source *Source) { if (File->NumberOfLayers + 1 > MAX_LAYERS) { // TODO(fox): Output! } project_layer *Layer = Layer_Init(File, Memory); Layer->Source = Source; State->UpdateFrame = true; } static void Layer_UpdateBitmap(project_data *File, project_layer *Layer, memory *Memory, int32 CurrentFrame) { source *Source = Layer->Source; layer_bitmap_info *BitmapInfo = &Layer->BitmapInfo; // AVInfo is created here instead of on layer creation so we can save // having to keep some state in the undo/delete commands. if (!BitmapInfo->AVInfo) { BitmapInfo->AVInfo = AllocateMemory(Memory, sizeof(av_info), P_AVInfo); AV_Init(Source, (av_info *)BitmapInfo->AVInfo, Memory); Layer_InitSource(Layer, Source, Memory); Layer_PositionCenter(Layer, File->Width, File->Height); } cached_bitmap *Bitmap = Cache_CheckBitmap(Source, BitmapInfo, Memory, CurrentFrame); if (!Bitmap) { if (Source->SourceType == source_type_image) { Bitmap = STB_LoadStill(Source, BitmapInfo, Memory); } else { Bitmap = AV_LoadVideoFrame(Source, BitmapInfo, Memory, CurrentFrame); } } uint16 Width = Source->Info.Width; uint16 Height = Source->Info.Height; uint16 BytesPerPixel = Source->Info.BytesPerPixel; void *DestBuffer = BitmapInfo->BitmapBuffer; uint64 UnpackedSize = Bitmap_CalcUnpackedBytes(Source->Info.Width, Source->Info.Height, Source->Info.BytesPerPixel); #if PACKEDRGB uint64 PackedSize = Bitmap_CalcTotalBytes(Source->Info.Width, Source->Info.Height, Source->Info.BytesPerPixel); if (Layer->NumberOfMasks == 0 && Layer->NumberOfEffects == 0) { Bitmap_ConvertPacking(Bitmap->Data, Memory->Scratch, Width, Height, BytesPerPixel, 0); Bitmap_CopyToPointer(Memory->Scratch, DestBuffer, BytesPerPixel, PackedSize); } else { Bitmap_CopyToPointer(Bitmap->Data, DestBuffer, BytesPerPixel, UnpackedSize); // GL_InitTexture(&BitmapInfo->Test, DestBuffer, Width, Height); // GL_MaskTexture(&BitmapInfo->TestM, DestBuffer, Width, Height); if (Layer->NumberOfMasks) { for (int i = 0; i < Layer->NumberOfMasks; i++) { mask *Mask = &Layer->Mask[i]; if (Mask->IsClosed) Mask_TriangulateAndRasterize(Memory, Layer, Mask); } } for (int i = 0; i < Layer->NumberOfEffects; i++) { if (Layer->Effect[i]->IsActive) Layer->Effect[i]->func(Source, BitmapInfo, Memory, Layer->Effect[i]->Property); } Bitmap_ConvertPacking(DestBuffer, Memory->Scratch, Width, Height, BytesPerPixel, 0); Bitmap_CopyToPointer(Memory->Scratch, DestBuffer, BytesPerPixel, PackedSize); } #else Bitmap_CopyToPointer(Bitmap->Data, DestBuffer, BytesPerPixel, UnpackedSize); GL_UpdateTexture(&BitmapInfo->Test, DestBuffer, Width, Height, 0); GL_UpdateTexture(&BitmapInfo->TestM, DestBuffer, Width, Height, 1); if (Layer->NumberOfMasks) { for (int i = 0; i < Layer->NumberOfMasks; i++) { mask *Mask = &Layer->Mask[i]; if (Mask->IsClosed) Mask_TriangulateAndRasterize(Memory, Layer, Mask); } Bitmap_StencilAlpha(Bitmap->Data, DestBuffer, BytesPerPixel, UnpackedSize); } if (Layer->NumberOfEffects) { for (int i = 0; i < Layer->NumberOfEffects; i++) { if (Layer->Effect[i]->IsActive) Layer->Effect[i]->func(Source, BitmapInfo, Memory, Layer->Effect[i]->Property); } } #endif } static ImVec2 Layer_LocalToScreenSpace(project_layer *Layer, ui *UI, comp_buffer CompBuffer, v2 Point) { real32 Rad = (Layer->rotation.CurrentValue.f * (PI / 180)); real32 s = Layer->scale.CurrentValue.f; real32 AX = Layer->ax.CurrentValue.f; real32 AY = Layer->ay.CurrentValue.f; source *Source = Layer->Source; v2 XAxis = (Point.x - AX*Source->Info.Width) * s * V2(cos(Rad), sin(Rad)); v2 YAxis = (Point.y - AY*Source->Info.Height) * -s * V2(sin(Rad), -cos(Rad)); v2 LocalPoint = XAxis + YAxis; v2 CompUV = V2((Layer->x.CurrentValue.f + LocalPoint.x) / CompBuffer.Width, (Layer->y.CurrentValue.f + LocalPoint.y) / CompBuffer.Height); v2 ScreenPoint = V2(UI->CompPos.x + CompUV.x * UI->CompZoom.x, UI->CompPos.y + CompUV.y * UI->CompZoom.y); return ImVec2(ScreenPoint.x, ScreenPoint.y); } static v2 Layer_ScreenSpaceToLocal(project_layer *Layer, ImVec2 CompPos, ImVec2 CompZoom, comp_buffer CompBuffer, ImVec2 ViewportMin, ImVec2 Point) { source *Source = Layer->Source; v2 CompUV = ImGui_ScreenPointToCompUV(ViewportMin, CompPos, CompZoom, Point); v2 LayerUV = CompUVToLayerUV(Layer, &CompBuffer, CompUV); return V2(LayerUV.x * Source->Info.Width, LayerUV.y * Source->Info.Height); } static void LoadTestFootage(project_data *File, project_state *State, memory *Memory) { void *SourceString = String_GenerateFromChar(Memory, "../asset/a.jpg"); Source_Generate(File, State, Memory, SourceString); SourceString = String_GenerateFromChar(Memory, "../asset/24.mp4"); Source_Generate(File, State, Memory, SourceString); SourceString = String_GenerateFromChar(Memory, "../asset/b.jpg"); Source_Generate(File, State, Memory, SourceString); SourceString = String_GenerateFromChar(Memory, "../asset/c.jpg"); Source_Generate(File, State, Memory, SourceString); SourceString = String_GenerateFromChar(Memory, "../asset/p.mp4"); Source_Generate(File, State, Memory, SourceString); Layer_CreateFromSource(File, State, Memory, &File->Source[0]); Keyframe_Insert(&File->Layer[0]->x, Memory, 01, -10); Keyframe_Insert(&File->Layer[0]->x, Memory, 10, -5); Keyframe_Insert(&File->Layer[0]->x, Memory, 23, 0); Keyframe_Insert(&File->Layer[0]->x, Memory, 34, 5); for (int i = 0; i < 5; i++) { keyframe *Keyframe = KeyframeLookup(&File->Layer[0]->x, i); Keyframe->TangentLeft = V2(-3, 0); Keyframe->TangentRight = V2(1.5, 0); Keyframe->Type = bezier; } File->Layer[0]->x.IsToggled = true; SelectLayer(File->Layer[0], State, 0); // AddEffect(File->Layer[0], Memory, 1); // property_channel *Property = &File->Layer[0]->x; // for (int i = 0; i < 16; i++) // Keyframe_Insert(Property, Memory, i*2, i*2*100); // Keyframe_Insert(Property, Memory, 1, 100); // Keyframe_Insert(Property, Memory, 15, 1500); // Keyframe_Insert(Property, Memory, 16, 1600); // Keyframe_Insert(Property, Memory, 31, 3100); // Keyframe_Delete(Property, Memory, 1); // History_Undo(Memory); // History_Redo(Memory); // Property->IsToggled = true; /* mask *Mask = &File->Layer[0]->Mask[0]; File->Layer[0]->NumberOfMasks = 1; Mask->Point[0].Pos = V2(200, 200); Mask->Point[1].Pos = V2(210, 400); Mask->Point[2].Pos = V2(220, 520); Mask->Point[3].Pos = V2(1380, 520); Mask->Point[4].Pos = V2(1480, 200); Mask->Point[0].TangentLeft = V2(-50, 0); Mask->Point[1].TangentLeft = V2(-50, 0); Mask->Point[2].TangentLeft = V2(-50, 0); Mask->Point[3].TangentLeft = V2(-50, 0); Mask->Point[4].TangentLeft = V2(-50, 0); Mask->Point[0].TangentRight = V2(50, 0); Mask->Point[1].TangentRight = V2(50, 0); Mask->Point[2].TangentRight = V2(50, 0); Mask->Point[3].TangentRight = V2(50, 0); Mask->Point[4].TangentRight = V2(50, 0); Mask->Point[0].HandleBezier = true; Mask->Point[1].HandleBezier = true; Mask->Point[2].HandleBezier = true; Mask->Point[3].HandleBezier = true; Mask->Point[4].HandleBezier = true; Mask->NumberOfPoints = 5; Mask->IsClosed = true; */ /* Mask_DeletePoint(Memory, Mask, 1); History_Undo(Memory); // History_Redo(Memory); Mask_AddPointToCurve(Memory, Mask, 1, 0.5); History_Undo(Memory); History_Redo(Memory); */ // if (!Source_Generate(File, Memory, "../asset/test.png")) // PostMsg(State, "File open fail..."); // if (!Source_Generate(File, Memory, "../asset/debug.png")) // PostMsg(State, "File open fail..."); // property_channel *Property = &File->Layer[0]->x; // Keyframe_Insert(Property, Memory, 1, 500); // Keyframe_Insert(Property, Memory, 30, 800); // Keyframe_Insert(Property, Memory, 15, 400); // Keyframe_Insert(Property, Memory, 20, 100); // Property->IsToggled = true; // Property->IsGraphToggled = true; // Property->GraphLength = 150; // Property->GraphYOffset = (Property->GraphWindowHeight - Property->GraphLength)/2; // Layer_CreateFromSource(File, State, Memory, Source); // if (!Source_Generate(File, Memory, "../asset/p.mp4")) // PostMsg(State, "File open fail..."); // source *Source2 = &File->Source[1]; // project_layer *Layer2 = Layer_Init(File, Memory); // AV_Init(Source2, &Layer2->BitmapInfo, Memory); // Layer_InitSource(Layer2, Source2, Memory); // Layer2->StartFrame = 11; // Layer2->EndFrame = 23; // void *SourceString1 = String_GenerateFromChar(Memory, "../asset/b.jpg"); // if (!Source_Generate(File, Memory, SourceString1)) // PostMsg(State, "File open fail..."); // source *Source1 = &File->Source[1]; // for (int i = 0; i < 25; i++) // Layer_CreateFromSource(File, State, Memory, Source1); // project_layer *Layer2 = Layer_Init(File, Memory); // Layer_InitSource(Layer2, Source2, Memory); // AddEffect(File->Layer[0], Memory, 2); // project_layer *Layer1 = CreateDebugLayer(&File, &Memory, 9, 14); // project_layer *Layer1 = CreateSolidLayer(&File, &Memory, 9, 13, V4(1.0, 1.0, 1.0, 1.0)); // Layer1->x.CurrentValue.f = 7; // Layer1->y.CurrentValue.f = 4; // Layer1->StartFrame = 0; // Layer1->EndFrame = File.EndFrame; } static void CreateDemoScene(project_data *File, project_state *State, memory *Memory) { Layer_CreateFromSource(File, State, Memory, &File->Source[1]); project_layer *Layer1 = File->Layer[0]; Layer1->x.CurrentValue.f = 1920/2; Layer1->y.CurrentValue.f = 1080/2; Layer1->StartFrame = 0; Layer1->EndFrame = File->EndFrame; Layer_CreateFromSource(File, State, Memory, &File->Source[2]); project_layer *Layer2 = File->Layer[1]; Layer2->x.CurrentValue.f = 1920/2; Layer2->y.CurrentValue.f = 1080/2; Layer2->StartFrame = 0; Layer2->EndFrame = File->EndFrame; Keyframe_Insert(&Layer2->rotation, Memory, 2, 0); Keyframe_Insert(&Layer2->rotation, Memory, 50, 360); Layer2->rotation.IsToggled = true; Layer2->scale.IsToggled = true; Layer_CreateFromSource(File, State, Memory, &File->Source[3]); project_layer *Layer3 = File->Layer[2]; Layer3->x.CurrentValue.f = 1920/4; Layer3->y.CurrentValue.f = 1080/4; Layer3->opacity.CurrentValue.f = 0.5f; Layer3->StartFrame = 0; Layer3->EndFrame = File->EndFrame; Keyframe_Insert(&Layer3->x, Memory, 2, Layer3->x.CurrentValue.f); Keyframe_Insert(&Layer3->x, Memory, 30, Layer3->x.CurrentValue.f+(1280/2)); Keyframe_Insert(&Layer3->x, Memory, 60, Layer3->x.CurrentValue.f+(1280/3)); Layer3->x.IsToggled = true; Layer3->y.IsToggled = true; } #endif