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 bool32 Source_Generate(project_data *File, memory *Memory, void *Path) { Assert(File->NumberOfSources < MAX_SOURCES); source *Source = &File->Source[File->NumberOfSources]; bool32 Found = 0; /* // TODO(fox): Unbreak stbi! int w, h; if (stbi_info(Path, &w, &h, NULL)) { Source->SourceType = source_type_image; Found = true; } */ // TODO(fox): This cast won't work above STRING_MAX. if (!Found && AV_IsFileSupported((char *)Path)) { Source->SourceType = source_type_video; Found = true; } if (Found) { Action_Entry_Begin(Memory, action_entry_default, "Add source"); Action_Change_Commit(Memory, &Source->Path, &Source->Path, &Path, action_change_ptr); uint32 i = File->NumberOfSources + 1; Action_Change_Commit(Memory, &File->NumberOfSources, &File->NumberOfSources, &i, action_change_u16); Action_Entry_End(Memory); return 1; } else { // PostMsg(State, "File open fail..."); } return 0; } /* static pixel_buffer LoadImage(memory *Memory, char *filename) { pixel_buffer Buffer = {}; Buffer.BytesPerPixel = 4; int n = 0; int h, w; void *temp = stbi_load(filename, &w, &h, &n, 4); // printf("%s", stbi_failure_reason()); Buffer.Height = h; Buffer.Width = w; CalculateFull(&Buffer); Buffer.Pitch = Buffer.FullWidth*Buffer.BytesPerPixel; // TODO(fox): Implement custom malloc in stbi so we don't have to do this. Buffer.OriginalBuffer = MoveImportToBitmap(Memory, &Buffer, temp); stbi_image_free(temp); Buffer.EffectBuffer = AllocateMemory(Memory, Buffer.FullWidth * Buffer.FullHeight * Buffer.BytesPerPixel, B_Scratch); BitmapPackRGB(&Buffer); Buffer.ToUpdate = true; return Buffer; } */ static property_channel InitFloatProperty(char *Name, real32 Val, real32 ScrubVal, real32 MinVal = PROPERTY_REAL_MIN, real32 MaxVal = PROPERTY_REAL_MAX) { property_channel Property = {}; Property.Name = Name; Property.CurrentValue.f = Val; Property.MinVal.f = MinVal; Property.MaxVal.f = MaxVal; Property.ScrubVal.f = ScrubVal; Property.VarType = type_real; Property.GraphWindowHeight = 300; return Property; } 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); } static void PostMsg(project_state *State, char *msg) { State->MsgTime = 120; State->Msg = msg; } /* static project_layer * CreateSolidLayer(project_data *File, memory *Memory, uint16 Width, uint16 Height, v4 Col) { project_layer *Layer = CreateLayer(File, Memory); Layer->RenderInfo = AllocateMemory(Memory, sizeof(source_image), P_SourceData); source_image *Source = (source_image *)Layer->RenderInfo; Source->Raster = CreateSolidBitmap(Memory, Width, Height, Col); Layer->SourceType = source_type_image; return Layer; } static project_layer * CreateDebugLayer(project_data *File, memory *Memory, uint16 Width, uint16 Height, int i) { project_layer *Layer = CreateLayer(File, Memory); Layer->RenderInfo = AllocateMemory(Memory, sizeof(source_image), P_SourceData); source_image *Source = (source_image *)Layer->RenderInfo; Source->Raster = CreateDebugBitmap(Memory, Width, Height); Layer->SourceType = source_type_image; return Layer; } */ // 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; } project_layer * Layer_Init(project_data *File, memory *Memory) { int16 Index = File->NumberOfLayers; int16 NextIndex = File->NumberOfLayers + 1; // NOTE(fox): We don't have to record any values that get set here aside // from this index in the Action tree since all we need to do to "delete" // the layer is to unset this. The layer that gets made here is always at // the top of the index. Action_Entry_Begin(Memory, action_entry_layerinit, "Create layer"); Action_Change_Commit(Memory, &File->NumberOfLayers, &Index, &NextIndex, action_change_u16); File->Layer[Index] = (project_layer *)AllocateMemory(Memory, sizeof(project_layer), F_Layers); project_layer *Layer = File->Layer[Index]; Action_Entry_SetPointer(Memory, &Layer->BitmapInfo.AVInfo); Action_Entry_End(Memory); Layer->Name = (char *)AllocateMemory(Memory, STRING_SIZE, F_Strings); sprintf(Layer->Name, "Layer %i", NextIndex); // CSbros... Layer->x = InitFloatProperty("X Position", 0.0f, 1.0f); Layer->y = InitFloatProperty("Y Position", 0.0f, 1.0f); Layer->ax = InitFloatProperty("Anchor X", 0.5f, 0.005f); Layer->ay = InitFloatProperty("Anchor Y", 0.5f, 0.005f); Layer->scale = InitFloatProperty("Scale", 1.0f, 0.005f); Layer->rotation = InitFloatProperty("Rotation", 0.0f, 1.0f); Layer->opacity = InitFloatProperty("Opacity", 1.0f, 0.005f, 0.0f, 1.0f); Layer->time = InitFloatProperty("Frame Number", 0.0f, 1.0f, 0, 100000); Layer->EndFrame = File->NumberOfFrames; return Layer; } 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; // Stills have a frame index of zero. if (FrameToSeek < 0 || 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 Mask_RasterizePoints(mask *Mask) { Mask->NumberOfVerts = 0; for (int i = 0; i < Mask->NumberOfPoints; i++) { mask_point Point0 = Mask->Point[i]; mask_point Point1 = Mask->Point[i+1]; if (i+1 == Mask->NumberOfPoints) Point1 = Mask->Point[0]; if (Point0.HandleBezier || Point1.HandleBezier) { Bezier_CubicCalcPoints(Point0.Pos, Point0.Pos + Point0.TangentRight, Point1.Pos + Point1.TangentLeft, Point1.Pos, Mask->TriangulatedPointCache, &Mask->NumberOfVerts); } else { real32 *Data = (real32 *)Mask->TriangulatedPointCache + Mask->NumberOfVerts*3; *(Data++) = Point0.Pos.x; *(Data++) = Point0.Pos.y; *(Data++) = 0; Mask->NumberOfVerts += 1; } } } static void Mask_TriangulateAndRasterize(memory *Memory, project_layer *Layer, mask *Mask) { if (!Mask->TriangulatedPointCache) { Mask->TriangulatedPointCache = AllocateMemory(Memory, 50*1024, P_VectorPoints); } Mask_RasterizePoints(Mask); gl_vertex_shader VertData; gl_effect_layer Test = Layer->BitmapInfo.Test; glBindFramebuffer(GL_FRAMEBUFFER, Test.FramebufferObject); glEnable(GL_STENCIL_TEST); // glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glStencilFunc(GL_ALWAYS, 0, 0xFF); // always write glStencilMask(0xff); // allow writing; ANDs any writes to the stencil buffer with this glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glUseProgram(MaskShaderProgram); // secondary VBO glGenVertexArrays(1, &VertData.VertexArrayObject); glBindVertexArray(VertData.VertexArrayObject); glBindBuffer(GL_ARRAY_BUFFER, VertData.VertexBufferObject); glBufferData(GL_ARRAY_BUFFER, Mask->NumberOfVerts*3*sizeof(real32), Mask->TriangulatedPointCache, GL_STREAM_DRAW); // position attribute glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); int Scale = glGetUniformLocation(MaskShaderProgram, "CompDimensions"); glUniform3f(Scale, (real32)Layer->Source->Info.Width, (real32)Layer->Source->Info.Height, 0); glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); glDisable(GL_CULL_FACE); glDrawArrays(GL_TRIANGLE_FAN, 0, Mask->NumberOfVerts); // glEnable(GL_CULL_FACE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glBindVertexArray(0); // glStencilFunc(GL_EQUAL, 1, 0xFF); // glStencilMask(0x00); // disables stencil writing glBindRenderbuffer(GL_RENDERBUFFER, Test.Color_Renderbuffer); glUseProgram(DefaultShaderProgram); // // Switch to main buffer glBindBuffer(GL_ARRAY_BUFFER, DefaultVerts.VertexBufferObject); glBufferData(GL_ARRAY_BUFFER, sizeof(DefaultVertices), DefaultVertices, GL_STATIC_DRAW); // position attribute glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // texture coordinate (note the last parameter's offset) glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); //glStencilFunc(GL_EQUAL, 0, 0xFF); //glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); //glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_INT, 0); glStencilFunc(GL_NOTEQUAL, 0, 0xFF); glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_INT, 0); glDisable(GL_STENCIL_TEST); glStencilMask(0xFF); glStencilFunc(GL_ALWAYS, 0, 0xFF); uint8 *Data = (uint8 *)Layer->BitmapInfo.BitmapBuffer; glReadPixels(0, 0, Layer->Source->Info.Width, Layer->Source->Info.Height, GL_RGBA, GL_UNSIGNED_BYTE, &Data[0]); glBindFramebuffer(GL_FRAMEBUFFER, 0); } 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); uint64 PackedSize = Bitmap_CalcTotalBytes(Source->Info.Width, Source->Info.Height, Source->Info.BytesPerPixel); Bitmap_CopyToPointer(Bitmap->Data, DestBuffer, BytesPerPixel, UnpackedSize); TestGL_InitTexture(&BitmapInfo->Test, DestBuffer, Width, Height); if (Layer->NumberOfMasks) { for (int i = 0; i < Layer->NumberOfMasks; i++) { mask *Mask = &Layer->Mask[i]; 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); } 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, ui *UI, comp_buffer CompBuffer, ImVec2 ViewportMin, ImVec2 Point) { source *Source = Layer->Source; v2 CompUV = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->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/24.mp4"); Source_Generate(File, Memory, SourceString); source *Source = &File->Source[0]; Layer_CreateFromSource(File, State, Memory, Source); // Action_Undo(Memory); // Action_Redo(Memory); SelectLayer(File->Layer[0], State, 0); // AddEffect(File->Layer[0], Memory, 3); /* mask *Mask = &File->Layer[0]->Mask[0]; File->Layer[0]->NumberOfMasks = 1; Mask->Point[0].Pos = V2(200, 200); Mask->Point[1].Pos = V2(200, 400); Mask->Point[2].Pos = V2(200, 520); Mask->Point[3].Pos = V2(1080, 520); Mask->Point[4].Pos = V2(1080, 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; */ // 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; // ManualKeyframeInsertF(Property, Memory, 1, 500); // ManualKeyframeInsertF(Property, Memory, 30, 800); // ManualKeyframeInsertF(Property, Memory, 15, 400); // ManualKeyframeInsertF(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; // if (!Source_Generate(File, Memory, "../asset/b.jpg")) // PostMsg(State, "File open fail..."); // source *Source2 = &File->Source[2]; // 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, memory *Memory) { #if 0 project_layer *Layer1 = CreateSolidLayer(File, Memory, 720, 1280, V4(0.0, 0.0, 0.0, 1.0)); Layer1->x.CurrentValue.f = 1280/2; Layer1->y.CurrentValue.f = 720/2; Layer1->StartFrame = 0; Layer1->EndFrame = File->EndFrame; project_layer *Layer2 = CreateSolidLayer(File, Memory, 499, 503, V4(0.0, 1.0, 0.4, 1.0)); Layer2->x.CurrentValue.f = 1280/2; Layer2->y.CurrentValue.f = 720/2; Layer2->StartFrame = 0; Layer2->EndFrame = File->EndFrame; ManualKeyframeInsertF(&Layer2->rotation, Memory, 2, 0); ManualKeyframeInsertF(&Layer2->rotation, Memory, 50, 360); Layer2->rotation.IsToggled = true; Layer2->scale.IsToggled = true; project_layer *Layer3 = CreateSolidLayer(File, Memory, 157, 163, V4(1.0, 0.3, 0.2, 1.0)); Layer3->x.CurrentValue.f = 1280/4; Layer3->y.CurrentValue.f = 720/4; Layer3->opacity.CurrentValue.f = 0.5f; Layer3->StartFrame = 0; Layer3->EndFrame = File->EndFrame; ManualKeyframeInsertF(&Layer3->x, Memory, 2, Layer3->x.CurrentValue.f); ManualKeyframeInsertF(&Layer3->x, Memory, 30, Layer3->x.CurrentValue.f+(1280/2)); ManualKeyframeInsertF(&Layer3->x, Memory, 60, Layer3->x.CurrentValue.f+(1280/3)); Layer3->x.IsToggled = true; Layer3->y.IsToggled = true; #endif } #if 0 static void CreateGrid(project_data *File, memory *Memory) { uint16 Amount = 8; real32 XInc = File->Width / Amount; real32 YInc = File->Height / Amount; for (int16 j = 0; j < 8; j++) { for (int16 i = 0; i < 8; i++) { project_layer *Layer = CreateSolidLayer(File, Memory, 400, 400, V4(0.6, 0.3, 0.4, 1.0)); Layer->x.CurrentValue.f = (XInc*i); Layer->y.CurrentValue.f = (XInc*j); Layer->opacity.CurrentValue.f = 0.25; Layer->StartFrame = 0; Layer->EndFrame = File->EndFrame; ManualKeyframeInsertF(&Layer->rotation, Memory, i, 0); ManualKeyframeInsertF(&Layer->rotation, Memory, 40+i, 360); } } } #endif /* { Layer->RenderInfo = AllocateMemory(Memory, sizeof(source_image), P_SourceData); Layer->RenderInfo = AllocateMemory(Memory, sizeof(source_video), P_SourceData); source_image *Source = (source_image *)Layer->RenderInfo; Source->Raster = LoadImage(Memory, filename); Layer->EndFrame = File.EndFrame; Layer->x.CurrentValue.f = 1280/2; Layer->y.CurrentValue.f = 720/2; Layer->StartFrame = 0; Layer->x.CurrentValue.f = 1280/2; Layer->y.CurrentValue.f = 720/2; Layer->StartFrame = 0; Layer->EndFrame = File.EndFrame; } */