#include #include #if WINDOWS #include #else #include #include #endif #if ARM #include #include #else #include #endif #include "imgui.h" #include "imgui_impl_sdl.h" #include "imgui_impl_opengl3.h" #include #if defined(IMGUI_IMPL_OPENGL_ES2) #include #else #include #endif #define STB_IMAGE_IMPLEMENTATION #define STBI_FAILURE_USERMSG #include "stb_image.h" #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" // TODO(fox): Used for thumbnails. The renderer could be expanded to do this // much more efficiently. #define STB_IMAGE_RESIZE_IMPLEMENTATION #include "stb_image_resize.h" extern "C" { #include #include #include #include #include #include } #include "defines.h" #include "my_math.h" #include "structs.h" #if STABLE #include "stable_diffusion.h" #endif #include "memory.h" #include "nanovg.h" enum instruction_mode { instruction_mode_scalar, #if ARM instruction_mode_neon, #else instruction_mode_sse, instruction_mode_avx #endif }; static char* BlendmodeNames[] = { "Normal", "Multiply", "Color Burn", "Linear Burn", "Add", "Screen", "Overlay", "Soft Light", "Hard Light", "Subtract", "Divide", "Difference" }; enum blend_mode { blend_normal, blend_multiply, blend_colorburn, blend_linearburn, blend_add, blend_screen, blend_overlay, blend_softlight, blend_hardlight, blend_subtract, blend_divide, blend_difference }; #define STRING_SIZE (1024 - sizeof(uint8)) // TODO(fox): Paths above STRING_SIZE length aren't handled properly. struct block_string { uint8 Occupied; char Char[STRING_SIZE]; }; struct bitmap_cache_status { uint32 Block_End; }; enum cache_entry_type { cache_entry_type_assert, cache_entry_type_comp, cache_entry_type_source }; struct cache_entry { uint8 IsOccupied; uint8 IsCached; uint64 CycleTime; uint32 Block_StartIndex; enum cache_entry_type Type; uint32 TypeInfo; uint32 TypeInfo_Sub; }; enum interpolation_type { interpolation_type_linear, interpolation_type_bezier, interpolation_type_hold }; struct bezier_point { uint8 Occupied; v2 Pos[3]; interpolation_type Type; bool32 IsSelected; uint8 PointSelect[3]; uint8 Color; }; struct block_bezier { uint8 Occupied; bezier_point Point[MAX_KEYFRAMES_PER_BLOCK]; }; enum selection_type { selection_type_none, selection_type_layer, selection_type_keyframe }; struct clipboard_channel { void *Name; uint16 LayerOffset; uint16 KeyframeCount; }; struct clipboard_contents { selection_type Type; clipboard_channel Channel[16]; uint16 ChannelCount; }; struct layer_bitmap_state { // Image and video bool32 ToUpdate = 1; // GL state // gl_effect_layer Test; // gl_effect_layer TestM; // Video state int32 CurrentFrame = -1; // The last frame number rendered to the bitmap. -1 gurantees a load upon layer creation. void *AVInfo; // Internal data containing current frame info }; struct render_state { struct layer_bitmap_state Bitmap[MAX_LAYERS]; cache_entry Entry[2048]; }; struct render_queue_item { bool32 Type; v2 Pos; }; struct layer_transforms { real32 x; real32 y; real32 ax; real32 ay; real32 rotation; real32 scale; }; struct render_queue { uint16 Playhead; uint16 CurrentIdx; render_queue_item Item[512]; }; enum focused_window { focus_viewport, focus_properties, focus_timeline }; struct sorted_property_array { uint32 KeyframeCount; uint32 MinYIndex; uint32 MaxYIndex; }; struct sorted_comp_array { uint32 LayerCount; uint32 FakeLayerCount; uint32 CurrentSortIndex; // Used intermediately in the sorting algorithm real32 DisplaySize; }; struct sorted_layer_array { uint16 Block_Layer_Index; bool32 IsFake; real32 SortedOffset; real32 DisplayOffset; uint16 Sorted_Effect_Index[MAX_EFFECTS]; uint16 SortedPropertyStart; uint16 SortedKeyframeStart; real32 Interact_Offset[4]; }; struct sorted_file { sorted_comp_array *CompArray; sorted_layer_array *LayerArray; sorted_property_array *PropertyStart; uint16 *PropertyArray; uint16 *SourceArray; // sorted by 'recency,' wip for stablediffusion stuff uint16 TempSourceCount; uint64 Layer_SortSize; uint64 Property_SortSize; uint64 Source_SortSize; }; struct shape_options { int Visibility; v4 FillCol = {1, 1, 1, 1}; v4 StrokeCol = {0, 0, 0, 1}; float StrokeWidth = 0; nvg_line_cap LineJoinType = NVG_SQUARE; nvg_line_cap LineCapType = NVG_MITER; real32 Roundness; }; struct shape_layer { uint16 Block_Bezier_Index[MAX_KEYFRAME_BLOCKS]; uint16 Block_Bezier_Count; uint16 Point_Count; bool32 IsClosed; bool32 Contiguous = 1; // No points have been deleted/added, so sorting isn't needed. // NOTE(fox): Point vals are normalized based on whatever these values are! // They get set once the shape becomes a shape layer! real32 Width; real32 Height; }; enum timeline_mode { timeline_mode_default, timeline_mode_graph }; struct pen_state { }; struct brush_state { ImVec2 UIPos; // Initial position when ctrl is held real32 Size = 256; // Maxes at 1024 for now real32 Hardness = 0.55f; // From 1 to 100 real32 Spacing = 1.0f; bool32 EraseMode = 0; GLuint GLTexture; v2 PrevPos; void *PaintBuffer; void *TransientBitmap; uint32 LayerToPaint_Index = -1; rectangle CacheBounds = { 99999, 99999, -99999, -99999 }; }; enum interact_type { interact_type_none, interact_type_timeline_scrub, interact_type_slider_scrub, interact_type_layer_move, interact_type_layer_timeadjust, interact_type_viewport_selection, interact_type_viewport_transform, interact_type_viewport_transform_gizmo, interact_type_viewport_duplicate, interact_type_viewport_slide, interact_type_keyframe_move, interact_type_keyframe_scale, interact_type_keyframe_rotate, interact_type_brush }; char *ToolName[] { "Move", "Point move", "Crop", "Brush", "Slide", "Pen", "Rectangle" }; enum tool { tool_default, tool_default_pointmove, tool_crop, tool_brush, tool_slide, tool_pen, tool_rectangle, tool_count }; struct interact_transform { v2 Min; v2 Max; real32 Radians; real32 RadianOffset; v2 Position; v2 OGCenter; v2 NewCenter; real32 Scale = 1.0f; ImVec2 OGPos; uint32 TransformMode; }; struct interact_slide_layer { uint16 Index; real32 Offset[4]; }; enum hotkey_input { hotkey_none, hotkey_newlayer_source, hotkey_newlayer_paint, hotkey_newlayer_shape, hotkey_deletelayer, hotkey_duplicatelayer, hotkey_undo, hotkey_redo, }; enum property_display_type { property_display_type_standard, property_display_type_blendmode, property_display_type_color }; struct header_property { char *Name; real32 DefaultValue; property_display_type DisplayType; real32 MinVal; real32 MaxVal; bool32 AlwaysInteger; }; struct gl_effect_layer { bool32 Initialized; GLuint Texture; GLuint FramebufferObject; uint32 Color_Renderbuffer; uint32 Stencil_Renderbuffer; }; enum gl_shape_renderflags { gl_renderflag_fill = 1 << 0, gl_renderflag_stroke = 1 << 1, gl_renderflag_convex = 1 << 2, gl_renderflag_texture = 1 << 3, gl_renderflag_blend = 1 << 4 }; struct gl_data { int Type; void *StrokeData; uint32 StrokeCount; v4 StrokeCol; void *FillData; uint32 FillCount; v4 FillCol; layer_transforms T; real32 Width; real32 Height; void *BitmapData; blend_mode BlendMode; int RenderMode; v2 BlendMin; v2 BlendMax; }; struct gl_viewport_data { ImVec2 ViewportMin; ImVec2 ViewportMax; ImVec2 ViewportSize; int ViewportDisplay; int Width; int Height; int BytesPerPixel; ImVec2 UIPos; ImVec2 UIZoom; real32 UIScale; gl_data *LayerEntry[MAX_LAYERS]; int LayerCount; }; enum effect_display_type { effect_display_type_standard, effect_display_type_levels, effect_display_type_curves }; struct block_effect { uint8 Occupied; char ID[8]; bool32 IsToggled; uint32 Block_Property_Index[MAX_PROPERTIES_PER_EFFECT]; real32 ExtraData[16]; }; struct header_effect { char *Name; char *ID; // Eight-letter string that's used in the actual file void (*func)(real32 *, int, int, int, void *, uint16); uint16 PropertyStartIndex; uint16 Property_Count; effect_display_type DisplayType; bool32 UseGL; uint32 GLShaderIndex; }; enum imgui_popups { popup_none, popup_saveas, popup_keybinds }; enum option_sortflag { sortflag_up = 1 << 0, sortflag_furthest = 1 << 1 }; struct project_state { bool32 UpdateKeyframes = 0; bool32 UpdateFrame = 1; // only refreshes frame; set UpdateKeyframes to update animation bool32 UpdateScreen = 1; // refreshes entire UI; influenced by raw key/mouse input bool32 DebugDisableCache = 1; // TODO(fox): Group inconsequential state UI into one thing? bool32 DuplicateMode = sortflag_furthest | sortflag_up; bool32 ShapeMode = 0; bool32 ViewportEnabled = 0; bool32 SelectionMode = 1; uint16 PreviousSelection[MAX_LAYERS]; uint16 PreviousSelectionCount = 0; // bad uint32 CachedFrameCount; int32 LastCachedFrame = -10000; bool32 MenuFocused = 0; uint64 HotFramePerf = 0; uint32 AVCount; render_state Render; render_queue Queue; int32 Frame_Current; tool Tool = tool_default; // GLuint ToolIconTex[(int)tool_count]; pen_state Pen = {}; brush_state Brush; #if STABLE int32 CurlActive = 0; char JSONPayload[1024*1024*4]; real32 SDPercentDone; real32 SDTimeEstimate; real64 SDTimer; #endif header_effect Effect[128]; header_property Property[512]; uint16 Playhead_Effect; uint16 Playhead_Property; int32 PreviewLayer = -1; int32 PreviewSource = -1; hotkey_input HotkeyInput; void *Dump1; void *Dump2; char DummyName[512]; char DummyName2[512]; bool32 IsRunning = 1; bool32 IsPlaying; bool32 PlayAudio; int32 FramePlayingCount; bool32 FirstFrame = 1; int32 AudioLayerIndex = -1; void *ClipboardBuffer; uint64 ClipboardSize; int16 MostRecentlySelectedLayer = -1; // NOTE(fox): Try to use this only where you actually need it (the // ambiguous case of copying keyframes versus layers), since this state // transfer will get buggy if you expand it to everything. selection_type RecentSelectionType = selection_type_none; interact_transform Interact_Transform; interact_type Interact_Active; int32 Interact_Modifier; bool32 Interact_OutOfDrag; // TODO(fox): replace this real32 Interact_Offset[12]; real32 Interact_Dup_Previous[2]; void *Interact_Address; int Interact_Count; // whether duplicated layers get created above or below int DuplicateDirection = -1; ImGuiID RightDock; // NOTE(fox): We need to keep track of when the user changes the CurrentValue of a // channel that has keyframes on it (i.e. CurrentValue will now evaluate to // its previous value unless the user adds a new keyframe to the channel) // so we can purge the cache if the user changes to a different frame. int UncommitedKeyframe; int32 Initializing = 3; int32 MsgTime; // currently in "frames" char *Msg; imgui_popups ImGuiPopups; char Filename[512]; ImGuiTextFilter filter; // This filter API is pretty ballin'. bool32 RerouteEffects; // Allows shift+space hotkey to gain focus on the effects panel. ImDrawListSplitter Test; ImDrawListSplitter Split_KeybindUI; int32 Warp_X = 0; int32 Warp_Y = 0; bool32 Warp_WantSetPos = false; ImVec2 Warp_PositionToSet; real32 Warp_PositionInitial; int32 Warp_Direction; uint32 InteractTransformMode; // Whether a drag on the Shift+T UI is scale (1), rotation (2), or position (3). timeline_mode TimelineMode; bool32 BoxSelect; focused_window FocusedWindow; // Convenience for adding window-specific hotkeys. bool32 SetFocus; v2 LastClickedPoint = V2(1, 1); }; // UI info that's saved to the file and is not part of the history tree struct ui { ImVec2 CompZoom; // In screen pixels, not percentage. ImVec2 CompPos; int Mode = 1; // Under 1 is zoomed in! ImVec2 TimelinePercentZoomed; ImVec2 TimelinePercentOffset; ImVec2 GraphZoomSize; ImVec2 GraphMoveSize; v4 Color = {1, 1, 1, 1}; v4 AltColor = {0, 0, 0, 1}; bool32 IsPrimary; #if STABLE sd_state SD; bool32 StableEnabled = 0; #endif shape_layer Shape; shape_options ShapeOpt; ImU32 LayerColors[16] = { 0xff8b1f1f, 0xffc25909, 0xff57c20a, 0xff203d6a, 0xffa48fb7, 0xffd14061, 0xff38b683, 0xff3fdbe5, 0xffc9c9c9, 0xff978489, 0xfffaf5ab, 0xff101010, 0xffa024ca, 0xfffae920, 0xff208dfa, 0xfffa2051 }; }; struct project_data { uint8 Occupied; uint16 Layer_Count; uint16 Source_Count; uint16 Comp_Count; uint16 PrincipalCompIndex; ui UI; }; struct block_composition { uint8 Occupied; uint16 Name_String_Index; uint16 Width; uint16 Height; uint16 BytesPerPixel; uint16 FPS; uint32 Frame_Count; int32 Frame_Start; int32 Frame_End; }; enum source_type { source_type_none, source_type_principal, source_type_principal_temp, source_type_file }; struct block_source { uint8 Occupied; bool32 IsSelected; uint16 Path_String_Index; uint16 Name_String_Index; // Only used for type_principal uint16 Bitmap_Index; uint16 Width; uint16 Height; uint16 BytesPerPixel; // LibAV specific real32 FPS; bool32 HasAudio; bool32 HasVideo; uint32 RelativeTimestamp; GLuint ThumbnailTex; source_type Type; }; struct property_channel { uint8 Occupied; uint16 Block_Bezier_Index[MAX_KEYFRAME_BLOCKS]; uint16 Block_Bezier_Count; // NOTE(fox): Don't use this in sorted circumstances; use the one in sorted_property_array instead! uint16 Keyframe_Count; int32 Identifier; real32 CurrentValue; real32 MaxVal; real32 MinVal; bool32 AlwaysInteger; real32 ScrubVal; // increment when dragging on sliders, etc. bool32 IsToggled; }; char *DefaultChannel[] = { "X Position", "Y Position", "Anchor X", "Anchor Y", "Rotation", "Scale", "Opacity", "Frame Number" }; struct block_layer { uint8 Occupied; bool32 IsPrecomp; bool32 IsShapeLayer; bool32 Precomp_Toggled; // NOTE(fox): References a precomp index if IsPrecomp is true, not a source index. uint16 Block_Source_Index; uint16 Block_String_Index; // References the precomp that the layer belongs to. uint16 Block_Composition_Index; uint16 Block_Effect_Index[MAX_EFFECTS]; uint16 Block_Effect_Count; shape_layer Shape; shape_options ShapeOpt; blend_mode BlendMode; union { property_channel Property[8]; struct { property_channel x; property_channel y; property_channel ax; property_channel ay; property_channel rotation; property_channel scale; property_channel opacity; property_channel time; }; }; bool32 IsVisible; bool32 IsLocked; // Valid values this can be: // 0x01 - standard select // 0x02 - precomp layer // 0x04 - transient box selection state when shift is held bool32 IsSelected; bool32 IsAdjustment; // NOTE(fox): I use these in some places without calling // Interact_Evaluate_Layer(), because I'm assuming it's impossible for // layer dragging to happen in those contexts. Check for this if weird bugs appear. int32 Frame_Start; int32 Frame_End; // This is what changes when the layer is dragged. It also marks the starting frame for video footage. int32 Frame_Offset; real32 Vertical_Offset; real32 Vertical_Height = 1; uint16 ColIndex; }; struct render_byte_info { uint32 MaskPixel; uint32 ByteOffset; uint32 Bits; real32 Normalized; }; struct transform_info { real32 XAxisPX; real32 XAxisPY; real32 YAxisPX; real32 YAxisPY; real32 LayerWidth; real32 LayerHeight; real32 LayerBytesPerPixel; real32 LayerPitch; render_byte_info LayerBits; real32 BufferWidth; real32 BufferHeight; real32 BufferBytesPerPixel; real32 BufferPitch; render_byte_info BufferBits; real32 LayerOpacity; real32 OriginX; real32 OriginY; blend_mode BlendMode; bool32 IsAdjustment; rectangle ClipRect; void *SourceBuffer; }; struct direct_info { real32 BufferWidth; real32 BufferHeight; real32 BufferBytesPerPixel; real32 BufferPitch; render_byte_info BufferBits; real32 Opacity; blend_mode BlendMode; rectangle ClipRect; void *SourceBuffer; bool32 SwapActive; bool32 OnlyBlendAlpha; }; struct brush_info { uint32 BrushLength; rectangle LayerBounds; uint32 LayerPitch; uint32 BrushPitch; int32 ExtraX; int32 ExtraY; render_byte_info LayerBits; render_byte_info BrushBits; int BytesPerPixel; int SourceWidth; int SourceBytesPerPixel; void *SourceBuffer; void *BrushBuffer; real32 R_Brush; real32 G_Brush; real32 B_Brush; real32 A_Brush; bool32 EraseMode; uint8 *BrushRow; }; enum render_type { render_type_main, render_type_notransform, render_type_notransform_swap, render_type_brush }; struct render_entry { void *RenderData; void *OutputBuffer; render_type RenderType; rectangle RenderRegion; }; #include "ffmpeg_backend.h" #include "layer.h" #include "debug.h" #include "all.h" #include "imgui_internal_widgets.h" #include "imgui_ops.h"