summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFox Caminiti <fox@foxcam.net>2022-11-21 13:56:37 -0500
committerFox Caminiti <fox@foxcam.net>2022-11-21 13:56:37 -0500
commit09c6cb9e3be2655b842e13dd68879cb10cf52acf (patch)
tree62b19be03e7fd0e9235953e8517980e7c11b059e
parent990bd319c696c2b65fa858b40fd75279fec2a13b (diff)
things
-rwxr-xr-xbuild.bat6
-rwxr-xr-xbuild.sh20
-rw-r--r--createcalls.cpp160
-rw-r--r--debug.h1
-rw-r--r--functions.h2
-rw-r--r--lib/base64.c4
-rw-r--r--lib/stb_image_resize.h2634
-rw-r--r--main.cpp351
-rw-r--r--main.h38
-rw-r--r--memory.cpp2
-rw-r--r--memory.h1
-rw-r--r--my_imgui_widgets.cpp395
-rw-r--r--prenderer.cpp43
-rw-r--r--stable_diffusion.cpp80
-rw-r--r--stable_diffusion.h2
-rw-r--r--undo.cpp2
16 files changed, 3119 insertions, 622 deletions
diff --git a/build.bat b/build.bat
index c2735c1..17eb14e 100755
--- a/build.bat
+++ b/build.bat
@@ -8,7 +8,6 @@ REM /Zi /O2
set OPTIMIZATION=/Zi
set DEBUG=1
set IMGUI=0
-set THREADED=1
set WINDOWS=1
set ARM=0
set PACKEDRGB=0
@@ -21,15 +20,14 @@ set IMGUI_FILES=imgui\backends\imgui_impl_sdl.cpp imgui\backends\imgui_impl_open
set WARNING_FLAGS=/W2 /wd4805 /wd4477 /wd4244 /wd4305
if %DEBUG%==1 ( set PREPROCESSORS="/DDEBUG=1" )
-if %THREADED%==1 ( set PREPROCESSORS=%PREPROCESSORS% "/DTHREADED=1" )
if %WINDOWS%==1 ( set PREPROCESSORS=%PREPROCESSORS% "/DWINDOWS=1" )
if %ARM%==1 ( set PREPROCESSORS=%PREPROCESSORS% "/DARM=1" )
if %PACKEDRGB%==1 ( set PREPROCESSORS=%PREPROCESSORS% "/DPACKEDRGB=1" )
if %PERF%==1 ( set PREPROCESSORS=%PREPROCESSORS% "/DPERF=1" )
-cl /nologo %OPTIMIZATION% /MD /I.. /Iimgui /I%SDL2_DIR%\include my_imgui_internal_widgets.cpp /Fobin/ /c
+cl /nologo %OPTIMIZATION% /MD /I.. /Iimgui /I%SDL2_DIR%\include my_imgui_internal_widgets.cpp /Fobin/ /c
if %IMGUI%==1 ( cl /nologo %OPTIMIZATION% /MD /I.. /Iimgui /I%SDL2_DIR%\include %IMGUI_FILES% /Fobin/ /c )
-cl /nologo %OPTIMIZATION% /MD /I.. /Ilib/glad/include lib/glad.c /Fobin/ /c
+cl /nologo %OPTIMIZATION% /MD /I.. /Ilib/glad/include lib/glad.c /Fobin/ /c
cl /nologo /Zi /MD %WARNING_FLAGS% %PREPROCESSORS% %INCLUDES% main.cpp bin/*.obj /Febin/real2d.exe /Fobin/ /link %SDL_LIBS% %FFMPEG_LIBS% /subsystem:console
diff --git a/build.sh b/build.sh
index e0df0d4..31a9eed 100755
--- a/build.sh
+++ b/build.sh
@@ -1,13 +1,10 @@
#!/bin/bash
-WINDOWS=0 # Compile for Windows with Mingw.
ARM=0 # Compile on ARM machines.
OPTIMIZATION="-O2" # Enable optimization.
DEBUG=0 # Compile with debug UI.
IMGUI=1 # Compile ImGui libs. Our custom ImGui functions still compile on zero.
-THREADED=1 # Compile with threading. Useful to disable when stepping through the renderer.
-PACKEDRGB=0 # Use 4x4 chunks for the software rasterizer. Isn't actually faster at the moment.
PERF=0 # Print cycle stats.
# Add flag overrides to this file (it's untracked on the tree)
@@ -53,9 +50,6 @@ fi
if [[ "$THREADED" == 1 ]]; then
WARNING_FLAGS="$WARNING_FLAGS -DTHREADED=1"
fi
-if [[ "$WINDOWS" == 1 ]]; then
-WARNING_FLAGS="$WARNING_FLAGS -DWINDOWS=1"
-fi
if [[ "$ARM" == 1 ]]; then
WARNING_FLAGS="$WARNING_FLAGS -DARM=1"
ADDITIONAL_FLAGS="
@@ -66,9 +60,6 @@ ADDITIONAL_FLAGS="
-march=native
"
fi
-if [[ "$PACKEDRGB" == 1 ]]; then
-WARNING_FLAGS="$WARNING_FLAGS -DPACKEDRGB=1"
-fi
if [[ "$PERF" == 1 ]]; then
WARNING_FLAGS="$WARNING_FLAGS -DPERF=1"
fi
@@ -107,19 +98,12 @@ if [[ "$IMGUI" == 1 ]]; then
done
fi
-if [[ "$WINDOWS" == 1 ]]; then
-clang++ $OPTIMIZATION $WARNING_FLAGS -target x86_64-pc-windows-gnu -march=x86-64-v3 -I .. -Iimgui -Iimgui/backends \
- main.cpp imgui/imgui*.cpp imgui/backends/imgui_impl_sdl.cpp imgui/backends/imgui_impl_opengl3.cpp \
- -I/usr/x86_64-w64-mingw32/include/SDL2 -I/usr/x86_64-w64-mingw32/include/GL \
- -lmingw32 -lopengl32 -lSDL2main -lSDL2 -llibavcodec -llibswscale -llibavformat -llibavutil \
- -o bin/real2d
-else
clang lib/glad.c $GLAD_FLAGS -I/usr/local/include -I/opt/local/include -c \
$WARNING_FLAGS $OPTIMIZATION $ADDITIONAL_FLAGS -o bin/glad.o
clang main.cpp $WARNING_FLAGS $OPTIMIZATION $ADDITIONAL_FLAGS -o bin/real2d bin/*.o \
$GLAD_FLAGS \
- -std=c++11 -lstdc++ -lcurl -Iimgui -Iimgui/backends \
+ -std=c++11 -lstdc++ -Iimgui -Iimgui/backends \
$SDL_ARGS \
-I . \
-lm -I /usr/local/include $(pkg-config --cflags --libs $FFMPEG_LIBS)
-fi
+# -lcurl
diff --git a/createcalls.cpp b/createcalls.cpp
index e88e39f..4c72c4e 100644
--- a/createcalls.cpp
+++ b/createcalls.cpp
@@ -38,6 +38,22 @@ Source_Delete(project_data *File, memory *Memory, uint32 Index)
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);
+ 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)
{
@@ -67,10 +83,10 @@ Source_Generate(project_data *File, project_state *State, memory *Memory, void *
}
static bezier_point *
-Bezier_LookupAddress(memory *Memory, property_channel *Property, uint16 Index)
+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]);
+ block_bezier *Bezier = (block_bezier *)Memory_Block_AddressAtIndex(Memory, F_Bezier, Property->Block_Bezier_Index[0], AssertExists);
return &Bezier->Point[Index];
}
@@ -100,7 +116,7 @@ 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]);
+ 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);
@@ -108,7 +124,7 @@ Bezier_Add(memory *Memory, property_channel *Property, bezier_point PointData)
}
int k = 0;
for (;;) {
- bezier_point *Point = Bezier_LookupAddress(Memory, Property, k);
+ bezier_point *Point = Bezier_LookupAddress(Memory, Property, k, 0);
if (!Point->Occupied) {
History_Action_Swap(Memory, F_Bezier, sizeof(*Point), Point);
*Point = PointData;
@@ -182,6 +198,18 @@ Layer_Interact_Evaluate(memory *Memory, project_state *State, uint16 Layer_Index
}
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);
@@ -208,23 +236,47 @@ void Source_DeselectAll(project_data *File, memory *Memory)
}
}
-void Clipboard_Paste(project_data *File, project_state *State, memory *Memory, sorted_layer *SortedLayerArray)
+// 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 h = 0, c = 0, i = 0;
- block_layer *Layer;
+ int i = SortedCompInfo->LayerCount - 1;
+ block_layer *Layer = NULL;
clipboard_channel *Channel;
int b = 0;
- while (b < Contents->ChannelCount) {
+ 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 (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i))
+ while (i >= 0)
{
- Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
- if (Layer->IsSelected)
+ 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++) {
@@ -232,6 +284,7 @@ void Clipboard_Paste(project_data *File, project_state *State, memory *Memory, s
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);
}
@@ -239,22 +292,26 @@ void Clipboard_Paste(project_data *File, project_state *State, memory *Memory, s
Channel = &Contents->Channel[b];
}
}
+ Layer = NULL;
+ if (b < Contents->ChannelCount) {
+ if (NumberOfLayersFromClipboard != 1)
+ break;
+ else
+ b = 0;
+ }
}
}
-void Clipboard_Store(project_data *File, project_state *State, memory *Memory, sorted_property_info *SortedPropertyInfo, uint16 *SortedPropertyArray)
+void Clipboard_Store(project_data *File, project_state *State, memory *Memory, sorted_comp_info *SortedCompInfo, sorted_layer *SortedLayerInfo, sorted_property_info *SortedPropertyInfo, uint16 *SortedPropertyArray)
{
- // TODO(fox): Multi-precomp support!
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--)
- Assert(0);
- for (int i = 10; i >= 0; i--)
+ for (int i = SortedCompInfo->LayerCount - 1; i >= 0; i--)
{
- sorted_layer SortEntry = {}; // SortedLayerInfo[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++) {
@@ -263,10 +320,18 @@ void Clipboard_Store(project_data *File, project_state *State, memory *Memory, s
sorted_property_info *InfoLocation = SortedPropertyInfo + (i * 7) + h;
uint16 *ArrayLocation = SortedPropertyArray + (i * 7 * MAX_KEYFRAMES_PER_BLOCK) + (h * MAX_KEYFRAMES_PER_BLOCK);
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) {
- Memory_Copy((uint8 *)State->ClipboardBuffer + ClipboardPos, (uint8 *)PointAddress, sizeof(bezier_point));
+ 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++;
}
@@ -486,6 +551,35 @@ void Layer_Evaluate_Display(block_layer *Layer, sorted_layer *LayerArrayStart, s
*/
}
+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.
@@ -591,12 +685,20 @@ sorted_file File_Sort_Push(project_data *File, project_state *State, memory *Mem
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)
+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);
}
@@ -844,6 +946,7 @@ Brush_Info(brush_info *B, brush_state *Brush, block_source *Source, v2 LayerPos,
}
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;
@@ -890,11 +993,22 @@ PaintTest(brush_info B, void *Buffer, rectangle RenderRegion)
real32 BrushAlpha = Brush_BitmapAlpha * B.A_Brush;
real32 LayerAlpha = A_Layer;
- real32 A_Blend = LayerAlpha + BrushAlpha;
+ real32 A_Blend = 0;
+ real32 R_Blend = 0;
+ real32 G_Blend = 0;
+ real32 B_Blend = 0;
- real32 R_Blend = (R_Layer * (1.0f - BrushAlpha)) + (B.R_Brush * BrushAlpha);
- real32 G_Blend = (G_Layer * (1.0f - BrushAlpha)) + (B.G_Brush * BrushAlpha);
- real32 B_Blend = (B_Layer * (1.0f - BrushAlpha)) + (B.B_Brush * BrushAlpha);
+ 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);
+ } 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);
diff --git a/debug.h b/debug.h
index 202eb20..6860bdb 100644
--- a/debug.h
+++ b/debug.h
@@ -28,6 +28,7 @@ struct project_debug
{
debug_temp Temp;
bool32 ToggleWindow = 1;
+ bool32 NoThreading = 0;
uint64 PixelCountTransparent;
uint64 PixelCountRendered;
uint64 PixelCountChecked;
diff --git a/functions.h b/functions.h
index 69ce817..9ac14d4 100644
--- a/functions.h
+++ b/functions.h
@@ -16,7 +16,7 @@ static ImVec2 Layer_LocalToScreenSpace(project_state *State, memory *Memory, blo
static v2 TransformPoint(layer_transforms T, real32 Width, real32 Height, v2 Point);
-static bezier_point * Bezier_LookupAddress(memory *Memory, property_channel *Property, uint16 Index);
+static bezier_point * Bezier_LookupAddress(memory *Memory, property_channel *Property, uint16 Index, bool32 AssertExists = 1);
static void Bezier_EvaluateValue(project_state *State, bezier_point *PointAddress, v2 *Pos, real32 Y_Increment);
static void Transform_ApplyInteractive(interact_transform Interact, real32 *OutputX, real32 *OutputY, real32 *OutputRotation, real32 *OutputScale);
diff --git a/lib/base64.c b/lib/base64.c
index b6a8e5d..07caa0a 100644
--- a/lib/base64.c
+++ b/lib/base64.c
@@ -58,7 +58,7 @@ size_t base64_encode_size(size_t len)
{
size_t olen;
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
- olen += olen / 72; /* line feeds */
+ // olen += olen / 72; /* line feeds */
olen++; /* nul termination */
Assert(olen >= len);
return olen;
@@ -85,7 +85,7 @@ void base64_encode(const unsigned char *src, size_t len,
in += 3;
line_len += 4;
if (line_len >= 72) {
- *pos++ = '\n';
+ // *pos++ = '\n';
line_len = 0;
}
}
diff --git a/lib/stb_image_resize.h b/lib/stb_image_resize.h
new file mode 100644
index 0000000..ef9e6fe
--- /dev/null
+++ b/lib/stb_image_resize.h
@@ -0,0 +1,2634 @@
+/* stb_image_resize - v0.97 - public domain image resizing
+ by Jorge L Rodriguez (@VinoBS) - 2014
+ http://github.com/nothings/stb
+
+ Written with emphasis on usability, portability, and efficiency. (No
+ SIMD or threads, so it be easily outperformed by libs that use those.)
+ Only scaling and translation is supported, no rotations or shears.
+ Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation.
+
+ COMPILING & LINKING
+ In one C/C++ file that #includes this file, do this:
+ #define STB_IMAGE_RESIZE_IMPLEMENTATION
+ before the #include. That will create the implementation in that file.
+
+ QUICKSTART
+ stbir_resize_uint8( input_pixels , in_w , in_h , 0,
+ output_pixels, out_w, out_h, 0, num_channels)
+ stbir_resize_float(...)
+ stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0,
+ output_pixels, out_w, out_h, 0,
+ num_channels , alpha_chan , 0)
+ stbir_resize_uint8_srgb_edgemode(
+ input_pixels , in_w , in_h , 0,
+ output_pixels, out_w, out_h, 0,
+ num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP)
+ // WRAP/REFLECT/ZERO
+
+ FULL API
+ See the "header file" section of the source for API documentation.
+
+ ADDITIONAL DOCUMENTATION
+
+ SRGB & FLOATING POINT REPRESENTATION
+ The sRGB functions presume IEEE floating point. If you do not have
+ IEEE floating point, define STBIR_NON_IEEE_FLOAT. This will use
+ a slower implementation.
+
+ MEMORY ALLOCATION
+ The resize functions here perform a single memory allocation using
+ malloc. To control the memory allocation, before the #include that
+ triggers the implementation, do:
+
+ #define STBIR_MALLOC(size,context) ...
+ #define STBIR_FREE(ptr,context) ...
+
+ Each resize function makes exactly one call to malloc/free, so to use
+ temp memory, store the temp memory in the context and return that.
+
+ ASSERT
+ Define STBIR_ASSERT(boolval) to override assert() and not use assert.h
+
+ OPTIMIZATION
+ Define STBIR_SATURATE_INT to compute clamp values in-range using
+ integer operations instead of float operations. This may be faster
+ on some platforms.
+
+ DEFAULT FILTERS
+ For functions which don't provide explicit control over what filters
+ to use, you can change the compile-time defaults with
+
+ #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something
+ #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something
+
+ See stbir_filter in the header-file section for the list of filters.
+
+ NEW FILTERS
+ A number of 1D filter kernels are used. For a list of
+ supported filters see the stbir_filter enum. To add a new filter,
+ write a filter function and add it to stbir__filter_info_table.
+
+ PROGRESS
+ For interactive use with slow resize operations, you can install
+ a progress-report callback:
+
+ #define STBIR_PROGRESS_REPORT(val) some_func(val)
+
+ The parameter val is a float which goes from 0 to 1 as progress is made.
+
+ For example:
+
+ static void my_progress_report(float progress);
+ #define STBIR_PROGRESS_REPORT(val) my_progress_report(val)
+
+ #define STB_IMAGE_RESIZE_IMPLEMENTATION
+ #include "stb_image_resize.h"
+
+ static void my_progress_report(float progress)
+ {
+ printf("Progress: %f%%\n", progress*100);
+ }
+
+ MAX CHANNELS
+ If your image has more than 64 channels, define STBIR_MAX_CHANNELS
+ to the max you'll have.
+
+ ALPHA CHANNEL
+ Most of the resizing functions provide the ability to control how
+ the alpha channel of an image is processed. The important things
+ to know about this:
+
+ 1. The best mathematically-behaved version of alpha to use is
+ called "premultiplied alpha", in which the other color channels
+ have had the alpha value multiplied in. If you use premultiplied
+ alpha, linear filtering (such as image resampling done by this
+ library, or performed in texture units on GPUs) does the "right
+ thing". While premultiplied alpha is standard in the movie CGI
+ industry, it is still uncommon in the videogame/real-time world.
+
+ If you linearly filter non-premultiplied alpha, strange effects
+ occur. (For example, the 50/50 average of 99% transparent bright green
+ and 1% transparent black produces 50% transparent dark green when
+ non-premultiplied, whereas premultiplied it produces 50%
+ transparent near-black. The former introduces green energy
+ that doesn't exist in the source image.)
+
+ 2. Artists should not edit premultiplied-alpha images; artists
+ want non-premultiplied alpha images. Thus, art tools generally output
+ non-premultiplied alpha images.
+
+ 3. You will get best results in most cases by converting images
+ to premultiplied alpha before processing them mathematically.
+
+ 4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the
+ resizer does not do anything special for the alpha channel;
+ it is resampled identically to other channels. This produces
+ the correct results for premultiplied-alpha images, but produces
+ less-than-ideal results for non-premultiplied-alpha images.
+
+ 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED,
+ then the resizer weights the contribution of input pixels
+ based on their alpha values, or, equivalently, it multiplies
+ the alpha value into the color channels, resamples, then divides
+ by the resultant alpha value. Input pixels which have alpha=0 do
+ not contribute at all to output pixels unless _all_ of the input
+ pixels affecting that output pixel have alpha=0, in which case
+ the result for that pixel is the same as it would be without
+ STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for
+ input images in integer formats. For input images in float format,
+ input pixels with alpha=0 have no effect, and output pixels
+ which have alpha=0 will be 0 in all channels. (For float images,
+ you can manually achieve the same result by adding a tiny epsilon
+ value to the alpha channel of every image, and then subtracting
+ or clamping it at the end.)
+
+ 6. You can suppress the behavior described in #5 and make
+ all-0-alpha pixels have 0 in all channels by #defining
+ STBIR_NO_ALPHA_EPSILON.
+
+ 7. You can separately control whether the alpha channel is
+ interpreted as linear or affected by the colorspace. By default
+ it is linear; you almost never want to apply the colorspace.
+ (For example, graphics hardware does not apply sRGB conversion
+ to the alpha channel.)
+
+ CONTRIBUTORS
+ Jorge L Rodriguez: Implementation
+ Sean Barrett: API design, optimizations
+ Aras Pranckevicius: bugfix
+ Nathan Reed: warning fixes
+
+ REVISIONS
+ 0.97 (2020-02-02) fixed warning
+ 0.96 (2019-03-04) fixed warnings
+ 0.95 (2017-07-23) fixed warnings
+ 0.94 (2017-03-18) fixed warnings
+ 0.93 (2017-03-03) fixed bug with certain combinations of heights
+ 0.92 (2017-01-02) fix integer overflow on large (>2GB) images
+ 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions
+ 0.90 (2014-09-17) first released version
+
+ LICENSE
+ See end of file for license information.
+
+ TODO
+ Don't decode all of the image data when only processing a partial tile
+ Don't use full-width decode buffers when only processing a partial tile
+ When processing wide images, break processing into tiles so data fits in L1 cache
+ Installable filters?
+ Resize that respects alpha test coverage
+ (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage:
+ https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp )
+*/
+
+#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H
+#define STBIR_INCLUDE_STB_IMAGE_RESIZE_H
+
+#ifdef _MSC_VER
+typedef unsigned char stbir_uint8;
+typedef unsigned short stbir_uint16;
+typedef unsigned int stbir_uint32;
+#else
+#include <stdint.h>
+typedef uint8_t stbir_uint8;
+typedef uint16_t stbir_uint16;
+typedef uint32_t stbir_uint32;
+#endif
+
+#ifndef STBIRDEF
+#ifdef STB_IMAGE_RESIZE_STATIC
+#define STBIRDEF static
+#else
+#ifdef __cplusplus
+#define STBIRDEF extern "C"
+#else
+#define STBIRDEF extern
+#endif
+#endif
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Easy-to-use API:
+//
+// * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4)
+// * input_w is input image width (x-axis), input_h is input image height (y-axis)
+// * stride is the offset between successive rows of image data in memory, in bytes. you can
+// specify 0 to mean packed continuously in memory
+// * alpha channel is treated identically to other channels.
+// * colorspace is linear or sRGB as specified by function name
+// * returned result is 1 for success or 0 in case of an error.
+// #define STBIR_ASSERT() to trigger an assert on parameter validation errors.
+// * Memory required grows approximately linearly with input and output size, but with
+// discontinuities at input_w == output_w and input_h == output_h.
+// * These functions use a "default" resampling filter defined at compile time. To change the filter,
+// you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE
+// and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API.
+
+STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ int num_channels);
+
+STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ float *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ int num_channels);
+
+
+// The following functions interpret image data as gamma-corrected sRGB.
+// Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel,
+// or otherwise provide the index of the alpha channel. Flags value
+// of 0 will probably do the right thing if you're not sure what
+// the flags mean.
+
+#define STBIR_ALPHA_CHANNEL_NONE -1
+
+// Set this flag if your texture has premultiplied alpha. Otherwise, stbir will
+// use alpha-weighted resampling (effectively premultiplying, resampling,
+// then unpremultiplying).
+#define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0)
+// The specified alpha channel should be handled as gamma-corrected value even
+// when doing sRGB operations.
+#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1)
+
+STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ int num_channels, int alpha_channel, int flags);
+
+
+typedef enum
+{
+ STBIR_EDGE_CLAMP = 1,
+ STBIR_EDGE_REFLECT = 2,
+ STBIR_EDGE_WRAP = 3,
+ STBIR_EDGE_ZERO = 4,
+} stbir_edge;
+
+// This function adds the ability to specify how requests to sample off the edge of the image are handled.
+STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ int num_channels, int alpha_channel, int flags,
+ stbir_edge edge_wrap_mode);
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Medium-complexity API
+//
+// This extends the easy-to-use API as follows:
+//
+// * Alpha-channel can be processed separately
+// * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE
+// * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT)
+// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)
+// * Filter can be selected explicitly
+// * uint16 image type
+// * sRGB colorspace available for all types
+// * context parameter for passing to STBIR_MALLOC
+
+typedef enum
+{
+ STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses
+ STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios
+ STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering
+ STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque
+ STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline
+ STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3
+} stbir_filter;
+
+typedef enum
+{
+ STBIR_COLORSPACE_LINEAR,
+ STBIR_COLORSPACE_SRGB,
+
+ STBIR_MAX_COLORSPACES,
+} stbir_colorspace;
+
+// The following functions are all identical except for the type of the image data
+
+STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ int num_channels, int alpha_channel, int flags,
+ stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
+ void *alloc_context);
+
+STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes,
+ int num_channels, int alpha_channel, int flags,
+ stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
+ void *alloc_context);
+
+STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ float *output_pixels , int output_w, int output_h, int output_stride_in_bytes,
+ int num_channels, int alpha_channel, int flags,
+ stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
+ void *alloc_context);
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Full-complexity API
+//
+// This extends the medium API as follows:
+//
+// * uint32 image type
+// * not typesafe
+// * separate filter types for each axis
+// * separate edge modes for each axis
+// * can specify scale explicitly for subpixel correctness
+// * can specify image source tile using texture coordinates
+
+typedef enum
+{
+ STBIR_TYPE_UINT8 ,
+ STBIR_TYPE_UINT16,
+ STBIR_TYPE_UINT32,
+ STBIR_TYPE_FLOAT ,
+
+ STBIR_MAX_TYPES
+} stbir_datatype;
+
+STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ stbir_datatype datatype,
+ int num_channels, int alpha_channel, int flags,
+ stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
+ stbir_filter filter_horizontal, stbir_filter filter_vertical,
+ stbir_colorspace space, void *alloc_context);
+
+STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ stbir_datatype datatype,
+ int num_channels, int alpha_channel, int flags,
+ stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
+ stbir_filter filter_horizontal, stbir_filter filter_vertical,
+ stbir_colorspace space, void *alloc_context,
+ float x_scale, float y_scale,
+ float x_offset, float y_offset);
+
+STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ stbir_datatype datatype,
+ int num_channels, int alpha_channel, int flags,
+ stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
+ stbir_filter filter_horizontal, stbir_filter filter_vertical,
+ stbir_colorspace space, void *alloc_context,
+ float s0, float t0, float s1, float t1);
+// (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use.
+
+//
+//
+//// end header file /////////////////////////////////////////////////////
+#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H
+
+
+
+
+
+#ifdef STB_IMAGE_RESIZE_IMPLEMENTATION
+
+#ifndef STBIR_ASSERT
+#include <assert.h>
+#define STBIR_ASSERT(x) assert(x)
+#endif
+
+// For memset
+#include <string.h>
+
+#include <math.h>
+
+#ifndef STBIR_MALLOC
+#include <stdlib.h>
+// use comma operator to evaluate c, to avoid "unused parameter" warnings
+#define STBIR_MALLOC(size,c) ((void)(c), malloc(size))
+#define STBIR_FREE(ptr,c) ((void)(c), free(ptr))
+#endif
+
+#ifndef _MSC_VER
+#ifdef __cplusplus
+#define stbir__inline inline
+#else
+#define stbir__inline
+#endif
+#else
+#define stbir__inline __forceinline
+#endif
+
+
+// should produce compiler error if size is wrong
+typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1];
+
+#ifdef _MSC_VER
+#define STBIR__NOTUSED(v) (void)(v)
+#else
+#define STBIR__NOTUSED(v) (void)sizeof(v)
+#endif
+
+#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0]))
+
+#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE
+#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
+#endif
+
+#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE
+#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL
+#endif
+
+#ifndef STBIR_PROGRESS_REPORT
+#define STBIR_PROGRESS_REPORT(float_0_to_1)
+#endif
+
+#ifndef STBIR_MAX_CHANNELS
+#define STBIR_MAX_CHANNELS 64
+#endif
+
+#if STBIR_MAX_CHANNELS > 65536
+#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536."
+// because we store the indices in 16-bit variables
+#endif
+
+// This value is added to alpha just before premultiplication to avoid
+// zeroing out color values. It is equivalent to 2^-80. If you don't want
+// that behavior (it may interfere if you have floating point images with
+// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to
+// disable it.
+#ifndef STBIR_ALPHA_EPSILON
+#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20))
+#endif
+
+
+
+#ifdef _MSC_VER
+#define STBIR__UNUSED_PARAM(v) (void)(v)
+#else
+#define STBIR__UNUSED_PARAM(v) (void)sizeof(v)
+#endif
+
+// must match stbir_datatype
+static unsigned char stbir__type_size[] = {
+ 1, // STBIR_TYPE_UINT8
+ 2, // STBIR_TYPE_UINT16
+ 4, // STBIR_TYPE_UINT32
+ 4, // STBIR_TYPE_FLOAT
+};
+
+// Kernel function centered at 0
+typedef float (stbir__kernel_fn)(float x, float scale);
+typedef float (stbir__support_fn)(float scale);
+
+typedef struct
+{
+ stbir__kernel_fn* kernel;
+ stbir__support_fn* support;
+} stbir__filter_info;
+
+// When upsampling, the contributors are which source pixels contribute.
+// When downsampling, the contributors are which destination pixels are contributed to.
+typedef struct
+{
+ int n0; // First contributing pixel
+ int n1; // Last contributing pixel
+} stbir__contributors;
+
+typedef struct
+{
+ const void* input_data;
+ int input_w;
+ int input_h;
+ int input_stride_bytes;
+
+ void* output_data;
+ int output_w;
+ int output_h;
+ int output_stride_bytes;
+
+ float s0, t0, s1, t1;
+
+ float horizontal_shift; // Units: output pixels
+ float vertical_shift; // Units: output pixels
+ float horizontal_scale;
+ float vertical_scale;
+
+ int channels;
+ int alpha_channel;
+ stbir_uint32 flags;
+ stbir_datatype type;
+ stbir_filter horizontal_filter;
+ stbir_filter vertical_filter;
+ stbir_edge edge_horizontal;
+ stbir_edge edge_vertical;
+ stbir_colorspace colorspace;
+
+ stbir__contributors* horizontal_contributors;
+ float* horizontal_coefficients;
+
+ stbir__contributors* vertical_contributors;
+ float* vertical_coefficients;
+
+ int decode_buffer_pixels;
+ float* decode_buffer;
+
+ float* horizontal_buffer;
+
+ // cache these because ceil/floor are inexplicably showing up in profile
+ int horizontal_coefficient_width;
+ int vertical_coefficient_width;
+ int horizontal_filter_pixel_width;
+ int vertical_filter_pixel_width;
+ int horizontal_filter_pixel_margin;
+ int vertical_filter_pixel_margin;
+ int horizontal_num_contributors;
+ int vertical_num_contributors;
+
+ int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter)
+ int ring_buffer_num_entries; // Total number of entries in the ring buffer.
+ int ring_buffer_first_scanline;
+ int ring_buffer_last_scanline;
+ int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer
+ float* ring_buffer;
+
+ float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds.
+
+ int horizontal_contributors_size;
+ int horizontal_coefficients_size;
+ int vertical_contributors_size;
+ int vertical_coefficients_size;
+ int decode_buffer_size;
+ int horizontal_buffer_size;
+ int ring_buffer_size;
+ int encode_buffer_size;
+} stbir__info;
+
+
+static const float stbir__max_uint8_as_float = 255.0f;
+static const float stbir__max_uint16_as_float = 65535.0f;
+static const double stbir__max_uint32_as_float = 4294967295.0;
+
+
+static stbir__inline int stbir__min(int a, int b)
+{
+ return a < b ? a : b;
+}
+
+static stbir__inline float stbir__saturate(float x)
+{
+ if (x < 0)
+ return 0;
+
+ if (x > 1)
+ return 1;
+
+ return x;
+}
+
+#ifdef STBIR_SATURATE_INT
+static stbir__inline stbir_uint8 stbir__saturate8(int x)
+{
+ if ((unsigned int) x <= 255)
+ return x;
+
+ if (x < 0)
+ return 0;
+
+ return 255;
+}
+
+static stbir__inline stbir_uint16 stbir__saturate16(int x)
+{
+ if ((unsigned int) x <= 65535)
+ return x;
+
+ if (x < 0)
+ return 0;
+
+ return 65535;
+}
+#endif
+
+static float stbir__srgb_uchar_to_linear_float[256] = {
+ 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f,
+ 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f,
+ 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f,
+ 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f,
+ 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f,
+ 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f,
+ 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f,
+ 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f,
+ 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f,
+ 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f,
+ 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f,
+ 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f,
+ 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f,
+ 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f,
+ 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f,
+ 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f,
+ 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f,
+ 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f,
+ 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f,
+ 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f,
+ 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f,
+ 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f,
+ 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f,
+ 0.982251f, 0.991102f, 1.0f
+};
+
+static float stbir__srgb_to_linear(float f)
+{
+ if (f <= 0.04045f)
+ return f / 12.92f;
+ else
+ return (float)pow((f + 0.055f) / 1.055f, 2.4f);
+}
+
+static float stbir__linear_to_srgb(float f)
+{
+ if (f <= 0.0031308f)
+ return f * 12.92f;
+ else
+ return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f;
+}
+
+#ifndef STBIR_NON_IEEE_FLOAT
+// From https://gist.github.com/rygorous/2203834
+
+typedef union
+{
+ stbir_uint32 u;
+ float f;
+} stbir__FP32;
+
+static const stbir_uint32 fp32_to_srgb8_tab4[104] = {
+ 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
+ 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
+ 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
+ 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067,
+ 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5,
+ 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2,
+ 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143,
+ 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af,
+ 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240,
+ 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300,
+ 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
+ 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
+ 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
+};
+
+static stbir_uint8 stbir__linear_to_srgb_uchar(float in)
+{
+ static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps
+ static const stbir__FP32 minval = { (127-13) << 23 };
+ stbir_uint32 tab,bias,scale,t;
+ stbir__FP32 f;
+
+ // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively.
+ // The tests are carefully written so that NaNs map to 0, same as in the reference
+ // implementation.
+ if (!(in > minval.f)) // written this way to catch NaNs
+ in = minval.f;
+ if (in > almostone.f)
+ in = almostone.f;
+
+ // Do the table lookup and unpack bias, scale
+ f.f = in;
+ tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20];
+ bias = (tab >> 16) << 9;
+ scale = tab & 0xffff;
+
+ // Grab next-highest mantissa bits and perform linear interpolation
+ t = (f.u >> 12) & 0xff;
+ return (unsigned char) ((bias + scale*t) >> 16);
+}
+
+#else
+// sRGB transition values, scaled by 1<<28
+static int stbir__srgb_offset_to_linear_scaled[256] =
+{
+ 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603,
+ 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926,
+ 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148,
+ 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856,
+ 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731,
+ 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369,
+ 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021,
+ 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073,
+ 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389,
+ 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552,
+ 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066,
+ 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490,
+ 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568,
+ 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316,
+ 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096,
+ 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700,
+ 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376,
+ 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912,
+ 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648,
+ 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512,
+ 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072,
+ 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544,
+ 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832,
+ 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528,
+ 140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968,
+ 154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184,
+ 168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992,
+ 183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968,
+ 199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480,
+ 215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656,
+ 232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464,
+ 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664,
+};
+
+static stbir_uint8 stbir__linear_to_srgb_uchar(float f)
+{
+ int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp
+ int v = 0;
+ int i;
+
+ // Refine the guess with a short binary search.
+ i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+ i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+ i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+ i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+ i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+ i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+ i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+ i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+
+ return (stbir_uint8) v;
+}
+#endif
+
+static float stbir__filter_trapezoid(float x, float scale)
+{
+ float halfscale = scale / 2;
+ float t = 0.5f + halfscale;
+ STBIR_ASSERT(scale <= 1);
+
+ x = (float)fabs(x);
+
+ if (x >= t)
+ return 0;
+ else
+ {
+ float r = 0.5f - halfscale;
+ if (x <= r)
+ return 1;
+ else
+ return (t - x) / scale;
+ }
+}
+
+static float stbir__support_trapezoid(float scale)
+{
+ STBIR_ASSERT(scale <= 1);
+ return 0.5f + scale / 2;
+}
+
+static float stbir__filter_triangle(float x, float s)
+{
+ STBIR__UNUSED_PARAM(s);
+
+ x = (float)fabs(x);
+
+ if (x <= 1.0f)
+ return 1 - x;
+ else
+ return 0;
+}
+
+static float stbir__filter_cubic(float x, float s)
+{
+ STBIR__UNUSED_PARAM(s);
+
+ x = (float)fabs(x);
+
+ if (x < 1.0f)
+ return (4 + x*x*(3*x - 6))/6;
+ else if (x < 2.0f)
+ return (8 + x*(-12 + x*(6 - x)))/6;
+
+ return (0.0f);
+}
+
+static float stbir__filter_catmullrom(float x, float s)
+{
+ STBIR__UNUSED_PARAM(s);
+
+ x = (float)fabs(x);
+
+ if (x < 1.0f)
+ return 1 - x*x*(2.5f - 1.5f*x);
+ else if (x < 2.0f)
+ return 2 - x*(4 + x*(0.5f*x - 2.5f));
+
+ return (0.0f);
+}
+
+static float stbir__filter_mitchell(float x, float s)
+{
+ STBIR__UNUSED_PARAM(s);
+
+ x = (float)fabs(x);
+
+ if (x < 1.0f)
+ return (16 + x*x*(21 * x - 36))/18;
+ else if (x < 2.0f)
+ return (32 + x*(-60 + x*(36 - 7*x)))/18;
+
+ return (0.0f);
+}
+
+static float stbir__support_zero(float s)
+{
+ STBIR__UNUSED_PARAM(s);
+ return 0;
+}
+
+static float stbir__support_one(float s)
+{
+ STBIR__UNUSED_PARAM(s);
+ return 1;
+}
+
+static float stbir__support_two(float s)
+{
+ STBIR__UNUSED_PARAM(s);
+ return 2;
+}
+
+static stbir__filter_info stbir__filter_info_table[] = {
+ { NULL, stbir__support_zero },
+ { stbir__filter_trapezoid, stbir__support_trapezoid },
+ { stbir__filter_triangle, stbir__support_one },
+ { stbir__filter_cubic, stbir__support_two },
+ { stbir__filter_catmullrom, stbir__support_two },
+ { stbir__filter_mitchell, stbir__support_two },
+};
+
+stbir__inline static int stbir__use_upsampling(float ratio)
+{
+ return ratio > 1;
+}
+
+stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info)
+{
+ return stbir__use_upsampling(stbir_info->horizontal_scale);
+}
+
+stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info)
+{
+ return stbir__use_upsampling(stbir_info->vertical_scale);
+}
+
+// This is the maximum number of input samples that can affect an output sample
+// with the given filter
+static int stbir__get_filter_pixel_width(stbir_filter filter, float scale)
+{
+ STBIR_ASSERT(filter != 0);
+ STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table));
+
+ if (stbir__use_upsampling(scale))
+ return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2);
+ else
+ return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale);
+}
+
+// This is how much to expand buffers to account for filters seeking outside
+// the image boundaries.
+static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale)
+{
+ return stbir__get_filter_pixel_width(filter, scale) / 2;
+}
+
+static int stbir__get_coefficient_width(stbir_filter filter, float scale)
+{
+ if (stbir__use_upsampling(scale))
+ return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2);
+ else
+ return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2);
+}
+
+static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size)
+{
+ if (stbir__use_upsampling(scale))
+ return output_size;
+ else
+ return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2);
+}
+
+static int stbir__get_total_horizontal_coefficients(stbir__info* info)
+{
+ return info->horizontal_num_contributors
+ * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale);
+}
+
+static int stbir__get_total_vertical_coefficients(stbir__info* info)
+{
+ return info->vertical_num_contributors
+ * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale);
+}
+
+static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n)
+{
+ return &contributors[n];
+}
+
+// For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample,
+// if you change it here change it there too.
+static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c)
+{
+ int width = stbir__get_coefficient_width(filter, scale);
+ return &coefficients[width*n + c];
+}
+
+static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max)
+{
+ switch (edge)
+ {
+ case STBIR_EDGE_ZERO:
+ return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later
+
+ case STBIR_EDGE_CLAMP:
+ if (n < 0)
+ return 0;
+
+ if (n >= max)
+ return max - 1;
+
+ return n; // NOTREACHED
+
+ case STBIR_EDGE_REFLECT:
+ {
+ if (n < 0)
+ {
+ if (n < max)
+ return -n;
+ else
+ return max - 1;
+ }
+
+ if (n >= max)
+ {
+ int max2 = max * 2;
+ if (n >= max2)
+ return 0;
+ else
+ return max2 - n - 1;
+ }
+
+ return n; // NOTREACHED
+ }
+
+ case STBIR_EDGE_WRAP:
+ if (n >= 0)
+ return (n % max);
+ else
+ {
+ int m = (-n) % max;
+
+ if (m != 0)
+ m = max - m;
+
+ return (m);
+ }
+ // NOTREACHED
+
+ default:
+ STBIR_ASSERT(!"Unimplemented edge type");
+ return 0;
+ }
+}
+
+stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max)
+{
+ // avoid per-pixel switch
+ if (n >= 0 && n < max)
+ return n;
+ return stbir__edge_wrap_slow(edge, n, max);
+}
+
+// What input pixels contribute to this output pixel?
+static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out)
+{
+ float out_pixel_center = (float)n + 0.5f;
+ float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius;
+ float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius;
+
+ float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio;
+ float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio;
+
+ *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio;
+ *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5));
+ *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5));
+}
+
+// What output pixels does this input pixel contribute to?
+static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in)
+{
+ float in_pixel_center = (float)n + 0.5f;
+ float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius;
+ float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius;
+
+ float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift;
+ float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift;
+
+ *out_center_of_in = in_pixel_center * scale_ratio - out_shift;
+ *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5));
+ *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5));
+}
+
+static void stbir__calculate_coefficients_upsample(stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group)
+{
+ int i;
+ float total_filter = 0;
+ float filter_scale;
+
+ STBIR_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical.
+
+ contributor->n0 = in_first_pixel;
+ contributor->n1 = in_last_pixel;
+
+ STBIR_ASSERT(contributor->n1 >= contributor->n0);
+
+ for (i = 0; i <= in_last_pixel - in_first_pixel; i++)
+ {
+ float in_pixel_center = (float)(i + in_first_pixel) + 0.5f;
+ coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale);
+
+ // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.)
+ if (i == 0 && !coefficient_group[i])
+ {
+ contributor->n0 = ++in_first_pixel;
+ i--;
+ continue;
+ }
+
+ total_filter += coefficient_group[i];
+ }
+
+ // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be.
+ // It would be true in exact math but is at best approximately true in floating-point math,
+ // and it would not make sense to try and put actual bounds on this here because it depends
+ // on the image aspect ratio which can get pretty extreme.
+ //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0);
+
+ STBIR_ASSERT(total_filter > 0.9);
+ STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off.
+
+ // Make sure the sum of all coefficients is 1.
+ filter_scale = 1 / total_filter;
+
+ for (i = 0; i <= in_last_pixel - in_first_pixel; i++)
+ coefficient_group[i] *= filter_scale;
+
+ for (i = in_last_pixel - in_first_pixel; i >= 0; i--)
+ {
+ if (coefficient_group[i])
+ break;
+
+ // This line has no weight. We can skip it.
+ contributor->n1 = contributor->n0 + i - 1;
+ }
+}
+
+static void stbir__calculate_coefficients_downsample(stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group)
+{
+ int i;
+
+ STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical.
+
+ contributor->n0 = out_first_pixel;
+ contributor->n1 = out_last_pixel;
+
+ STBIR_ASSERT(contributor->n1 >= contributor->n0);
+
+ for (i = 0; i <= out_last_pixel - out_first_pixel; i++)
+ {
+ float out_pixel_center = (float)(i + out_first_pixel) + 0.5f;
+ float x = out_pixel_center - out_center_of_in;
+ coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio;
+ }
+
+ // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be.
+ // It would be true in exact math but is at best approximately true in floating-point math,
+ // and it would not make sense to try and put actual bounds on this here because it depends
+ // on the image aspect ratio which can get pretty extreme.
+ //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0);
+
+ for (i = out_last_pixel - out_first_pixel; i >= 0; i--)
+ {
+ if (coefficient_group[i])
+ break;
+
+ // This line has no weight. We can skip it.
+ contributor->n1 = contributor->n0 + i - 1;
+ }
+}
+
+static void stbir__normalize_downsample_coefficients(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, int input_size, int output_size)
+{
+ int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size);
+ int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio);
+ int i, j;
+ int skip;
+
+ for (i = 0; i < output_size; i++)
+ {
+ float scale;
+ float total = 0;
+
+ for (j = 0; j < num_contributors; j++)
+ {
+ if (i >= contributors[j].n0 && i <= contributors[j].n1)
+ {
+ float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0);
+ total += coefficient;
+ }
+ else if (i < contributors[j].n0)
+ break;
+ }
+
+ STBIR_ASSERT(total > 0.9f);
+ STBIR_ASSERT(total < 1.1f);
+
+ scale = 1 / total;
+
+ for (j = 0; j < num_contributors; j++)
+ {
+ if (i >= contributors[j].n0 && i <= contributors[j].n1)
+ *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale;
+ else if (i < contributors[j].n0)
+ break;
+ }
+ }
+
+ // Optimize: Skip zero coefficients and contributions outside of image bounds.
+ // Do this after normalizing because normalization depends on the n0/n1 values.
+ for (j = 0; j < num_contributors; j++)
+ {
+ int range, max, width;
+
+ skip = 0;
+ while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0)
+ skip++;
+
+ contributors[j].n0 += skip;
+
+ while (contributors[j].n0 < 0)
+ {
+ contributors[j].n0++;
+ skip++;
+ }
+
+ range = contributors[j].n1 - contributors[j].n0 + 1;
+ max = stbir__min(num_coefficients, range);
+
+ width = stbir__get_coefficient_width(filter, scale_ratio);
+ for (i = 0; i < max; i++)
+ {
+ if (i + skip >= width)
+ break;
+
+ *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip);
+ }
+
+ continue;
+ }
+
+ // Using min to avoid writing into invalid pixels.
+ for (i = 0; i < num_contributors; i++)
+ contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1);
+}
+
+// Each scan line uses the same kernel values so we should calculate the kernel
+// values once and then we can use them for every scan line.
+static void stbir__calculate_filters(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size)
+{
+ int n;
+ int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size);
+
+ if (stbir__use_upsampling(scale_ratio))
+ {
+ float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio;
+
+ // Looping through out pixels
+ for (n = 0; n < total_contributors; n++)
+ {
+ float in_center_of_out; // Center of the current out pixel in the in pixel space
+ int in_first_pixel, in_last_pixel;
+
+ stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out);
+
+ stbir__calculate_coefficients_upsample(filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0));
+ }
+ }
+ else
+ {
+ float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio;
+
+ // Looping through in pixels
+ for (n = 0; n < total_contributors; n++)
+ {
+ float out_center_of_in; // Center of the current out pixel in the in pixel space
+ int out_first_pixel, out_last_pixel;
+ int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio);
+
+ stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in);
+
+ stbir__calculate_coefficients_downsample(filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0));
+ }
+
+ stbir__normalize_downsample_coefficients(contributors, coefficients, filter, scale_ratio, input_size, output_size);
+ }
+}
+
+static float* stbir__get_decode_buffer(stbir__info* stbir_info)
+{
+ // The 0 index of the decode buffer starts after the margin. This makes
+ // it okay to use negative indexes on the decode buffer.
+ return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels];
+}
+
+#define STBIR__DECODE(type, colorspace) ((int)(type) * (STBIR_MAX_COLORSPACES) + (int)(colorspace))
+
+static void stbir__decode_scanline(stbir__info* stbir_info, int n)
+{
+ int c;
+ int channels = stbir_info->channels;
+ int alpha_channel = stbir_info->alpha_channel;
+ int type = stbir_info->type;
+ int colorspace = stbir_info->colorspace;
+ int input_w = stbir_info->input_w;
+ size_t input_stride_bytes = stbir_info->input_stride_bytes;
+ float* decode_buffer = stbir__get_decode_buffer(stbir_info);
+ stbir_edge edge_horizontal = stbir_info->edge_horizontal;
+ stbir_edge edge_vertical = stbir_info->edge_vertical;
+ size_t in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes;
+ const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset;
+ int max_x = input_w + stbir_info->horizontal_filter_pixel_margin;
+ int decode = STBIR__DECODE(type, colorspace);
+
+ int x = -stbir_info->horizontal_filter_pixel_margin;
+
+ // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input,
+ // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO
+ if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h))
+ {
+ for (; x < max_x; x++)
+ for (c = 0; c < channels; c++)
+ decode_buffer[x*channels + c] = 0;
+ return;
+ }
+
+ switch (decode)
+ {
+ case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR):
+ for (; x < max_x; x++)
+ {
+ int decode_pixel_index = x * channels;
+ int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
+ for (c = 0; c < channels; c++)
+ decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / stbir__max_uint8_as_float;
+ }
+ break;
+
+ case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB):
+ for (; x < max_x; x++)
+ {
+ int decode_pixel_index = x * channels;
+ int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
+ for (c = 0; c < channels; c++)
+ decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]];
+
+ if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
+ decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint8_as_float;
+ }
+ break;
+
+ case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR):
+ for (; x < max_x; x++)
+ {
+ int decode_pixel_index = x * channels;
+ int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
+ for (c = 0; c < channels; c++)
+ decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float;
+ }
+ break;
+
+ case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB):
+ for (; x < max_x; x++)
+ {
+ int decode_pixel_index = x * channels;
+ int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
+ for (c = 0; c < channels; c++)
+ decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float);
+
+ if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
+ decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint16_as_float;
+ }
+ break;
+
+ case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR):
+ for (; x < max_x; x++)
+ {
+ int decode_pixel_index = x * channels;
+ int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
+ for (c = 0; c < channels; c++)
+ decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float);
+ }
+ break;
+
+ case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB):
+ for (; x < max_x; x++)
+ {
+ int decode_pixel_index = x * channels;
+ int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
+ for (c = 0; c < channels; c++)
+ decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float));
+
+ if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
+ decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint32_as_float);
+ }
+ break;
+
+ case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR):
+ for (; x < max_x; x++)
+ {
+ int decode_pixel_index = x * channels;
+ int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
+ for (c = 0; c < channels; c++)
+ decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c];
+ }
+ break;
+
+ case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB):
+ for (; x < max_x; x++)
+ {
+ int decode_pixel_index = x * channels;
+ int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
+ for (c = 0; c < channels; c++)
+ decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]);
+
+ if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
+ decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel];
+ }
+
+ break;
+
+ default:
+ STBIR_ASSERT(!"Unknown type/colorspace/channels combination.");
+ break;
+ }
+
+ if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED))
+ {
+ for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++)
+ {
+ int decode_pixel_index = x * channels;
+
+ // If the alpha value is 0 it will clobber the color values. Make sure it's not.
+ float alpha = decode_buffer[decode_pixel_index + alpha_channel];
+#ifndef STBIR_NO_ALPHA_EPSILON
+ if (stbir_info->type != STBIR_TYPE_FLOAT) {
+ alpha += STBIR_ALPHA_EPSILON;
+ decode_buffer[decode_pixel_index + alpha_channel] = alpha;
+ }
+#endif
+ for (c = 0; c < channels; c++)
+ {
+ if (c == alpha_channel)
+ continue;
+
+ decode_buffer[decode_pixel_index + c] *= alpha;
+ }
+ }
+ }
+
+ if (edge_horizontal == STBIR_EDGE_ZERO)
+ {
+ for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++)
+ {
+ for (c = 0; c < channels; c++)
+ decode_buffer[x*channels + c] = 0;
+ }
+ for (x = input_w; x < max_x; x++)
+ {
+ for (c = 0; c < channels; c++)
+ decode_buffer[x*channels + c] = 0;
+ }
+ }
+}
+
+static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length)
+{
+ return &ring_buffer[index * ring_buffer_length];
+}
+
+static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n)
+{
+ int ring_buffer_index;
+ float* ring_buffer;
+
+ stbir_info->ring_buffer_last_scanline = n;
+
+ if (stbir_info->ring_buffer_begin_index < 0)
+ {
+ ring_buffer_index = stbir_info->ring_buffer_begin_index = 0;
+ stbir_info->ring_buffer_first_scanline = n;
+ }
+ else
+ {
+ ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries;
+ STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index);
+ }
+
+ ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float));
+ memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes);
+
+ return ring_buffer;
+}
+
+
+static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, float* output_buffer)
+{
+ int x, k;
+ int output_w = stbir_info->output_w;
+ int channels = stbir_info->channels;
+ float* decode_buffer = stbir__get_decode_buffer(stbir_info);
+ stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors;
+ float* horizontal_coefficients = stbir_info->horizontal_coefficients;
+ int coefficient_width = stbir_info->horizontal_coefficient_width;
+
+ for (x = 0; x < output_w; x++)
+ {
+ int n0 = horizontal_contributors[x].n0;
+ int n1 = horizontal_contributors[x].n1;
+
+ int out_pixel_index = x * channels;
+ int coefficient_group = coefficient_width * x;
+ int coefficient_counter = 0;
+
+ STBIR_ASSERT(n1 >= n0);
+ STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin);
+ STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin);
+ STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin);
+ STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin);
+
+ switch (channels) {
+ case 1:
+ for (k = n0; k <= n1; k++)
+ {
+ int in_pixel_index = k * 1;
+ float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
+ STBIR_ASSERT(coefficient != 0);
+ output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
+ }
+ break;
+ case 2:
+ for (k = n0; k <= n1; k++)
+ {
+ int in_pixel_index = k * 2;
+ float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
+ STBIR_ASSERT(coefficient != 0);
+ output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
+ output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
+ }
+ break;
+ case 3:
+ for (k = n0; k <= n1; k++)
+ {
+ int in_pixel_index = k * 3;
+ float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
+ STBIR_ASSERT(coefficient != 0);
+ output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
+ output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
+ output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
+ }
+ break;
+ case 4:
+ for (k = n0; k <= n1; k++)
+ {
+ int in_pixel_index = k * 4;
+ float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
+ STBIR_ASSERT(coefficient != 0);
+ output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
+ output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
+ output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
+ output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient;
+ }
+ break;
+ default:
+ for (k = n0; k <= n1; k++)
+ {
+ int in_pixel_index = k * channels;
+ float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
+ int c;
+ STBIR_ASSERT(coefficient != 0);
+ for (c = 0; c < channels; c++)
+ output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient;
+ }
+ break;
+ }
+ }
+}
+
+static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float* output_buffer)
+{
+ int x, k;
+ int input_w = stbir_info->input_w;
+ int channels = stbir_info->channels;
+ float* decode_buffer = stbir__get_decode_buffer(stbir_info);
+ stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors;
+ float* horizontal_coefficients = stbir_info->horizontal_coefficients;
+ int coefficient_width = stbir_info->horizontal_coefficient_width;
+ int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin;
+ int max_x = input_w + filter_pixel_margin * 2;
+
+ STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info));
+
+ switch (channels) {
+ case 1:
+ for (x = 0; x < max_x; x++)
+ {
+ int n0 = horizontal_contributors[x].n0;
+ int n1 = horizontal_contributors[x].n1;
+
+ int in_x = x - filter_pixel_margin;
+ int in_pixel_index = in_x * 1;
+ int max_n = n1;
+ int coefficient_group = coefficient_width * x;
+
+ for (k = n0; k <= max_n; k++)
+ {
+ int out_pixel_index = k * 1;
+ float coefficient = horizontal_coefficients[coefficient_group + k - n0];
+ output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
+ }
+ }
+ break;
+
+ case 2:
+ for (x = 0; x < max_x; x++)
+ {
+ int n0 = horizontal_contributors[x].n0;
+ int n1 = horizontal_contributors[x].n1;
+
+ int in_x = x - filter_pixel_margin;
+ int in_pixel_index = in_x * 2;
+ int max_n = n1;
+ int coefficient_group = coefficient_width * x;
+
+ for (k = n0; k <= max_n; k++)
+ {
+ int out_pixel_index = k * 2;
+ float coefficient = horizontal_coefficients[coefficient_group + k - n0];
+ output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
+ output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
+ }
+ }
+ break;
+
+ case 3:
+ for (x = 0; x < max_x; x++)
+ {
+ int n0 = horizontal_contributors[x].n0;
+ int n1 = horizontal_contributors[x].n1;
+
+ int in_x = x - filter_pixel_margin;
+ int in_pixel_index = in_x * 3;
+ int max_n = n1;
+ int coefficient_group = coefficient_width * x;
+
+ for (k = n0; k <= max_n; k++)
+ {
+ int out_pixel_index = k * 3;
+ float coefficient = horizontal_coefficients[coefficient_group + k - n0];
+ output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
+ output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
+ output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
+ }
+ }
+ break;
+
+ case 4:
+ for (x = 0; x < max_x; x++)
+ {
+ int n0 = horizontal_contributors[x].n0;
+ int n1 = horizontal_contributors[x].n1;
+
+ int in_x = x - filter_pixel_margin;
+ int in_pixel_index = in_x * 4;
+ int max_n = n1;
+ int coefficient_group = coefficient_width * x;
+
+ for (k = n0; k <= max_n; k++)
+ {
+ int out_pixel_index = k * 4;
+ float coefficient = horizontal_coefficients[coefficient_group + k - n0];
+ output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
+ output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
+ output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
+ output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient;
+ }
+ }
+ break;
+
+ default:
+ for (x = 0; x < max_x; x++)
+ {
+ int n0 = horizontal_contributors[x].n0;
+ int n1 = horizontal_contributors[x].n1;
+
+ int in_x = x - filter_pixel_margin;
+ int in_pixel_index = in_x * channels;
+ int max_n = n1;
+ int coefficient_group = coefficient_width * x;
+
+ for (k = n0; k <= max_n; k++)
+ {
+ int c;
+ int out_pixel_index = k * channels;
+ float coefficient = horizontal_coefficients[coefficient_group + k - n0];
+ for (c = 0; c < channels; c++)
+ output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient;
+ }
+ }
+ break;
+ }
+}
+
+static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n)
+{
+ // Decode the nth scanline from the source image into the decode buffer.
+ stbir__decode_scanline(stbir_info, n);
+
+ // Now resample it into the ring buffer.
+ if (stbir__use_width_upsampling(stbir_info))
+ stbir__resample_horizontal_upsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n));
+ else
+ stbir__resample_horizontal_downsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n));
+
+ // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling.
+}
+
+static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n)
+{
+ // Decode the nth scanline from the source image into the decode buffer.
+ stbir__decode_scanline(stbir_info, n);
+
+ memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float));
+
+ // Now resample it into the horizontal buffer.
+ if (stbir__use_width_upsampling(stbir_info))
+ stbir__resample_horizontal_upsample(stbir_info, stbir_info->horizontal_buffer);
+ else
+ stbir__resample_horizontal_downsample(stbir_info, stbir_info->horizontal_buffer);
+
+ // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers.
+}
+
+// Get the specified scan line from the ring buffer.
+static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_num_entries, int ring_buffer_length)
+{
+ int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_num_entries;
+ return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length);
+}
+
+
+static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode)
+{
+ int x;
+ int n;
+ int num_nonalpha;
+ stbir_uint16 nonalpha[STBIR_MAX_CHANNELS];
+
+ if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED))
+ {
+ for (x=0; x < num_pixels; ++x)
+ {
+ int pixel_index = x*channels;
+
+ float alpha = encode_buffer[pixel_index + alpha_channel];
+ float reciprocal_alpha = alpha ? 1.0f / alpha : 0;
+
+ // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb
+ for (n = 0; n < channels; n++)
+ if (n != alpha_channel)
+ encode_buffer[pixel_index + n] *= reciprocal_alpha;
+
+ // We added in a small epsilon to prevent the color channel from being deleted with zero alpha.
+ // Because we only add it for integer types, it will automatically be discarded on integer
+ // conversion, so we don't need to subtract it back out (which would be problematic for
+ // numeric precision reasons).
+ }
+ }
+
+ // build a table of all channels that need colorspace correction, so
+ // we don't perform colorspace correction on channels that don't need it.
+ for (x = 0, num_nonalpha = 0; x < channels; ++x)
+ {
+ if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE))
+ {
+ nonalpha[num_nonalpha++] = (stbir_uint16)x;
+ }
+ }
+
+ #define STBIR__ROUND_INT(f) ((int) ((f)+0.5))
+ #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5))
+
+ #ifdef STBIR__SATURATE_INT
+ #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * stbir__max_uint8_as_float ))
+ #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * stbir__max_uint16_as_float))
+ #else
+ #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint8_as_float )
+ #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint16_as_float)
+ #endif
+
+ switch (decode)
+ {
+ case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR):
+ for (x=0; x < num_pixels; ++x)
+ {
+ int pixel_index = x*channels;
+
+ for (n = 0; n < channels; n++)
+ {
+ int index = pixel_index + n;
+ ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]);
+ }
+ }
+ break;
+
+ case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB):
+ for (x=0; x < num_pixels; ++x)
+ {
+ int pixel_index = x*channels;
+
+ for (n = 0; n < num_nonalpha; n++)
+ {
+ int index = pixel_index + nonalpha[n];
+ ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]);
+ }
+
+ if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE))
+ ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]);
+ }
+ break;
+
+ case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR):
+ for (x=0; x < num_pixels; ++x)
+ {
+ int pixel_index = x*channels;
+
+ for (n = 0; n < channels; n++)
+ {
+ int index = pixel_index + n;
+ ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]);
+ }
+ }
+ break;
+
+ case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB):
+ for (x=0; x < num_pixels; ++x)
+ {
+ int pixel_index = x*channels;
+
+ for (n = 0; n < num_nonalpha; n++)
+ {
+ int index = pixel_index + nonalpha[n];
+ ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * stbir__max_uint16_as_float);
+ }
+
+ if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
+ ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]);
+ }
+
+ break;
+
+ case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR):
+ for (x=0; x < num_pixels; ++x)
+ {
+ int pixel_index = x*channels;
+
+ for (n = 0; n < channels; n++)
+ {
+ int index = pixel_index + n;
+ ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * stbir__max_uint32_as_float);
+ }
+ }
+ break;
+
+ case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB):
+ for (x=0; x < num_pixels; ++x)
+ {
+ int pixel_index = x*channels;
+
+ for (n = 0; n < num_nonalpha; n++)
+ {
+ int index = pixel_index + nonalpha[n];
+ ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * stbir__max_uint32_as_float);
+ }
+
+ if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
+ ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * stbir__max_uint32_as_float);
+ }
+ break;
+
+ case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR):
+ for (x=0; x < num_pixels; ++x)
+ {
+ int pixel_index = x*channels;
+
+ for (n = 0; n < channels; n++)
+ {
+ int index = pixel_index + n;
+ ((float*)output_buffer)[index] = encode_buffer[index];
+ }
+ }
+ break;
+
+ case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB):
+ for (x=0; x < num_pixels; ++x)
+ {
+ int pixel_index = x*channels;
+
+ for (n = 0; n < num_nonalpha; n++)
+ {
+ int index = pixel_index + nonalpha[n];
+ ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]);
+ }
+
+ if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
+ ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel];
+ }
+ break;
+
+ default:
+ STBIR_ASSERT(!"Unknown type/colorspace/channels combination.");
+ break;
+ }
+}
+
+static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n)
+{
+ int x, k;
+ int output_w = stbir_info->output_w;
+ stbir__contributors* vertical_contributors = stbir_info->vertical_contributors;
+ float* vertical_coefficients = stbir_info->vertical_coefficients;
+ int channels = stbir_info->channels;
+ int alpha_channel = stbir_info->alpha_channel;
+ int type = stbir_info->type;
+ int colorspace = stbir_info->colorspace;
+ int ring_buffer_entries = stbir_info->ring_buffer_num_entries;
+ void* output_data = stbir_info->output_data;
+ float* encode_buffer = stbir_info->encode_buffer;
+ int decode = STBIR__DECODE(type, colorspace);
+ int coefficient_width = stbir_info->vertical_coefficient_width;
+ int coefficient_counter;
+ int contributor = n;
+
+ float* ring_buffer = stbir_info->ring_buffer;
+ int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index;
+ int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline;
+ int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float);
+
+ int n0,n1, output_row_start;
+ int coefficient_group = coefficient_width * contributor;
+
+ n0 = vertical_contributors[contributor].n0;
+ n1 = vertical_contributors[contributor].n1;
+
+ output_row_start = n * stbir_info->output_stride_bytes;
+
+ STBIR_ASSERT(stbir__use_height_upsampling(stbir_info));
+
+ memset(encode_buffer, 0, output_w * sizeof(float) * channels);
+
+ // I tried reblocking this for better cache usage of encode_buffer
+ // (using x_outer, k, x_inner), but it lost speed. -- stb
+
+ coefficient_counter = 0;
+ switch (channels) {
+ case 1:
+ for (k = n0; k <= n1; k++)
+ {
+ int coefficient_index = coefficient_counter++;
+ float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
+ float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
+ for (x = 0; x < output_w; ++x)
+ {
+ int in_pixel_index = x * 1;
+ encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient;
+ }
+ }
+ break;
+ case 2:
+ for (k = n0; k <= n1; k++)
+ {
+ int coefficient_index = coefficient_counter++;
+ float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
+ float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
+ for (x = 0; x < output_w; ++x)
+ {
+ int in_pixel_index = x * 2;
+ encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient;
+ encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient;
+ }
+ }
+ break;
+ case 3:
+ for (k = n0; k <= n1; k++)
+ {
+ int coefficient_index = coefficient_counter++;
+ float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
+ float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
+ for (x = 0; x < output_w; ++x)
+ {
+ int in_pixel_index = x * 3;
+ encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient;
+ encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient;
+ encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient;
+ }
+ }
+ break;
+ case 4:
+ for (k = n0; k <= n1; k++)
+ {
+ int coefficient_index = coefficient_counter++;
+ float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
+ float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
+ for (x = 0; x < output_w; ++x)
+ {
+ int in_pixel_index = x * 4;
+ encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient;
+ encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient;
+ encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient;
+ encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient;
+ }
+ }
+ break;
+ default:
+ for (k = n0; k <= n1; k++)
+ {
+ int coefficient_index = coefficient_counter++;
+ float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
+ float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
+ for (x = 0; x < output_w; ++x)
+ {
+ int in_pixel_index = x * channels;
+ int c;
+ for (c = 0; c < channels; c++)
+ encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient;
+ }
+ }
+ break;
+ }
+ stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode);
+}
+
+static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n)
+{
+ int x, k;
+ int output_w = stbir_info->output_w;
+ stbir__contributors* vertical_contributors = stbir_info->vertical_contributors;
+ float* vertical_coefficients = stbir_info->vertical_coefficients;
+ int channels = stbir_info->channels;
+ int ring_buffer_entries = stbir_info->ring_buffer_num_entries;
+ float* horizontal_buffer = stbir_info->horizontal_buffer;
+ int coefficient_width = stbir_info->vertical_coefficient_width;
+ int contributor = n + stbir_info->vertical_filter_pixel_margin;
+
+ float* ring_buffer = stbir_info->ring_buffer;
+ int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index;
+ int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline;
+ int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float);
+ int n0,n1;
+
+ n0 = vertical_contributors[contributor].n0;
+ n1 = vertical_contributors[contributor].n1;
+
+ STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info));
+
+ for (k = n0; k <= n1; k++)
+ {
+ int coefficient_index = k - n0;
+ int coefficient_group = coefficient_width * contributor;
+ float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
+
+ float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
+
+ switch (channels) {
+ case 1:
+ for (x = 0; x < output_w; x++)
+ {
+ int in_pixel_index = x * 1;
+ ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient;
+ }
+ break;
+ case 2:
+ for (x = 0; x < output_w; x++)
+ {
+ int in_pixel_index = x * 2;
+ ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient;
+ ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient;
+ }
+ break;
+ case 3:
+ for (x = 0; x < output_w; x++)
+ {
+ int in_pixel_index = x * 3;
+ ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient;
+ ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient;
+ ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient;
+ }
+ break;
+ case 4:
+ for (x = 0; x < output_w; x++)
+ {
+ int in_pixel_index = x * 4;
+ ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient;
+ ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient;
+ ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient;
+ ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient;
+ }
+ break;
+ default:
+ for (x = 0; x < output_w; x++)
+ {
+ int in_pixel_index = x * channels;
+
+ int c;
+ for (c = 0; c < channels; c++)
+ ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient;
+ }
+ break;
+ }
+ }
+}
+
+static void stbir__buffer_loop_upsample(stbir__info* stbir_info)
+{
+ int y;
+ float scale_ratio = stbir_info->vertical_scale;
+ float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio;
+
+ STBIR_ASSERT(stbir__use_height_upsampling(stbir_info));
+
+ for (y = 0; y < stbir_info->output_h; y++)
+ {
+ float in_center_of_out = 0; // Center of the current out scanline in the in scanline space
+ int in_first_scanline = 0, in_last_scanline = 0;
+
+ stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out);
+
+ STBIR_ASSERT(in_last_scanline - in_first_scanline + 1 <= stbir_info->ring_buffer_num_entries);
+
+ if (stbir_info->ring_buffer_begin_index >= 0)
+ {
+ // Get rid of whatever we don't need anymore.
+ while (in_first_scanline > stbir_info->ring_buffer_first_scanline)
+ {
+ if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline)
+ {
+ // We just popped the last scanline off the ring buffer.
+ // Reset it to the empty state.
+ stbir_info->ring_buffer_begin_index = -1;
+ stbir_info->ring_buffer_first_scanline = 0;
+ stbir_info->ring_buffer_last_scanline = 0;
+ break;
+ }
+ else
+ {
+ stbir_info->ring_buffer_first_scanline++;
+ stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries;
+ }
+ }
+ }
+
+ // Load in new ones.
+ if (stbir_info->ring_buffer_begin_index < 0)
+ stbir__decode_and_resample_upsample(stbir_info, in_first_scanline);
+
+ while (in_last_scanline > stbir_info->ring_buffer_last_scanline)
+ stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1);
+
+ // Now all buffers should be ready to write a row of vertical sampling.
+ stbir__resample_vertical_upsample(stbir_info, y);
+
+ STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h);
+ }
+}
+
+static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline)
+{
+ int output_stride_bytes = stbir_info->output_stride_bytes;
+ int channels = stbir_info->channels;
+ int alpha_channel = stbir_info->alpha_channel;
+ int type = stbir_info->type;
+ int colorspace = stbir_info->colorspace;
+ int output_w = stbir_info->output_w;
+ void* output_data = stbir_info->output_data;
+ int decode = STBIR__DECODE(type, colorspace);
+
+ float* ring_buffer = stbir_info->ring_buffer;
+ int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float);
+
+ if (stbir_info->ring_buffer_begin_index >= 0)
+ {
+ // Get rid of whatever we don't need anymore.
+ while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline)
+ {
+ if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h)
+ {
+ int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes;
+ float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length);
+ stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode);
+ STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h);
+ }
+
+ if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline)
+ {
+ // We just popped the last scanline off the ring buffer.
+ // Reset it to the empty state.
+ stbir_info->ring_buffer_begin_index = -1;
+ stbir_info->ring_buffer_first_scanline = 0;
+ stbir_info->ring_buffer_last_scanline = 0;
+ break;
+ }
+ else
+ {
+ stbir_info->ring_buffer_first_scanline++;
+ stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries;
+ }
+ }
+ }
+}
+
+static void stbir__buffer_loop_downsample(stbir__info* stbir_info)
+{
+ int y;
+ float scale_ratio = stbir_info->vertical_scale;
+ int output_h = stbir_info->output_h;
+ float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio;
+ int pixel_margin = stbir_info->vertical_filter_pixel_margin;
+ int max_y = stbir_info->input_h + pixel_margin;
+
+ STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info));
+
+ for (y = -pixel_margin; y < max_y; y++)
+ {
+ float out_center_of_in; // Center of the current out scanline in the in scanline space
+ int out_first_scanline, out_last_scanline;
+
+ stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in);
+
+ STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries);
+
+ if (out_last_scanline < 0 || out_first_scanline >= output_h)
+ continue;
+
+ stbir__empty_ring_buffer(stbir_info, out_first_scanline);
+
+ stbir__decode_and_resample_downsample(stbir_info, y);
+
+ // Load in new ones.
+ if (stbir_info->ring_buffer_begin_index < 0)
+ stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline);
+
+ while (out_last_scanline > stbir_info->ring_buffer_last_scanline)
+ stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1);
+
+ // Now the horizontal buffer is ready to write to all ring buffer rows.
+ stbir__resample_vertical_downsample(stbir_info, y);
+ }
+
+ stbir__empty_ring_buffer(stbir_info, stbir_info->output_h);
+}
+
+static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels)
+{
+ info->input_w = input_w;
+ info->input_h = input_h;
+ info->output_w = output_w;
+ info->output_h = output_h;
+ info->channels = channels;
+}
+
+static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform)
+{
+ info->s0 = s0;
+ info->t0 = t0;
+ info->s1 = s1;
+ info->t1 = t1;
+
+ if (transform)
+ {
+ info->horizontal_scale = transform[0];
+ info->vertical_scale = transform[1];
+ info->horizontal_shift = transform[2];
+ info->vertical_shift = transform[3];
+ }
+ else
+ {
+ info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0);
+ info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0);
+
+ info->horizontal_shift = s0 * info->output_w / (s1 - s0);
+ info->vertical_shift = t0 * info->output_h / (t1 - t0);
+ }
+}
+
+static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter)
+{
+ if (h_filter == 0)
+ h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE;
+ if (v_filter == 0)
+ v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE;
+ info->horizontal_filter = h_filter;
+ info->vertical_filter = v_filter;
+}
+
+static stbir_uint32 stbir__calculate_memory(stbir__info *info)
+{
+ int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale);
+ int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale);
+
+ info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w);
+ info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h);
+
+ // One extra entry because floating point precision problems sometimes cause an extra to be necessary.
+ info->ring_buffer_num_entries = filter_height + 1;
+
+ info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors);
+ info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float);
+ info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors);
+ info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float);
+ info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float);
+ info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float);
+ info->ring_buffer_size = info->output_w * info->channels * info->ring_buffer_num_entries * sizeof(float);
+ info->encode_buffer_size = info->output_w * info->channels * sizeof(float);
+
+ STBIR_ASSERT(info->horizontal_filter != 0);
+ STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late
+ STBIR_ASSERT(info->vertical_filter != 0);
+ STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late
+
+ if (stbir__use_height_upsampling(info))
+ // The horizontal buffer is for when we're downsampling the height and we
+ // can't output the result of sampling the decode buffer directly into the
+ // ring buffers.
+ info->horizontal_buffer_size = 0;
+ else
+ // The encode buffer is to retain precision in the height upsampling method
+ // and isn't used when height downsampling.
+ info->encode_buffer_size = 0;
+
+ return info->horizontal_contributors_size + info->horizontal_coefficients_size
+ + info->vertical_contributors_size + info->vertical_coefficients_size
+ + info->decode_buffer_size + info->horizontal_buffer_size
+ + info->ring_buffer_size + info->encode_buffer_size;
+}
+
+static int stbir__resize_allocated(stbir__info *info,
+ const void* input_data, int input_stride_in_bytes,
+ void* output_data, int output_stride_in_bytes,
+ int alpha_channel, stbir_uint32 flags, stbir_datatype type,
+ stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace,
+ void* tempmem, size_t tempmem_size_in_bytes)
+{
+ size_t memory_required = stbir__calculate_memory(info);
+
+ int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type];
+ int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type];
+
+#ifdef STBIR_DEBUG_OVERWRITE_TEST
+#define OVERWRITE_ARRAY_SIZE 8
+ unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE];
+ unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE];
+ unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE];
+ unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE];
+
+ size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type];
+ memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE);
+ memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE);
+ memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE);
+ memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE);
+#endif
+
+ STBIR_ASSERT(info->channels >= 0);
+ STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS);
+
+ if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS)
+ return 0;
+
+ STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table));
+ STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table));
+
+ if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table))
+ return 0;
+ if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table))
+ return 0;
+
+ if (alpha_channel < 0)
+ flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED;
+
+ if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) {
+ STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels);
+ }
+
+ if (alpha_channel >= info->channels)
+ return 0;
+
+ STBIR_ASSERT(tempmem);
+
+ if (!tempmem)
+ return 0;
+
+ STBIR_ASSERT(tempmem_size_in_bytes >= memory_required);
+
+ if (tempmem_size_in_bytes < memory_required)
+ return 0;
+
+ memset(tempmem, 0, tempmem_size_in_bytes);
+
+ info->input_data = input_data;
+ info->input_stride_bytes = width_stride_input;
+
+ info->output_data = output_data;
+ info->output_stride_bytes = width_stride_output;
+
+ info->alpha_channel = alpha_channel;
+ info->flags = flags;
+ info->type = type;
+ info->edge_horizontal = edge_horizontal;
+ info->edge_vertical = edge_vertical;
+ info->colorspace = colorspace;
+
+ info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale);
+ info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale );
+ info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale);
+ info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale );
+ info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale);
+ info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale );
+
+ info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float);
+ info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2;
+
+#define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size)
+
+ info->horizontal_contributors = (stbir__contributors *) tempmem;
+ info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float);
+ info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors);
+ info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float);
+ info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float);
+
+ if (stbir__use_height_upsampling(info))
+ {
+ info->horizontal_buffer = NULL;
+ info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float);
+ info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float);
+
+ STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes);
+ }
+ else
+ {
+ info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float);
+ info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float);
+ info->encode_buffer = NULL;
+
+ STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes);
+ }
+
+#undef STBIR__NEXT_MEMPTR
+
+ // This signals that the ring buffer is empty
+ info->ring_buffer_begin_index = -1;
+
+ stbir__calculate_filters(info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w);
+ stbir__calculate_filters(info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h);
+
+ STBIR_PROGRESS_REPORT(0);
+
+ if (stbir__use_height_upsampling(info))
+ stbir__buffer_loop_upsample(info);
+ else
+ stbir__buffer_loop_downsample(info);
+
+ STBIR_PROGRESS_REPORT(1);
+
+#ifdef STBIR_DEBUG_OVERWRITE_TEST
+ STBIR_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0);
+ STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0);
+ STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0);
+ STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0);
+#endif
+
+ return 1;
+}
+
+
+static int stbir__resize_arbitrary(
+ void *alloc_context,
+ const void* input_data, int input_w, int input_h, int input_stride_in_bytes,
+ void* output_data, int output_w, int output_h, int output_stride_in_bytes,
+ float s0, float t0, float s1, float t1, float *transform,
+ int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type,
+ stbir_filter h_filter, stbir_filter v_filter,
+ stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace)
+{
+ stbir__info info;
+ int result;
+ size_t memory_required;
+ void* extra_memory;
+
+ stbir__setup(&info, input_w, input_h, output_w, output_h, channels);
+ stbir__calculate_transform(&info, s0,t0,s1,t1,transform);
+ stbir__choose_filter(&info, h_filter, v_filter);
+ memory_required = stbir__calculate_memory(&info);
+ extra_memory = STBIR_MALLOC(memory_required, alloc_context);
+
+ if (!extra_memory)
+ return 0;
+
+ result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes,
+ output_data, output_stride_in_bytes,
+ alpha_channel, flags, type,
+ edge_horizontal, edge_vertical,
+ colorspace, extra_memory, memory_required);
+
+ STBIR_FREE(extra_memory, alloc_context);
+
+ return result;
+}
+
+STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ int num_channels)
+{
+ return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes,
+ output_pixels, output_w, output_h, output_stride_in_bytes,
+ 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT,
+ STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR);
+}
+
+STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ float *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ int num_channels)
+{
+ return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes,
+ output_pixels, output_w, output_h, output_stride_in_bytes,
+ 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT,
+ STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR);
+}
+
+STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ int num_channels, int alpha_channel, int flags)
+{
+ return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes,
+ output_pixels, output_w, output_h, output_stride_in_bytes,
+ 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT,
+ STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB);
+}
+
+STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ int num_channels, int alpha_channel, int flags,
+ stbir_edge edge_wrap_mode)
+{
+ return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes,
+ output_pixels, output_w, output_h, output_stride_in_bytes,
+ 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT,
+ edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB);
+}
+
+STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ int num_channels, int alpha_channel, int flags,
+ stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
+ void *alloc_context)
+{
+ return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
+ output_pixels, output_w, output_h, output_stride_in_bytes,
+ 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter,
+ edge_wrap_mode, edge_wrap_mode, space);
+}
+
+STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes,
+ int num_channels, int alpha_channel, int flags,
+ stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
+ void *alloc_context)
+{
+ return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
+ output_pixels, output_w, output_h, output_stride_in_bytes,
+ 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter,
+ edge_wrap_mode, edge_wrap_mode, space);
+}
+
+
+STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ float *output_pixels , int output_w, int output_h, int output_stride_in_bytes,
+ int num_channels, int alpha_channel, int flags,
+ stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
+ void *alloc_context)
+{
+ return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
+ output_pixels, output_w, output_h, output_stride_in_bytes,
+ 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter,
+ edge_wrap_mode, edge_wrap_mode, space);
+}
+
+
+STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ stbir_datatype datatype,
+ int num_channels, int alpha_channel, int flags,
+ stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
+ stbir_filter filter_horizontal, stbir_filter filter_vertical,
+ stbir_colorspace space, void *alloc_context)
+{
+ return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
+ output_pixels, output_w, output_h, output_stride_in_bytes,
+ 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical,
+ edge_mode_horizontal, edge_mode_vertical, space);
+}
+
+
+STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ stbir_datatype datatype,
+ int num_channels, int alpha_channel, int flags,
+ stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
+ stbir_filter filter_horizontal, stbir_filter filter_vertical,
+ stbir_colorspace space, void *alloc_context,
+ float x_scale, float y_scale,
+ float x_offset, float y_offset)
+{
+ float transform[4];
+ transform[0] = x_scale;
+ transform[1] = y_scale;
+ transform[2] = x_offset;
+ transform[3] = y_offset;
+ return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
+ output_pixels, output_w, output_h, output_stride_in_bytes,
+ 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical,
+ edge_mode_horizontal, edge_mode_vertical, space);
+}
+
+STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
+ void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
+ stbir_datatype datatype,
+ int num_channels, int alpha_channel, int flags,
+ stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
+ stbir_filter filter_horizontal, stbir_filter filter_vertical,
+ stbir_colorspace space, void *alloc_context,
+ float s0, float t0, float s1, float t1)
+{
+ return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
+ output_pixels, output_w, output_h, output_stride_in_bytes,
+ s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical,
+ edge_mode_horizontal, edge_mode_vertical, space);
+}
+
+#endif // STB_IMAGE_RESIZE_IMPLEMENTATION
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/
diff --git a/main.cpp b/main.cpp
index 3cc976c..2d5b501 100644
--- a/main.cpp
+++ b/main.cpp
@@ -36,6 +36,11 @@
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "lib/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 "lib/stb_image_resize.h"
+
#include "defines.h"
#include "my_math.h"
#include "structs.h"
@@ -61,18 +66,18 @@ static uint64 BitmapBlockSize;
static instruction_mode InstructionMode = instruction_mode_scalar;
static uint32 RandomGlobalIncrement = 0;
+#if SD
#include "lib/base64.c"
#include <curl/curl.h>
+#include "stable_diffusion.cpp"
+#endif
#include "memory.cpp"
#include "undo.cpp"
#include "strings.cpp"
-#if THREADED
#include "threading.cpp"
-#endif
#include "createcalls.cpp"
// #include "ffmpeg_backend.cpp"
-#include "stable_diffusion.cpp"
#include "my_imgui_widgets.cpp"
#include "prenderer.cpp"
#include "gl_calls.cpp"
@@ -131,27 +136,35 @@ Main_InputTest(project_data *File, project_state *State, memory *Memory, ui *UI,
UI->Warp_WantSetPos = false;
}
- // NOTE(fox): The only requirement for something that needs to happen before the sort is anything that creates/deletes layers.
- // The requirement for something after the sort is things that depend on the sort. (obvious)
if (!io.WantCaptureKeyboard)
ImGui_ProcessInputs(File, State, UI, Memory, io);
sorted_file Sorted = File_Sort_Push(File, State, Memory);
- if (ImGui::IsKeyPressed(ImGuiKey_T)) {
- Interact_Transform_Begin(File, Memory, State, io.MousePos, Sorted.CompArray, Sorted.LayerArray);
- }
-
- bool32 mod_key = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
- if (mod_key) {
- if (ImGui::IsKeyPressed(ImGuiKey_C)) {
- Clipboard_Store(File, State, Memory, Sorted.PropertyInfo, Sorted.PropertyArray);
- }
- if (ImGui::IsKeyPressed(ImGuiKey_V)) {
- Clipboard_Paste(File, State, Memory, Sorted.LayerArray);
+ // These depend on sorting, so they can't be done in the ProcessInputs function:
+ if (State->HotkeyInput) {
+ switch (State->HotkeyInput) {
+ case hotkey_none:
+ {
+ Assert(0);
+ } break;
+ case hotkey_transform:
+ {
+ Interact_Transform_Begin(File, Memory, State, io.MousePos, Sorted.CompArray, Sorted.LayerArray);
+ } break;
+ case hotkey_copy:
+ {
+ Clipboard_Store(File, State, Memory, Sorted.CompArray, Sorted.LayerArray, Sorted.PropertyInfo, Sorted.PropertyArray);
+ } break;
+ case hotkey_paste:
+ {
+ Clipboard_Paste(File, State, Memory, Sorted.CompArray, Sorted.LayerArray);
+ } break;
}
+ State->HotkeyInput = hotkey_none;
}
+
ImGui::DockSpaceOverViewport();
if (Debug.ToggleWindow) {
@@ -162,7 +175,6 @@ Main_InputTest(project_data *File, project_state *State, memory *Memory, ui *UI,
if (State->Initializing == 3) {
Source_UICreateButton(File, State, Memory, Sorted.CompArray, Sorted.LayerArray);
- /*
block_layer *Layera = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, 2);
Layera->x.IsToggled = true;
Layera->y.IsToggled = true;
@@ -172,20 +184,17 @@ Main_InputTest(project_data *File, project_state *State, memory *Memory, ui *UI,
block_layer *Layerc = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, 0);
Layerc->x.IsToggled = true;
Layerc->y.IsToggled = true;
- */
- /*
- Layer_Select(Memory, State, 0);
- Layer_Select(Memory, State, 1);
- Layer_Select(Memory, State, 2);
+ // Layer_Select(Memory, State, 0);
+ // Layer_Select(Memory, State, 1);
+ // Layer_Select(Memory, State, 2);
}
if (State->Initializing == 2) {
- block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
- Precomp_UICreateButton(File, State, Memory, MainComp, *Sorted.CompArray, Sorted.LayerArray);
- Layer_DeselectAll(Memory, State, File->Layer_Count);
- */
- // block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, 0);
+ // block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
+ // Precomp_UICreateButton(File, State, Memory, MainComp, *Sorted.CompArray, Sorted.LayerArray);
+ // Layer_DeselectAll(Memory, State, File->Layer_Count);
+
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, 0);
Layer_Select(Memory, State, 0);
- /*
History_Entry_Commit(Memory, "Add keyframe");
property_channel *Property = &Layer->x;
{
@@ -200,6 +209,8 @@ Main_InputTest(project_data *File, project_state *State, memory *Memory, ui *UI,
bezier_point Point = { 1, {20, 300, -5, 0, 5, 0}, interpolation_type_bezier, 0, {0, 0, 0}, 0 };
Bezier_Add(Memory, Property, Point);
}
+ History_Entry_End(Memory);
+ /*
Property = &Layer->y;
{
bezier_point Point = { 1, {10, 50, -5, 0, 5, 0}, interpolation_type_bezier, 0, {0, 0, 0}, 0 };
@@ -239,17 +250,18 @@ Main_InputTest(project_data *File, project_state *State, memory *Memory, ui *UI,
*/
}
-
ImGui_Viewport(File, State, UI, Memory, io, textureID, Sorted.CompArray, Sorted.LayerArray);
ImGui_Timeline(File, State, Memory, UI, io, Sorted.CompArray, Sorted.LayerArray, Sorted.PropertyInfo, Sorted.PropertyArray);
ImGui_File(File, State, Memory, io, Sorted.CompArray, Sorted.LayerArray);
ImGui_PropertiesPanel(File, State, UI, Memory, io);
ImGui_ColorPanel(File, State, UI, Memory, io);
+#if SD
ImGui_SD_Prompt(File, State, UI, Memory, io);
- ImGui_SD_Thumbnail(File, State, UI, Memory, io);
+ ImGui_SD_Thumbnail(File, State, UI, Memory, io, Sorted.SourceArray, Sorted.TempSourceCount, textureID);
+#endif
ImGui_Menu(File, State, UI, Memory, io);
- File_Sort_Pop(Memory, Sorted.Layer_SortSize, Sorted.Property_SortSize);
+ File_Sort_Pop(Memory, Sorted.Layer_SortSize, Sorted.Property_SortSize, Sorted.Source_SortSize);
#if DEBUG
Debug.Temp = {};
@@ -264,10 +276,9 @@ Render_Main(void *Data, void *OutputBuffer, render_type RenderType, rectangle Re
bool32 IsRendering = true;
Renderer_Start(Data, OutputBuffer, RenderType, RenderRegion);
while (IsRendering) {
- // SDL_Delay(1);
+
// Main_InputTest(File, State, &Memory, &UI);
Renderer_Check(&IsRendering, RenderType);
- // TODO(fox): Make interruptable if the render time gets too long.
}
}
@@ -346,16 +357,24 @@ Render_Comp(project_data *File, project_state *State, memory *Memory, ImGuiIO io
layer_bitmap_state *BitmapState = &State->Render.Bitmap[Index_Physical];
void *BitmapAddress = NULL;
+ uint64 ScratchActive = 0;
if (!Layer->IsPrecomp) {
block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index);
-
if (Source->Type == source_type_principal) {
if (State->Interact_Active == interact_type_brush && State->Brush.LayerToPaint_Index == Index_Physical) {
- // Assert(0);
- // Render_Main((void *)T, , RenderRegion);
- BitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index);
+ void *SourceBitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index, 0);
+ rectangle RenderRegion = { 0, 0, Source->Width, Source->Height };
+ // TODO(fox): Do all these extra precomputes really make a difference in the renderer?
+ direct_info Info = { (real32)Source->Width, (real32)Source->Height, (real32)Source->BytesPerPixel, (real32)Source->Width * Source->BytesPerPixel,
+ Bitmap_ByteInfo(Source->BytesPerPixel), blend_normal,
+ RenderRegion, State->Brush.TransientBitmap};
+ ScratchActive = Source->Width * Source->Height * Source->BytesPerPixel;
+ void *SecondSourceBitmap = Memory_PushScratch(Memory, ScratchActive);
+ Memory_Copy((uint8 *)SecondSourceBitmap, (uint8 *)SourceBitmapAddress, ScratchActive);
+ Render_Main((void *)&Info, SecondSourceBitmap, render_type_notransform, RenderRegion);
+ BitmapAddress = SecondSourceBitmap;
} else {
- BitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index);
+ BitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index, 0);
}
} else {
cache_entry *Entry = Memory_Cache_Search(State, Memory, cache_entry_type_source, Layer->Block_Source_Index, 0);
@@ -407,6 +426,8 @@ Render_Comp(project_data *File, project_state *State, memory *Memory, ImGuiIO io
rectangle RenderRegion = {0, 0, Comp->Width, Comp->Height};
Render_Main((void *)&T, CompBuffer, render_type_main, RenderRegion);
+ if (ScratchActive)
+ Memory_PopScratch(Memory, ScratchActive);
}
}
Entry_Main->CycleTime = GetCPUTime() - Comp_TimeStart;
@@ -426,7 +447,7 @@ Main_Renderer(project_data *File, project_state *State, memory *Memory, SDL_Wind
void *MainCompBuffer = Render_Comp(File, State, Memory, io, Sorted.CompArray, Sorted.LayerArray,
Sorted.PropertyInfo, Sorted.PropertyArray, File->PrincipalCompIndex, State->Frame_Current);
- File_Sort_Pop(Memory, Sorted.Layer_SortSize, Sorted.Property_SortSize);
+ File_Sort_Pop(Memory, Sorted.Layer_SortSize, Sorted.Property_SortSize, Sorted.Source_SortSize);
glBindTexture(GL_TEXTURE_2D, textureID);
@@ -476,6 +497,7 @@ int main(int argc, char *argv[]) {
Memory_InitTable(&GlobalMemory, &Memory, 4 * 1024 * 1024, F_Strings, "Strings", sizeof(block_string));
Memory_InitTable(&GlobalMemory, &Memory, (uint64)50 * 1024 * 1024, F_PrincipalBitmaps, "Principal bitmap data", BitmapBlockSize);
+ Memory_InitTable(&GlobalMemory, &Memory, (uint64)5 * 1024 * 1024, B_Thumbnails, "Thumbnails");
Memory_InitTable(&GlobalMemory, &Memory, (uint64)64 * 1024 * 1024, B_ScratchSpace, "Scratch");
Memory_InitTable(&GlobalMemory, &Memory, (uint64)50 * 1024 * 1024, B_CachedBitmaps, "Cached bitmap buffer");
@@ -483,9 +505,9 @@ int main(int argc, char *argv[]) {
#if ARM
InstructionMode = instruction_mode_neon;
#else
- if (SDL_HasSSE2()) {
- InstructionMode = instruction_mode_sse;
- }
+ // if (SDL_HasSSE2()) {
+ // InstructionMode = instruction_mode_sse;
+ // }
if (SDL_HasAVX2()) {
InstructionMode = instruction_mode_avx;
}
@@ -518,12 +540,6 @@ int main(int argc, char *argv[]) {
ui UI = {};
UI.Test = ImDrawListSplitter();
- // int ToolCount = (int)tool_count;
- // for (int i = 0; i < ToolCount; i++) {
- // glBindTexture(GL_TEXTURE_2D, textureID);
- // glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MainComp->Width, MainComp->Height, 0, GL_RGBA, ByteFlag2, MainCompBuffer);
- // }
-
block_composition *MainComp = (block_composition *)Memory_Block_AllocateAddress(&Memory, F_Precomps);
MainComp->Width = 1280;
@@ -537,232 +553,35 @@ int main(int argc, char *argv[]) {
File->Comp_Count = 1;
- /*
- block_composition *Comp2 = (block_composition *)Memory_Block_AllocateAddress(&Memory, F_Precomps);
- Comp2->Width = 500;
- Comp2->Height = 500;
- Comp2->FPS = 24;
- Comp2->BytesPerPixel = 4;
- Comp2->Frame_Count = 48;
- Comp2->Frame_End = 48;
- Comp2->Occupied = 1;
- Comp2->Name_String_Index = String_AddToFile(&Memory, "Another comp");
- */
-
+#if 1
{
uint16 SourceIndex = Source_Generate(File, State, &Memory, (void *)"../asset/a.jpg");
block_source *Source = (block_source *)Memory_Block_AddressAtIndex(&Memory, F_Sources, 0);
Source->IsSelected = true;
-
- /*
- {
- Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End);
- block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1);
- Layer->Vertical_Offset = 6;
- Layer->Col[0] = 1;
- Layer->Col[1] = 0;
- Layer->Col[2] = 0;
- Layer->Block_Composition_Index = 0;
- Layer->IsSelected = true;
-
- property_channel *Property = &Layer->x;
- Property->Block_Bezier_Index[0] = Memory_Block_AllocateNew(&Memory, F_Bezier);
- Property->Block_Bezier_Count = 1;
-
- block_bezier *Bezier = (block_bezier *)Memory_Block_AddressAtIndex(&Memory, F_Bezier, Property->Block_Bezier_Index[0]);
- Bezier->Occupied = 1;
-
- Bezier->Point[0].Pos[0] = V2(0, 0);
- Bezier->Point[1].Pos[0] = V2(20, 50);
- Bezier->Point[2].Pos[0] = V2(40, -50);
- Bezier->Point[0].Pos[1] = V2(-4, 0);
- Bezier->Point[1].Pos[1] = V2(-4, 0);
- Bezier->Point[2].Pos[1] = V2(-4, 0);
- Bezier->Point[0].Pos[2] = V2(4, 0);
- Bezier->Point[1].Pos[2] = V2(4, 0);
- Bezier->Point[2].Pos[2] = V2(4, 0);
- Bezier->Point[0].Type = interpolation_type_bezier;
- Bezier->Point[1].Type = interpolation_type_bezier;
- Bezier->Point[2].Type = interpolation_type_bezier;
- Bezier->Point[1].IsSelected = false;
- Bezier->Point[0].Occupied = true;
- Bezier->Point[1].Occupied = true;
- Bezier->Point[2].Occupied = true;
-
- property_channel *Property2 = &Layer->opacity;
- Property2->Block_Bezier_Index[0] = Memory_Block_AllocateNew(&Memory, F_Bezier);
- Property2->Block_Bezier_Count = 1;
-
- block_bezier *Bezier2 = (block_bezier *)Memory_Block_AddressAtIndex(&Memory, F_Bezier, Property2->Block_Bezier_Index[0]);
- Bezier2->Occupied = 1;
-
- Bezier2->Point[0].Pos[0] = V2(0, 0);
- Bezier2->Point[1].Pos[0] = V2(20, 1);
- Bezier2->Point[0].Occupied = true;
- Bezier2->Point[1].Occupied = true;
-
- property_channel *Property3 = &Layer->y;
- Property3->Block_Bezier_Index[0] = Memory_Block_AllocateNew(&Memory, F_Bezier);
- Property3->Block_Bezier_Count = 1;
-
- block_bezier *Bezier3 = (block_bezier *)Memory_Block_AddressAtIndex(&Memory, F_Bezier, Property3->Block_Bezier_Index[0]);
- Bezier3->Occupied = 1;
-
- Bezier3->Point[0].Pos[0] = V2(0, -20);
- Bezier3->Point[1].Pos[0] = V2(20, 300);
- Bezier3->Point[2].Pos[0] = V2(40, 100);
- Bezier3->Point[0].Pos[1] = V2(-4, 0);
- Bezier3->Point[1].Pos[1] = V2(-4, 0);
- Bezier3->Point[2].Pos[1] = V2(-4, 0);
- Bezier3->Point[0].Pos[2] = V2(4, 0);
- Bezier3->Point[1].Pos[2] = V2(4, 0);
- Bezier3->Point[2].Pos[2] = V2(4, 0);
- Bezier3->Point[0].Type = interpolation_type_bezier;
- Bezier3->Point[1].Type = interpolation_type_bezier;
- Bezier3->Point[2].Type = interpolation_type_bezier;
- Bezier3->Point[0].Occupied = true;
- Bezier3->Point[1].Occupied = true;
- Bezier3->Point[2].Occupied = true;
- }
- */
- /*
- {
- Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End);
- block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1);
- Layer->Vertical_Offset = 2;
- Layer->IsSelected = true;
- Layer->ColIndex = 2;
- Layer->Block_Composition_Index = 0;
- }
- {
- Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End);
- block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1);
- Layer->Vertical_Offset = 5;
- Layer->ColIndex = 0;
- Layer->Block_Composition_Index = 0;
- }
- */
}
{
uint16 SourceIndex = Source_Generate(File, State, &Memory, (void *)"../asset/b.jpg");
block_source *Source = (block_source *)Memory_Block_AddressAtIndex(&Memory, F_Sources, 1);
- // Source->IsSelected = true;
- /*
- {
- Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End);
- block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1);
- Layer->IsSelected = true;
- Layer->Vertical_Offset = 3;
- Layer->ColIndex = 1;
- Layer->Block_Composition_Index = 0;
- // Layer->IsSelected = true;
-
- property_channel *Property = &Layer->x;
- Property->Block_Bezier_Index[0] = Memory_Block_AllocateNew(&Memory, F_Bezier);
- Property->Block_Bezier_Count = 1;
-
- block_bezier *Bezier = (block_bezier *)Memory_Block_AddressAtIndex(&Memory, F_Bezier, Property->Block_Bezier_Index[0]);
- Bezier->Occupied = 1;
-
- Bezier->Point[0].Pos[0] = V2(30, 0);
- Bezier->Point[1].Pos[0] = V2(33, 250);
- Bezier->Point[2].Pos[0] = V2(35, -50);
- Bezier->Point[3].Pos[0] = V2(55, 0);
- Bezier->Point[0].Pos[1] = V2(-1, 0);
- Bezier->Point[1].Pos[1] = V2(-1, 0);
- Bezier->Point[2].Pos[1] = V2(-1, 0);
- Bezier->Point[3].Pos[1] = V2(-1, 0);
- Bezier->Point[0].Pos[2] = V2(1, 0);
- Bezier->Point[1].Pos[2] = V2(1, 0);
- Bezier->Point[2].Pos[2] = V2(1, 0);
- Bezier->Point[3].Pos[2] = V2(1, 0);
- Bezier->Point[0].Type = interpolation_type_bezier;
- Bezier->Point[1].Type = interpolation_type_bezier;
- Bezier->Point[2].Type = interpolation_type_bezier;
- Bezier->Point[3].Type = interpolation_type_bezier;
- Bezier->Point[0].Occupied = true;
- Bezier->Point[1].Occupied = true;
- Bezier->Point[2].Occupied = true;
- Bezier->Point[3].Occupied = true;
-
- property_channel *Property2 = &Layer->opacity;
- Property2->Block_Bezier_Index[0] = Memory_Block_AllocateNew(&Memory, F_Bezier);
- Property2->Block_Bezier_Count = 1;
-
- block_bezier *Bezier2 = (block_bezier *)Memory_Block_AddressAtIndex(&Memory, F_Bezier, Property2->Block_Bezier_Index[0]);
- Bezier2->Occupied = 1;
-
- Bezier2->Point[0].Pos[0] = V2(25, 1);
- Bezier2->Point[1].Pos[0] = V2(40, 0);
- Bezier2->Point[0].Occupied = true;
- Bezier2->Point[1].Occupied = true;
- }
- */
+ Source->IsSelected = true;
}
{
uint16 SourceIndex = Source_Generate(File, State, &Memory, (void *)"../asset/c.jpg");
block_source *Source = (block_source *)Memory_Block_AddressAtIndex(&Memory, F_Sources, 2);
- // Source->IsSelected = true;
- /*
- {
- Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End);
- block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1);
- Layer->Vertical_Offset = 1;
- Layer->ColIndex = 3;
- Layer->Block_Composition_Index = 0;
- }
- */
- // {
- // Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End);
- // block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1);
- // Layer->Vertical_Offset = 4;
- // Layer->Col[0] = 0;
- // Layer->Col[1] = 1;
- // Layer->Col[2] = 1;
- // Layer->Block_Composition_Index = 1;
- // }
- }
-
- /*
- {
- Layer_CreateFromSource(File, State, &Memory, 1, MainComp->Frame_End);
- block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1);
- Layer->IsPrecomp = true;
- Layer->Vertical_Offset = 9;
- Layer->Col[0] = 1;
- Layer->Col[1] = 1;
- Layer->Col[2] = 1;
- Layer->Block_Composition_Index = 0;
- }
-
- {
- Layer_CreateFromSource(File, State, &Memory, 1, MainComp->Frame_End);
- block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1);
- Layer->IsPrecomp = true;
- Layer->Vertical_Offset = 10;
- Layer->Col[0] = 1;
- Layer->Col[1] = 1;
- Layer->Col[2] = 1;
- Layer->Block_Composition_Index = 0;
+ Source->IsSelected = true;
}
- */
-
- // History_Undo(&Memory);
- // History_Redo(&Memory);
+#endif
SDL_Init(SDL_INIT_VIDEO);
Semaphore = SDL_CreateSemaphore(0);
-#if THREADED
int Index[7];
for (int i = 0; i < 7; i++) {
Index[i] = i;
Thread[i] = SDL_CreateThread(TestThread, "thread", (void *)&Index[i]);
}
-#endif
// Decide GL+GLSL versions
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
@@ -859,46 +678,23 @@ int main(int argc, char *argv[]) {
Brush_CalcBitmapAlphaFromSize(&Memory, &State->Brush, 4);
State_BindBrushTexture(&Memory, &State->Brush, 4);
+#if SD
curl_global_init(CURL_GLOBAL_ALL);
curl_state MainHandle = {};
curl_state ProgHandle = {};
uint32 Inc = 0;
+#endif
while (State->IsRunning)
{
// State->Interact_Active = interact_type_layer_move;
// State->Interact_Offset[1] = -3.0f;
+#if SD
if (State->CurlActive) {
- if (State->CurlActive == -1) {
- Curl_GET_Init(&MainHandle, State->Dump1, State->JSONPayload, State->SD.ServerAddress, State->SD.Mode);
- Curl_Prog_Init(&ProgHandle, State->Dump2);
- State->CurlActive = 1;
- } else {
- if (Curl_Check(&MainHandle) == 1) {
- SD_JSONToSource(File, State, &Memory, State->Dump1, State->SD.Height, State->SD.Width);
- Curl_Free(&ProgHandle);
- curl_slist_free_all(ProgHandle.list);
- Curl_Free(&MainHandle);
- State->CurlActive = 0;
- }
- uint64 Time = ImGui::GetTime();
- if (Time - State->SDTimer > 0.3f) {
- if (Curl_Check(&ProgHandle) == 1) {
- SD_ParseProgress(State, (char *)State->Dump2);
- curl_multi_remove_handle(ProgHandle.curlm, ProgHandle.curl);
- curl_easy_reset(ProgHandle.curl);
- ProgHandle.CurlData.size = 0;
- curl_easy_setopt(ProgHandle.curl, CURLOPT_URL, "http://127.0.0.1:7860/sdapi/v1/progress");
- curl_easy_setopt(ProgHandle.curl, CURLOPT_WRITEFUNCTION, dumbcurlcallback);
- curl_easy_setopt(ProgHandle.curl, CURLOPT_WRITEDATA, (void *)&ProgHandle.CurlData);
- curl_multi_add_handle(ProgHandle.curlm, ProgHandle.curl);
- }
- State->SDTimer = Time;
- }
- Inc++;
- }
+ Curl_Main();
}
+#endif
Main_InputTest(File, State, &Memory, &UI, window, textureID);
@@ -916,7 +712,6 @@ int main(int argc, char *argv[]) {
Main_RenderUI(io, clear_color, window);
- // TODO(fox): Fix things that rely on this.
if (State->Initializing)
State->Initializing--;
}
diff --git a/main.h b/main.h
index 3d7b3e5..9630faf 100644
--- a/main.h
+++ b/main.h
@@ -163,8 +163,11 @@ struct sorted_file
sorted_comp_info *CompArray;
sorted_property_info *PropertyInfo;
uint16 *PropertyArray;
+ uint16 *SourceArray; // sort by recency, temp only
+ uint16 TempSourceCount;
uint64 Layer_SortSize;
uint64 Property_SortSize;
+ uint64 Source_SortSize;
};
enum timeline_mode
@@ -251,9 +254,10 @@ char *BrushNames[brush_amount] = {
struct brush_state
{
ImVec2 UIPos; // Initial position when ctrl is held
- real32 Size = 512; // Maxes at 1024 for now
+ real32 Size = 64; // Maxes at 1024 for now
real32 Hardness = 1.0f; // From 1 to 100
real32 Spacing = 1.0f;
+ bool32 EraseMode = 0;
brush_type Type = brush_normal;
GLuint GLTexture;
void *PaintBuffer;
@@ -300,6 +304,16 @@ struct interact_transform
uint32 TransformMode;
};
+
+
+enum hotkey_input
+{
+ hotkey_none,
+ hotkey_transform,
+ hotkey_copy,
+ hotkey_paste
+};
+
struct project_state
{
bool32 UpdateKeyframes = 1;
@@ -315,12 +329,14 @@ struct project_state
brush_state Brush;
sd_state SD;
- char JSONPayload[1024];
+ char JSONPayload[1024*1024*4];
int32 CurlActive = 0;
real32 SDPercentDone;
real32 SDTimeEstimate;
real64 SDTimer;
+ hotkey_input HotkeyInput;
+
void *Dump1;
void *Dump2;
@@ -407,6 +423,9 @@ struct block_source
uint16 Height;
uint16 BytesPerPixel;
+ uint32 RelativeTimestamp; //
+ GLuint ThumbnailTex;
+
source_type Type;
};
@@ -518,6 +537,18 @@ struct transform_info {
void *SourceBuffer;
};
+struct direct_info {
+ real32 BufferWidth;
+ real32 BufferHeight;
+ real32 BufferBytesPerPixel;
+ real32 BufferPitch;
+ render_byte_info BufferBits;
+
+ blend_mode BlendMode;
+ rectangle ClipRect;
+ void *SourceBuffer;
+};
+
struct brush_info {
uint32 BrushLength;
rectangle LayerBounds;
@@ -535,12 +566,13 @@ struct brush_info {
real32 G_Brush;
real32 B_Brush;
real32 A_Brush;
+ bool32 EraseMode;
uint8 *BrushRow;
};
enum render_type {
render_type_main,
- render_type_direct,
+ render_type_notransform,
render_type_brush
};
diff --git a/memory.cpp b/memory.cpp
index 64aadc1..47252fe 100644
--- a/memory.cpp
+++ b/memory.cpp
@@ -51,7 +51,7 @@ Memory_Block_AllocateAddress(memory *Memory, memory_table_list TableName)
return Memory_Block_AddressAtIndex(Memory, TableName, FileIndex, 0);
}
-// IMPORTANT(fox): All block data has to start with a uint8 Occupied variable!
+// IMPORTANT(fox): All block data structs have to start with a uint8 Occupied variable!
static bool32
Block_Loop(memory *Memory, memory_table_list TableName, uint32 TotalCount, int *HasIncremented, int *CurrentCount, int *Index)
{
diff --git a/memory.h b/memory.h
index 2067e16..5f15bca 100644
--- a/memory.h
+++ b/memory.h
@@ -14,6 +14,7 @@ enum memory_table_list {
F_Strings,
F_PrincipalBitmaps,
+ B_Thumbnails,
B_ScratchSpace,
B_CachedBitmaps,
};
diff --git a/my_imgui_widgets.cpp b/my_imgui_widgets.cpp
index b3339d2..0b1e017 100644
--- a/my_imgui_widgets.cpp
+++ b/my_imgui_widgets.cpp
@@ -236,10 +236,53 @@ ImGui_File(project_data *File, project_state *State, memory *Memory, ImGuiIO io,
ImGui::End();
}
+#if SD
static void
-ImGui_SD_Thumbnail(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io)
+ImGui_SD_Thumbnail(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io, uint16 *SourceArray, uint16 SourceCount, GLuint textureID)
{
ImGui::Begin("SD gallery");
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+ ImVec2 ViewportMin = ImGui::GetCursorScreenPos();
+ ImVec2 ViewportScale = ImGui::GetContentRegionAvail();
+ ImVec2 ViewportMax = ImVec2(ViewportMin.x + ViewportScale.x, ViewportMin.y + ViewportScale.y);
+ if (!SourceCount) {
+ ImGui::End();
+ return;
+ }
+ int test[16];
+ int count = 0;
+ // draw_list->AddImage((void *)(intptr_t)textureID, ViewportMin, ViewportMax);
+ uint32 T_Height = 256;
+ real32 RowPercent = (real32)ViewportScale.x / T_Height;
+ uint32 PerRow = (uint32)RowPercent;
+ uint32 UI_Size = T_Height;
+ for (int i = 0; i < SourceCount; i++) {
+ int32 Y = i / PerRow;
+ int32 X = i % PerRow;
+ ImVec2 ImgMin = ViewportMin + ImVec2(X * UI_Size, Y * UI_Size);
+ ImVec2 ImgMax = ImgMin + ImVec2(UI_Size, UI_Size);
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, SourceArray[i]);
+ // ImGui::Text("Count: %i", Source->RelativeTimestamp);
+ real32 Ratio = (real32)Source->Width / Source->Height;
+ uint32 T_Width = (uint32)(Ratio * T_Height);
+ if (Source->ThumbnailTex == 0) {
+ Source_DumpThumbnail(Memory, Source, T_Width, T_Height);
+ }
+ // draw_list->AddRect(ImgMin, ImgMax, IM_COL32(255, 255, 255, 64));
+ ImGui::SetCursorScreenPos(ImgMin);
+ ImGui::Button("asda", ImVec2(UI_Size, UI_Size));
+ draw_list->AddImage((void *)(intptr_t)Source->ThumbnailTex, ImgMin, ImgMax);
+
+ // block_string *String = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Source->Path_String_Index);
+ // ImGui::Selectable(String->Char, Source->IsSelected);
+ // if (ImGui::IsItemClicked() || ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
+ // if (!io.KeyShift && !Source->IsSelected) {
+ // Source_DeselectAll(File, Memory);
+ // }
+ // Source->IsSelected = 1;
+ // }
+ // ImGui::OpenPopupOnItemClick("sourcecontext", ImGuiPopupFlags_MouseButtonRight);
+ }
ImGui::End();
}
@@ -265,18 +308,18 @@ ImGui_SD_Prompt(project_data *File, project_state *State, ui *UI, memory *Memory
Source->Width, Source->Height, Source->BytesPerPixel, &len);
Assert(PNGBitmap);
- uint64 EncodedLength = 0;
- uint64 EncodedAllocSize = base64_encode_size(Size);
- uint8 *EncodedOutput = (uint8 *)Memory_PushScratch(Memory, EncodedAllocSize);
- uint64 EncodedTrueSize;
+ uint64 EncodedEstimateSize = base64_encode_size(Size);
+ uint8 *EncodedOutput = (uint8 *)Memory_PushScratch(Memory, EncodedEstimateSize);
+ uint64 EncodedTrueSize = 0;
- base64_encode((uint8 *)BitmapAddress, Size, EncodedOutput, &EncodedTrueSize);
+ base64_encode((uint8 *)PNGBitmap, len, EncodedOutput, &EncodedTrueSize);
Assert(EncodedOutput[EncodedTrueSize] == '\0');
+ // printf("%s", EncodedOutput);
STBIW_FREE(PNGBitmap);
- SD_AssembleJSON(SD, (char *)State->Dump1, EncodedOutput);
- Memory_PopScratch(Memory, EncodedAllocSize);
+ SD_AssembleJSON(SD, (char *)State->JSONPayload, EncodedOutput);
+ Memory_PopScratch(Memory, EncodedEstimateSize);
} else {
SD_AssembleJSON(SD, State->JSONPayload);
// SD_AssembleJSON(SD, (char *)State->Dump2);
@@ -317,6 +360,7 @@ ImGui_SD_Prompt(project_data *File, project_state *State, ui *UI, memory *Memory
ImGui::InputInt("Seed", &SD->Seed);
ImGui::End();
}
+#endif
static void
ImGui_ColorPanel(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io)
@@ -833,7 +877,6 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory,
block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
-
ImVec2 ViewportMin = ImGui::GetCursorScreenPos();
ImVec2 ViewportScale = ImGui::GetContentRegionAvail();
ImVec2 ViewportMax = ImVec2(ViewportMin.x + ViewportScale.x, ViewportMin.y + ViewportScale.y);
@@ -874,14 +917,15 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory,
ImGui::InvisibleButton("canvas", ViewportScale, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
bool32 IsHovered = ImGui::IsItemHovered();
-#if 1
+#if 0
bool32 IsActive = ImGui::IsItemActive();
bool32 IsActivated = ImGui::IsItemActivated();
+ bool32 IsDeactivated = ImGui::IsItemDeactivated();
#else
bool32 IsActive = ImGui::IsKeyDown(ImGuiKey_3);
bool32 IsActivated = ImGui::IsKeyPressed(ImGuiKey_3);
+ bool32 IsDeactivated = ImGui::IsKeyReleased(ImGuiKey_3);
#endif
- bool32 IsDeactivated = ImGui::IsItemDeactivated();
if (IsHovered && IsActivated && !ImGui::IsMouseDown(ImGuiMouseButton_Right))
{
@@ -891,14 +935,17 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory,
if (State->Tool == tool_brush) {
sorted_layer *SortedLayerInfo = Layer_GetSortedArray(SortedLayerArray, SortedCompArray, File->PrincipalCompIndex);
sorted_comp_info SortedCompInfo = SortedCompArray[File->PrincipalCompIndex];
- 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);
- if (Layer->IsSelected && Source->Type == source_type_principal) {
- State->Interact_Active = interact_type_brush;
- State->Brush.LayerToPaint_Index = Layer->Block_Source_Index;
+ if (!io.KeyCtrl) {
+ 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);
+ if (Layer->IsSelected && Source->Type == source_type_principal) {
+ State->Interact_Active = interact_type_brush;
+ State->Brush.LayerToPaint_Index = Index_Physical;
+ break;
+ }
}
}
if (State->Brush.LayerToPaint_Index == -1) {
@@ -963,7 +1010,7 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory,
block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, State->Brush.LayerToPaint_Index);
layer_transforms T_Layer = Layer_GetTransforms(Layer);
block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index);
- void *SourceBitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index);
+ void *SourceBitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index, 0);
ImVec2 MouseDelta = io.MouseDelta;
real32 Delta = MouseDelta.x + MouseDelta.y;
if (Delta != 0.0f) {
@@ -971,11 +1018,11 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory,
real32 DeltaSlope = MouseDelta.y / MouseDelta.x;
for (real32 i = 0; i < DeltaDistance; i += State->Brush.Spacing) {
ImVec2 MousePos = ImGui_Brush_CalcMousePos(State, io, MouseDelta, i, DeltaDistance, DeltaSlope);
- Brush_Render(State, UI, T_Layer, MainComp, Source, SourceBitmapAddress, ViewportMin, MousePos);
+ Brush_Render(State, UI, T_Layer, MainComp, Source, State->Brush.TransientBitmap, ViewportMin, MousePos);
}
} else if (IsActivated) {
ImVec2 MousePos = io.MousePos;
- Brush_Render(State, UI, T_Layer, MainComp, Source, SourceBitmapAddress, ViewportMin, MousePos);
+ Brush_Render(State, UI, T_Layer, MainComp, Source, State->Brush.TransientBitmap, ViewportMin, MousePos);
}
// Memory_Cache_Invalidate(State, Memory, cache_entry_type_comp, Layer->Block_Composition_Index, 0);
State->UpdateFrame = true;
@@ -1974,16 +2021,19 @@ ImGui_ProcessInputs(project_data *File, project_state *State, ui *UI, memory *Me
State->UpdateFrame = true;
}
if (ImGui::IsKeyPressed(ImGuiKey_E)) {
- block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
- State->Frame_Current = ((State->Frame_Current + 1) >= MainComp->Frame_Count) ? 0 : State->Frame_Current + 1;
- State->UpdateFrame = true;
+ if (!io.KeyShift) {
+ block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
+ State->Frame_Current = ((State->Frame_Current + 1) >= MainComp->Frame_Count) ? 0 : State->Frame_Current + 1;
+ State->UpdateFrame = true;
+ } else {
+ State->Brush.EraseMode ^= 1;
+ }
}
if (ImGui::IsKeyPressed(ImGuiKey_X)) {
v4 Temp = UI->Color;
UI->Color = UI->AltColor;
UI->AltColor = Temp;
}
-
if (ImGui::IsKeyPressed(ImGuiKey_V)) {
State->Tool = tool_default;
}
@@ -1995,19 +2045,43 @@ ImGui_ProcessInputs(project_data *File, project_state *State, ui *UI, memory *Me
UI->GraphZoomSize = ImVec2(0, 0);
UI->GraphMoveSize = ImVec2(0, 0);
}
- if (UI->TimelineMode == timeline_mode_graph) {
- if (ImGui::IsKeyPressed(ImGuiKey_G)) {
- State->Interact_Offset[2] = io.MousePos.x;
- State->Interact_Offset[3] = io.MousePos.y;
- State->Interact_Active = interact_type_keyframe_move;
- } else if (ImGui::IsKeyPressed(ImGuiKey_R)) {
- // State->Interact_Offset[2] = io.MousePos.x;
- // State->Interact_Offset[3] = io.MousePos.y;
- // State->Interact_Active = interact_type_keyframe_rotate;
- } else if (ImGui::IsKeyPressed(ImGuiKey_S)) {
- State->Interact_Offset[2] = io.MousePos.x;
- State->Interact_Offset[3] = io.MousePos.y;
- State->Interact_Active = interact_type_keyframe_scale;
+ if (UI->FocusedWindow == focus_timeline) {
+ if (UI->TimelineMode == timeline_mode_default) {
+ if (ImGui::IsKeyPressed(ImGuiKey_G)) {
+ Layer_ToggleChannel(File, Memory, 0);
+ Layer_ToggleChannel(File, Memory, 1);
+ } else if (ImGui::IsKeyPressed(ImGuiKey_A)) {
+ Layer_ToggleChannel(File, Memory, 2);
+ Layer_ToggleChannel(File, Memory, 3);
+ } else if (ImGui::IsKeyPressed(ImGuiKey_R)) {
+ Layer_ToggleChannel(File, Memory, 4);
+ } else if (ImGui::IsKeyPressed(ImGuiKey_S)) {
+ Layer_ToggleChannel(File, Memory, 5);
+ } else if (ImGui::IsKeyPressed(ImGuiKey_T)) {
+ if (io.KeyShift) {
+ Layer_ToggleChannel(File, Memory, 6);
+ } else {
+ Layer_ToggleChannel(File, Memory, 7);
+ }
+ }
+ } else if (UI->TimelineMode == timeline_mode_graph) {
+ if (ImGui::IsKeyPressed(ImGuiKey_G)) {
+ State->Interact_Offset[2] = io.MousePos.x;
+ State->Interact_Offset[3] = io.MousePos.y;
+ State->Interact_Active = interact_type_keyframe_move;
+ } else if (ImGui::IsKeyPressed(ImGuiKey_R)) {
+ // State->Interact_Offset[2] = io.MousePos.x;
+ // State->Interact_Offset[3] = io.MousePos.y;
+ // State->Interact_Active = interact_type_keyframe_rotate;
+ } else if (ImGui::IsKeyPressed(ImGuiKey_S)) {
+ State->Interact_Offset[2] = io.MousePos.x;
+ State->Interact_Offset[3] = io.MousePos.y;
+ State->Interact_Active = interact_type_keyframe_scale;
+ }
+ }
+ } else if (UI->FocusedWindow == focus_viewport) {
+ if (ImGui::IsKeyPressed(ImGuiKey_T)) {
+ State->HotkeyInput = hotkey_transform;
}
}
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
@@ -2066,6 +2140,23 @@ ImGui_ProcessInputs(project_data *File, project_state *State, ui *UI, memory *Me
bool32 mod_key = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
if (mod_key) {
+ if (ImGui::IsKeyPressed(ImGuiKey_C)) {
+ State->HotkeyInput = hotkey_copy;
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_V)) {
+ State->HotkeyInput = hotkey_paste;
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_Z)) {
+ if (io.KeyShift) {
+ History_Redo(Memory);
+ State->UpdateFrame = true;
+ } else {
+ History_Undo(Memory);
+ State->UpdateFrame = true;
+ }
+ }
+ }
+
/*
if (ImGui::IsKeyPressed(ImGuiKey_S)) {
if (io.KeyShift) {
@@ -2089,16 +2180,6 @@ ImGui_ProcessInputs(project_data *File, project_state *State, ui *UI, memory *Me
State->ImGuiPopups = popup_newlayer;
}
*/
- if (ImGui::IsKeyPressed(ImGuiKey_Z)) {
- if (io.KeyShift) {
- History_Redo(Memory);
- State->UpdateFrame = true;
- } else {
- History_Undo(Memory);
- State->UpdateFrame = true;
- }
- }
- }
}
@@ -3562,222 +3643,6 @@ ImGui_EffectsPanel(project_data *File, project_state *State, memory *Memory, ui
-static void
-ImGui_ProcessInputs(project_data *File, project_state *State, comp_buffer *CompBuffer, memory *Memory, ui *UI, ImGuiIO io)
-{
- if (io.KeysData[ImGuiKey_Q].Down) {
- State->IsRunning = false;
- }
-#if DEBUG
- // ImGui::SaveIniSettingsToDisk("asda");
-#endif
- if (ImGui::IsKeyPressed(ImGuiKey_R)) {
- /*
- UI->Y_TimelinePercentZoomed = UI->Default_Y_TimelinePercentZoomed;
- UI->Y_TimelinePercentOffset = UI->Default_Y_TimelinePercentOffset;
- keyframe *Keyframe = KeyframeLookup(&File->Layer[0]->x, 0);
- Keyframe->Value.f = -10;
- Keyframe = KeyframeLookup(&File->Layer[0]->x, 1);
- Keyframe->Value.f = -5;
- Keyframe = KeyframeLookup(&File->Layer[0]->x, 2);
- Keyframe->Value.f = 0;
- Keyframe = KeyframeLookup(&File->Layer[0]->x, 3);
- Keyframe->Value.f = 5;
- Keyframe = KeyframeLookup(&File->Layer[0]->x, 4);
- Keyframe->Value.f = 10;
- */
- }
-
- if (ImGui::IsKeyPressed(ImGuiKey_D)) {
- IncrementFrame(File, -1);
- State->UpdateFrame = true;
- State->UpdateKeyframes = true;
- }
-
- if (ImGui::IsKeyPressed(ImGuiKey_F)) {
- IncrementFrame(File, 1);
- State->UpdateFrame = true;
- State->UpdateKeyframes = true;
- }
-
- if (ImGui::IsKeyPressed(ImGuiKey_Z)) {
- if (io.KeyCtrl && io.KeyShift) {
- History_Redo(Memory);
- } else if (io.KeyCtrl) {
- History_Undo(Memory);
- }
- State->UpdateFrame = true;
- State->UpdateKeyframes = true;
- }
-
- if (ImGui::IsKeyPressed(ImGuiKey_V)) {
- if (State->Tool == tool_pen) {
- State->Tool = tool_default;
- } else {
- State->Tool = tool_pen;
- }
- }
-
- if (ImGui::IsKeyPressed(ImGuiKey_Space)) {
- if (io.KeyShift) {
- State->RerouteEffects = true;
- } else {
- State->IsPlaying ^= 1;
- }
- }
-
-
- if (State->IsPlaying && !IsRendering) {
- IncrementFrame(File, 1);
- State->UpdateFrame = true;
- State->UpdateKeyframes = true;
- }
-
- // if (ImGui::IsKeyPressed(ImGuiKey_R) && State->NumberOfSelectedLayers)
- // TransformsInteract(File, State, Memory, UI, sliding_rotation);
- if (ImGui::IsKeyPressed(ImGuiKey_S) && State->NumberOfSelectedLayers)
- TransformsInteract(File, State, Memory, UI, sliding_scale);
- if (ImGui::IsKeyPressed(ImGuiKey_G) && State->NumberOfSelectedLayers)
- TransformsInteract(File, State, Memory, UI, sliding_position);
- if (ImGui::IsKeyPressed(ImGuiKey_A) && State->NumberOfSelectedLayers)
- TransformsInteract(File, State, Memory, UI, sliding_anchorpoint);
-
- if (ImGui::IsKeyPressed(ImGuiKey_1))
- LoadTestFootage(File, State, Memory);
-
- if (ImGui::IsKeyPressed(ImGuiKey_D) && io.KeyCtrl)
- {
- }
-
- if (ImGui::IsKeyPressed(ImGuiKey_Delete))
- {
- switch (State->RecentSelectionType)
- {
- case selection_none:
- {
- } break;
- case selection_layer:
- {
- } break;
- case selection_effect:
- {
- } break;
- case selection_keyframe:
- {
- DeleteSelectedKeyframes(File, Memory);
- State->UpdateKeyframes = true;
- State->UpdateFrame = true;
- }
- case selection_maskpoint:
- {
- }
- case selection_source:
- {
- }
- break;
- }
- }
-
- if (State->IsInteracting) {
- ImVec2 MouseIncrement = io.MouseDelta * (ImVec2(CompBuffer->Width, CompBuffer->Height) / UI->CompZoom);
- project_layer *Layer = File->Layer[State->MostRecentlySelectedLayer];
- switch (State->TransformsHotkeyInteract)
- {
- case sliding_position:
- {
- Layer->x.CurrentValue.f += MouseIncrement.x;
- Layer->y.CurrentValue.f += MouseIncrement.y;
- } break;
- case sliding_anchorpoint:
- {
- source *Source = Layer->Source;
- Layer->x.CurrentValue.f += MouseIncrement.x;
- Layer->y.CurrentValue.f += MouseIncrement.y;
- Layer_CalcRotatedOffset(Layer, V2(MouseIncrement), V2(Source->Info.Width, Source->Info.Height),
- &Layer->ax.CurrentValue.f, &Layer->ay.CurrentValue.f);
- } break;
- case sliding_rotation:
- {
- Layer->rotation.CurrentValue.f += MouseIncrement.x / 10.0;
- } break;
- case sliding_scale:
- {
- Layer->scale.CurrentValue.f += MouseIncrement.x / 200.0;
- } break;
- }
- if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
- switch (State->TransformsHotkeyInteract)
- {
- case sliding_position:
- {
- Layer->x.CurrentValue.f = State->InteractCache[0];
- Layer->y.CurrentValue.f = State->InteractCache[1];
- } break;
- case sliding_anchorpoint:
- {
- Layer->x.CurrentValue.f = State->InteractCache[0];
- Layer->y.CurrentValue.f = State->InteractCache[1];
- Layer->ax.CurrentValue.f = State->InteractCache[2];
- Layer->ay.CurrentValue.f = State->InteractCache[3];
- } break;
- case sliding_rotation:
- {
- Layer->rotation.CurrentValue.f = State->InteractCache[0];
- } break;
- case sliding_scale:
- {
- Layer->scale.CurrentValue.f = State->InteractCache[0];
- } break;
- }
- State->IsInteracting = false;
- State->UpdateFrame = true;
- }
- if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
- History_Entry_Commit(Memory, action_entry_default, "Tranforms interact");
- switch (State->TransformsHotkeyInteract)
- {
- case sliding_position:
- {
- History_Action_Change(Memory, &Layer->x.CurrentValue.f, &State->InteractCache[0],
- &Layer->x.CurrentValue.f, action_type_change_r32);
- History_Action_Change(Memory, &Layer->y.CurrentValue.f, &State->InteractCache[1],
- &Layer->y.CurrentValue.f, action_type_change_r32);
- } break;
- case sliding_anchorpoint:
- {
- History_Action_Change(Memory, &Layer->x.CurrentValue.f, &State->InteractCache[0],
- &Layer->x.CurrentValue.f, action_type_change_r32);
- History_Action_Change(Memory, &Layer->y.CurrentValue.f, &State->InteractCache[1],
- &Layer->y.CurrentValue.f, action_type_change_r32);
- History_Action_Change(Memory, &Layer->ax.CurrentValue.f, &State->InteractCache[2],
- &Layer->ax.CurrentValue.f, action_type_change_r32);
- History_Action_Change(Memory, &Layer->ay.CurrentValue.f, &State->InteractCache[3],
- &Layer->ay.CurrentValue.f, action_type_change_r32);
- } break;
- case sliding_rotation:
- {
- History_Action_Change(Memory, &Layer->rotation.CurrentValue.f, &State->InteractCache[0],
- &Layer->rotation.CurrentValue.f, action_type_change_r32);
- } break;
- case sliding_scale:
- {
- History_Action_Change(Memory, &Layer->scale.CurrentValue.f, &State->InteractCache[0],
- &Layer->scale.CurrentValue.f, action_type_change_r32);
- } break;
- }
- History_Entry_End(Memory);
- State->IsInteracting = false;
- }
- State->UpdateFrame = true;
- }
-
- if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
- UI->DraggingLayerThreshold = 0;
- UI->DraggingTimelineThreshold = 0;
- UI->DraggingKeyframeThreshold = 0;
- }
-}
-
static char ImGuiPrefs[] = "[Window][DockSpaceViewport_11111111]"
"\nPos=0,0"
"\nSize=3200,1800"
diff --git a/prenderer.cpp b/prenderer.cpp
index aa44a5a..8690f4e 100644
--- a/prenderer.cpp
+++ b/prenderer.cpp
@@ -257,6 +257,8 @@ Layer_LocalToScreenSpace(project_state *State, memory *Memory, block_layer *Laye
static void
Fallback_RenderLayer(transform_info T, void *OutputBuffer, rectangle RenderRegion);
+static void
+Fallback_RenderDirect(direct_info T, void *OutputBuffer, rectangle RenderRegion);
static void
RenderLayers(render_entry Entry) {
@@ -266,14 +268,13 @@ RenderLayers(render_entry Entry) {
{
Fallback_RenderLayer(*(transform_info *)Entry.RenderData, Entry.OutputBuffer, Entry.RenderRegion);
} break;
- case render_type_direct:
+ case render_type_notransform:
{
- Assert(0);
- // Fallback_RenderDirect(*(direct_info *)Entry.RenderData, Entry.OutputBuffer, Entry.RenderRegion);
+ Fallback_RenderDirect(*(direct_info *)Entry.RenderData, Entry.OutputBuffer, Entry.RenderRegion);
} break;
case render_type_brush:
{
- PaintTest_AVX2(*(brush_info *)Entry.RenderData, Entry.OutputBuffer, Entry.RenderRegion);
+ PaintTest(*(brush_info *)Entry.RenderData, Entry.OutputBuffer, Entry.RenderRegion);
} break;
default:
{
@@ -295,6 +296,13 @@ RenderLayers(render_entry Entry) {
static void
Renderer_Start(void *Data, void *OutputBuffer, render_type RenderType, rectangle RenderRegion)
{
+#if DEBUG
+ if (Debug.NoThreading) {
+ render_entry Entry = { Data, OutputBuffer, RenderType, RenderRegion };
+ RenderLayers(Entry);
+ return;
+ }
+#endif
// CPU
Threading_BitmapOp(Data, OutputBuffer, RenderType, RenderRegion);
}
@@ -302,6 +310,12 @@ Renderer_Start(void *Data, void *OutputBuffer, render_type RenderType, rectangle
static void
Renderer_Check(bool32 *Test, render_type RenderType)
{
+#if DEBUG
+ if (Debug.NoThreading) {
+ *Test = true;
+ return;
+ }
+#endif
// CPU
*Test = Threading_IsActive(RenderType);
}
@@ -521,7 +535,7 @@ Transform_Calculate(project_state *State, memory *Memory, project_data *File, bl
A_Blend = A_Dest;\
static void
-Fallback_RenderDirect(transform_info T, void *OutputBuffer, rectangle RenderRegion)
+Fallback_RenderDirect(direct_info T, void *OutputBuffer, rectangle RenderRegion)
{
rectangle LayerBounds = ClipRectangle( T.ClipRect, RenderRegion);
@@ -534,16 +548,16 @@ Fallback_RenderDirect(transform_info T, void *OutputBuffer, rectangle RenderRegi
{
uint16 LX = X;
uint16 LY = Y;
- uint16 LXPlus = Ceil(X+1, (uint32)T.LayerWidth - 1);
- uint16 LYPlus = Ceil(Y+1, (uint32)T.LayerHeight - 1);
+ uint16 LXPlus = Ceil(X+1, (uint32)T.BufferWidth - 1);
+ uint16 LYPlus = Ceil(Y+1, (uint32)T.BufferHeight - 1);
- uint8 *TexPTR0 = ((uint8 *)T.SourceBuffer + ((uint16)T.LayerPitch * LY) + (LX * (uint16)T.LayerBytesPerPixel));
+ uint8 *TexPTR0 = ((uint8 *)T.SourceBuffer + ((uint16)T.BufferPitch * LY) + (LX * (uint16)T.BufferBytesPerPixel));
uint32 PixelA = *(uint32 *)TexPTR0;
- real32 TexRA = (real32)(*(uint32 *)(TexPTR0 + T.LayerBits.ByteOffset * 0) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
- real32 TexGA = (real32)(*(uint32 *)(TexPTR0 + T.LayerBits.ByteOffset * 1) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
- real32 TexBA = (real32)(*(uint32 *)(TexPTR0 + T.LayerBits.ByteOffset * 2) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
- real32 TexAA = (real32)(*(uint32 *)(TexPTR0 + T.LayerBits.ByteOffset * 3) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexRA = (real32)(*(uint32 *)(TexPTR0 + T.BufferBits.ByteOffset * 0) & T.BufferBits.MaskPixel) * T.BufferBits.Normalized;
+ real32 TexGA = (real32)(*(uint32 *)(TexPTR0 + T.BufferBits.ByteOffset * 1) & T.BufferBits.MaskPixel) * T.BufferBits.Normalized;
+ real32 TexBA = (real32)(*(uint32 *)(TexPTR0 + T.BufferBits.ByteOffset * 2) & T.BufferBits.MaskPixel) * T.BufferBits.Normalized;
+ real32 TexAA = (real32)(*(uint32 *)(TexPTR0 + T.BufferBits.ByteOffset * 3) & T.BufferBits.MaskPixel) * T.BufferBits.Normalized;
real32 LayerAlpha = TexAA * 1; // brush opacity
@@ -850,11 +864,9 @@ EndRenderState(project_state *State)
}
State->NumberOfLayersToRender = 0;
-#if THREADED
SDL_AtomicSet(&CurrentEntry, 0);
SDL_AtomicSet(&QueuedEntries, 0);
SDL_AtomicSet(&CompletedEntries, 0);
-#endif
}
@@ -930,7 +942,6 @@ QueueCurrentFrame(project_data *File, comp_buffer *CompBuffer, project_state *St
}
}
-#if THREADED
uint16 TileWidth = CompBuffer->Width / 4;
uint16 TileHeight = CompBuffer->Height / 4;
@@ -957,12 +968,10 @@ QueueCurrentFrame(project_data *File, comp_buffer *CompBuffer, project_state *St
}
}
-#else
rectangle RenderRegion = {0, 0, (int32)CompBuffer->Width, (int32)CompBuffer->Height};
RenderLayers(&RenderInfo, RenderRegion);
-#endif
}
#if ARM
diff --git a/stable_diffusion.cpp b/stable_diffusion.cpp
index 2eb230b..dc12bbc 100644
--- a/stable_diffusion.cpp
+++ b/stable_diffusion.cpp
@@ -38,9 +38,19 @@ SD_JSONToSource(project_data *File, project_state *State, memory *Memory, void *
void *RawData = stbi_load_from_memory((stbi_uc *)PNGData, PNGSize, &x, &y, &a, 4);
Assert(x == Width && y == Height);
Memory_PopScratch(Memory, MaxSize);
+ int32 Highest = 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 && Source->RelativeTimestamp > Highest)
+ Highest = Source->RelativeTimestamp;
+ }
+ }
int SrcIdx = Source_Generate_Blank(File, State, Memory, Width, Height, 4);
block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, SrcIdx);
- // Source->Type = source_type_principal_temp;
+ Source->Type = source_type_principal_temp;
+ Source->RelativeTimestamp = Highest + 1;
void *BitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index, 0);
Memory_Copy((uint8 *)BitmapAddress, (uint8 *)RawData, MaxSize);
stbi_image_free(RawData);
@@ -74,6 +84,8 @@ SD_ParseProgress(project_state *State, char *JSONInfo)
// Assert(0);
}
+static char *pre = "data:image/png;base64,";
+
static void
JSON_AppendParam_String(char *String, uint64 *i, char *P1, char *P2)
{
@@ -83,17 +95,22 @@ JSON_AppendParam_String(char *String, uint64 *i, char *P1, char *P2)
while(P1[a] != '\0') {
String[c++] = P1[a++];
}
+ String[c++] = '"';
String[c++] = ':';
String[c++] = ' ';
+ String[c++] = '[';
String[c++] = '"';
a = 0;
+ while(pre[a] != '\0') {
+ String[c++] = pre[a++];
+ }
+ a = 0;
while(P2[a] != '\0') {
String[c++] = P2[a++];
- if (a > 64)
- break;
}
c--;
String[c++] = '"';
+ String[c++] = ']';
String[c++] = ',';
String[c++] = '\n';
String[c++] = '\0';
@@ -114,8 +131,9 @@ SD_AssembleJSON(sd_state *SD, char *JSONPayload, void *Base64Bitmap = NULL)
JSONPayload[1] = '\n';
JSONPayload[2] = '\0';
uint64 i = 2;
- if (SD->Mode)
+ if (SD->Mode) {
JSON_AppendParam_String(JSONPayload, &i, "init_images", (char *)Base64Bitmap);
+ }
for (int i = 0; i < 6; i++) {
if (Type[i] == 0) {
sprintf(JSONPayload, "%s\"%s\": \"%s\",\n", JSONPayload, Test[i], (char *)Test2[i]);
@@ -131,6 +149,7 @@ SD_AssembleJSON(sd_state *SD, char *JSONPayload, void *Base64Bitmap = NULL)
sprintf(JSONPayload, "%s\"%s\": %.2f,\n", JSONPayload, "denoising_strength", SD->DenoisingStrength);
sprintf(JSONPayload, "%s%s\n", JSONPayload, "\"sampler_index\": \"DDIM\"");
sprintf(JSONPayload, "%s}\n", JSONPayload);
+ printf("%s\n", JSONPayload);
// sprintf(CurlCommand, "curl -X POST -H 'Content-Type: application/json' -i '%s/sdapi/v1/txt2img' --data '%s'", SD->ServerAddress, JSONPayload);
// printf("%s\n", CurlCommand);
};
@@ -144,6 +163,43 @@ struct curl_state
};
static void
+Curl_Main()
+{
+ if (State->CurlActive == -1) {
+ Curl_GET_Init(&MainHandle, State->Dump1, State->JSONPayload, State->SD.ServerAddress, State->SD.Mode);
+ Curl_Prog_Init(&ProgHandle, State->Dump2);
+ State->CurlActive = 1;
+ } else {
+ if (Curl_Check(&MainHandle) == 1) {
+ SD_JSONToSource(File, State, &Memory, State->Dump1, State->SD.Height, State->SD.Width);
+ Curl_StopAll(State, &ProgHandle, &MainHandle);
+ }
+ uint64 Time = ImGui::GetTime();
+ if (Time - State->SDTimer > 0.3f) {
+ int Test = Curl_Check(&ProgHandle);
+ if (Test == 1) {
+ SD_ParseProgress(State, (char *)State->Dump2);
+ curl_multi_remove_handle(ProgHandle.curlm, ProgHandle.curl);
+ curl_easy_reset(ProgHandle.curl);
+ ProgHandle.CurlData.size = 0;
+ curl_easy_setopt(ProgHandle.curl, CURLOPT_URL, "http://127.0.0.1:7860/sdapi/v1/progress");
+ curl_easy_setopt(ProgHandle.curl, CURLOPT_WRITEFUNCTION, dumbcurlcallback);
+ curl_easy_setopt(ProgHandle.curl, CURLOPT_WRITEDATA, (void *)&ProgHandle.CurlData);
+ curl_multi_add_handle(ProgHandle.curlm, ProgHandle.curl);
+ } else if (Test == -1) {
+ PostMsg(State, "Active stable-diffusion-webui instance not found at URL.");
+ Curl_StopAll(State, &ProgHandle, &MainHandle);
+ } else if (Test == -2) {
+ PostMsg(State, "CURL error; see command line.");
+ Curl_StopAll(State, &ProgHandle, &MainHandle);
+ }
+ State->SDTimer = Time;
+ }
+ Inc++;
+ }
+}
+
+static void
Curl_Free(curl_state *Handle)
{
curl_multi_remove_handle(Handle->curlm, Handle->curl);
@@ -151,7 +207,16 @@ Curl_Free(curl_state *Handle)
curl_multi_cleanup(Handle->curlm);
}
-static bool32
+static void
+Curl_StopAll(project_state *State, curl_state *ProgHandle, curl_state *MainHandle)
+{
+ Curl_Free(ProgHandle);
+ curl_slist_free_all(ProgHandle->list);
+ Curl_Free(MainHandle);
+ State->CurlActive = 0;
+}
+
+static int
Curl_Check(curl_state *Handle)
{
int IsActive;
@@ -167,11 +232,10 @@ Curl_Check(curl_state *Handle)
if (!msg->data.result) {
return 1;
} else if (msg->data.result == CURLE_COULDNT_CONNECT) {
- // printf("Active stable-diffusion-webui instance not found at URL.\n");
return -1;
} else {
- // printf("curl error: %s!\n", curl_easy_strerror(msg->data.result));
- return -1;
+ printf("curl error: %s!\n", curl_easy_strerror(msg->data.result));
+ return -2;
}
}
}
diff --git a/stable_diffusion.h b/stable_diffusion.h
index 97ae076..febb8f6 100644
--- a/stable_diffusion.h
+++ b/stable_diffusion.h
@@ -26,7 +26,7 @@ struct sd_state
int32 Height = 384;
int32 SamplerIndex = 0;
real32 CFG = 7;
- real32 DenoisingStrength = 0.2;
+ real32 DenoisingStrength = 0.8;
int32 Seed = -1;
};
diff --git a/undo.cpp b/undo.cpp
index e568675..d985cee 100644
--- a/undo.cpp
+++ b/undo.cpp
@@ -173,7 +173,7 @@ void History_Entry_Commit(memory *Memory, char *Name)
History->EntryPlayhead++;
Memory->IsFileSaved = false;
#if DEBUG
- Assert(Debug.UndoState != 1);
+ Assert(Debug.UndoState != 1); // You forgot to end a History_Entry_Commit()!
Debug.UndoState = 1;
#endif
}