Files
imgui/backends/imgui_impl_cpu.cpp
2026-03-15 12:33:32 +01:00

1820 lines
73 KiB
C++

// dear imgui: CPU framebuffer renderer backend
// (supports caller-owned framebuffers/textures in generic packed pixel formats)
//
// Implemented features:
// [X] Renderer: User texture binding. Use 'ImGui_ImplCPU_Texture*' as texture identifier.
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
//
// CHANGELOG
// 2026-03-16: Added generic packed-pixel format support, builtin formats, Alpha8 textures, and parity-oriented generic paths.
// 2026-03-15: Initial version, extracted from the SDL surface software renderer.
#include "imgui.h"
#ifndef IMGUI_DISABLE
#include "imgui_impl_cpu.h"
#include <algorithm>
#include <cmath>
#include <cstring>
#include <stdint.h>
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfloat-equal"
#pragma clang diagnostic ignored "-Wold-style-cast"
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#define IMGUI_IMPL_CPU_BIG_ENDIAN 1
#else
#define IMGUI_IMPL_CPU_BIG_ENDIAN 0
#endif
#if IMGUI_IMPL_CPU_BIG_ENDIAN
#define IMGUI_IMPL_CPU_RGBA8888_RMASK 0xFF000000u
#define IMGUI_IMPL_CPU_RGBA8888_GMASK 0x00FF0000u
#define IMGUI_IMPL_CPU_RGBA8888_BMASK 0x0000FF00u
#define IMGUI_IMPL_CPU_RGBA8888_AMASK 0x000000FFu
#define IMGUI_IMPL_CPU_BGRA8888_RMASK 0x0000FF00u
#define IMGUI_IMPL_CPU_BGRA8888_GMASK 0x00FF0000u
#define IMGUI_IMPL_CPU_BGRA8888_BMASK 0xFF000000u
#define IMGUI_IMPL_CPU_BGRA8888_AMASK 0x000000FFu
#define IMGUI_IMPL_CPU_ARGB8888_RMASK 0x00FF0000u
#define IMGUI_IMPL_CPU_ARGB8888_GMASK 0x0000FF00u
#define IMGUI_IMPL_CPU_ARGB8888_BMASK 0x000000FFu
#define IMGUI_IMPL_CPU_ARGB8888_AMASK 0xFF000000u
#define IMGUI_IMPL_CPU_ABGR8888_RMASK 0x000000FFu
#define IMGUI_IMPL_CPU_ABGR8888_GMASK 0x0000FF00u
#define IMGUI_IMPL_CPU_ABGR8888_BMASK 0x00FF0000u
#define IMGUI_IMPL_CPU_ABGR8888_AMASK 0xFF000000u
#define IMGUI_IMPL_CPU_RGB888_RMASK 0x00FF0000u
#define IMGUI_IMPL_CPU_RGB888_GMASK 0x0000FF00u
#define IMGUI_IMPL_CPU_RGB888_BMASK 0x000000FFu
#define IMGUI_IMPL_CPU_BGR888_RMASK 0x000000FFu
#define IMGUI_IMPL_CPU_BGR888_GMASK 0x0000FF00u
#define IMGUI_IMPL_CPU_BGR888_BMASK 0x00FF0000u
#else
#define IMGUI_IMPL_CPU_RGBA8888_RMASK 0x000000FFu
#define IMGUI_IMPL_CPU_RGBA8888_GMASK 0x0000FF00u
#define IMGUI_IMPL_CPU_RGBA8888_BMASK 0x00FF0000u
#define IMGUI_IMPL_CPU_RGBA8888_AMASK 0xFF000000u
#define IMGUI_IMPL_CPU_BGRA8888_RMASK 0x00FF0000u
#define IMGUI_IMPL_CPU_BGRA8888_GMASK 0x0000FF00u
#define IMGUI_IMPL_CPU_BGRA8888_BMASK 0x000000FFu
#define IMGUI_IMPL_CPU_BGRA8888_AMASK 0xFF000000u
#define IMGUI_IMPL_CPU_ARGB8888_RMASK 0x0000FF00u
#define IMGUI_IMPL_CPU_ARGB8888_GMASK 0x00FF0000u
#define IMGUI_IMPL_CPU_ARGB8888_BMASK 0xFF000000u
#define IMGUI_IMPL_CPU_ARGB8888_AMASK 0x000000FFu
#define IMGUI_IMPL_CPU_ABGR8888_RMASK 0xFF000000u
#define IMGUI_IMPL_CPU_ABGR8888_GMASK 0x00FF0000u
#define IMGUI_IMPL_CPU_ABGR8888_BMASK 0x0000FF00u
#define IMGUI_IMPL_CPU_ABGR8888_AMASK 0x000000FFu
#define IMGUI_IMPL_CPU_RGB888_RMASK 0x000000FFu
#define IMGUI_IMPL_CPU_RGB888_GMASK 0x0000FF00u
#define IMGUI_IMPL_CPU_RGB888_BMASK 0x00FF0000u
#define IMGUI_IMPL_CPU_BGR888_RMASK 0x00FF0000u
#define IMGUI_IMPL_CPU_BGR888_GMASK 0x0000FF00u
#define IMGUI_IMPL_CPU_BGR888_BMASK 0x000000FFu
#endif
static const ImGui_ImplCPU_PixelFormat GImGui_ImplCPU_BuiltinFormats[ImGui_ImplCPU_BuiltinFormat_COUNT] =
{
{ 32, 4, IMGUI_IMPL_CPU_RGBA8888_RMASK, IMGUI_IMPL_CPU_RGBA8888_GMASK, IMGUI_IMPL_CPU_RGBA8888_BMASK, IMGUI_IMPL_CPU_RGBA8888_AMASK, 0, 8, 16, 24, 0, 0, 0, 0, nullptr, 0, nullptr },
{ 32, 4, IMGUI_IMPL_CPU_BGRA8888_RMASK, IMGUI_IMPL_CPU_BGRA8888_GMASK, IMGUI_IMPL_CPU_BGRA8888_BMASK, IMGUI_IMPL_CPU_BGRA8888_AMASK, 16, 8, 0, 24, 0, 0, 0, 0, nullptr, 0, nullptr },
{ 32, 4, IMGUI_IMPL_CPU_ARGB8888_RMASK, IMGUI_IMPL_CPU_ARGB8888_GMASK, IMGUI_IMPL_CPU_ARGB8888_BMASK, IMGUI_IMPL_CPU_ARGB8888_AMASK, 8, 16, 24, 0, 0, 0, 0, 0, nullptr, 0, nullptr },
{ 32, 4, IMGUI_IMPL_CPU_ABGR8888_RMASK, IMGUI_IMPL_CPU_ABGR8888_GMASK, IMGUI_IMPL_CPU_ABGR8888_BMASK, IMGUI_IMPL_CPU_ABGR8888_AMASK, 24, 16, 8, 0, 0, 0, 0, 0, nullptr, 0, nullptr },
{ 16, 2, 0xF800u, 0x07E0u, 0x001Fu, 0x0000u, 11, 5, 0, 0, 3, 2, 3, 8, nullptr, 0, nullptr },
{ 16, 2, 0x7C00u, 0x03E0u, 0x001Fu, 0x8000u, 10, 5, 0, 15, 3, 3, 3, 7, nullptr, 0, nullptr },
{ 16, 2, 0xF000u, 0x0F00u, 0x00F0u, 0x000Fu, 12, 8, 4, 0, 4, 4, 4, 4, nullptr, 0, nullptr },
{ 24, 3, IMGUI_IMPL_CPU_RGB888_RMASK, IMGUI_IMPL_CPU_RGB888_GMASK, IMGUI_IMPL_CPU_RGB888_BMASK, 0x00000000u, 0, 8, 16, 0, 0, 0, 0, 8, nullptr, 0, nullptr },
{ 24, 3, IMGUI_IMPL_CPU_BGR888_RMASK, IMGUI_IMPL_CPU_BGR888_GMASK, IMGUI_IMPL_CPU_BGR888_BMASK, 0x00000000u, 16, 8, 0, 0, 0, 0, 0, 8, nullptr, 0, nullptr },
{ 8, 1, 0x00000000u, 0x00000000u, 0x00000000u, 0x000000FFu, 0, 0, 0, 0, 8, 8, 8, 0, nullptr, 0, nullptr }
};
const ImGui_ImplCPU_PixelFormat* ImGui_ImplCPU_GetBuiltinPixelFormat(ImGui_ImplCPU_BuiltinFormat format)
{
if (format < 0 || format >= ImGui_ImplCPU_BuiltinFormat_COUNT)
return nullptr;
return &GImGui_ImplCPU_BuiltinFormats[format];
}
enum ImGui_ImplCPU_FormatClass
{
ImGui_ImplCPU_FormatClass_FastRGBA32,
ImGui_ImplCPU_FormatClass_FastBGRA32,
ImGui_ImplCPU_FormatClass_FastARGB32,
ImGui_ImplCPU_FormatClass_FastABGR32,
ImGui_ImplCPU_FormatClass_RGB565,
ImGui_ImplCPU_FormatClass_ARGB1555,
ImGui_ImplCPU_FormatClass_RGBA4444,
ImGui_ImplCPU_FormatClass_RGB888,
ImGui_ImplCPU_FormatClass_BGR888,
ImGui_ImplCPU_FormatClass_Alpha8,
ImGui_ImplCPU_FormatClass_Indexed,
ImGui_ImplCPU_FormatClass_Generic
};
struct ImGui_ImplCPU_FastFormat32
{
ImU32 Rmask;
ImU32 Gmask;
ImU32 Bmask;
ImU32 Amask;
ImU8 Rshift;
ImU8 Gshift;
ImU8 Bshift;
ImU8 Ashift;
bool HasAlpha;
bool Valid;
ImGui_ImplCPU_FastFormat32() { memset((void*)this, 0, sizeof(*this)); }
};
struct ImGui_ImplCPU_SurfaceInfo;
typedef void (*ImGui_ImplCPU_ReadRGBAFn)(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y, unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a);
typedef unsigned char (*ImGui_ImplCPU_ReadAlphaFn)(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y);
typedef ImU32 (*ImGui_ImplCPU_PackRGBAFn)(const ImGui_ImplCPU_SurfaceInfo& surface, unsigned char r, unsigned char g, unsigned char b, unsigned char a);
struct ImGui_ImplCPU_SurfaceInfo
{
unsigned char* Pixels;
int Pitch;
int Width;
int Height;
const ImGui_ImplCPU_PixelFormat* Format;
ImGui_ImplCPU_FormatClass Class;
ImGui_ImplCPU_FastFormat32 FastFormat;
ImGui_ImplCPU_ReadRGBAFn ReadRGBAFn;
ImGui_ImplCPU_ReadAlphaFn ReadAlphaFn;
ImGui_ImplCPU_PackRGBAFn PackRGBAFn;
bool IsFast;
bool HasAlpha;
bool IsAlphaOnly;
bool IsIndexed;
ImGui_ImplCPU_SurfaceInfo() { memset((void*)this, 0, sizeof(*this)); }
};
struct ImGui_ImplCPU_RasterVertex
{
float X;
float Y;
float U;
float V;
float R;
float G;
float B;
float A;
};
struct ImGui_ImplCPU_Quad
{
const ImGui_ImplCPU_RasterVertex* TL;
const ImGui_ImplCPU_RasterVertex* TR;
const ImGui_ImplCPU_RasterVertex* BR;
const ImGui_ImplCPU_RasterVertex* BL;
};
struct ImGui_ImplCPU_Data
{
ImVector<ImGui_ImplCPU_RasterVertex> VertexCache;
};
static ImGui_ImplCPU_Data* ImGui_ImplCPU_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplCPU_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
}
static inline const ImGui_ImplCPU_PixelFormat* ImGui_ImplCPU_GetDefaultPixelFormat()
{
return ImGui_ImplCPU_GetBuiltinPixelFormat(ImGui_ImplCPU_BuiltinFormat_RGBA8888);
}
static inline float ImGui_ImplCPU_Edge(float ax, float ay, float bx, float by, float px, float py)
{
return (bx - ax) * (py - ay) - (by - ay) * (px - ax);
}
static inline bool ImGui_ImplCPU_NearEqual(float a, float b)
{
return fabsf(a - b) <= 0.0001f;
}
static inline int ImGui_ImplCPU_ClampInt(int v, int lo, int hi)
{
return (v < lo) ? lo : (v > hi ? hi : v);
}
static inline unsigned char ImGui_ImplCPU_ClampByte(float v)
{
if (v <= 0.0f)
return 0;
if (v >= 255.0f)
return 255;
return (unsigned char)(v + 0.5f);
}
static inline unsigned char ImGui_ImplCPU_Mul255(unsigned int a, unsigned int b)
{
unsigned int t = a * b + 128;
return (unsigned char)((t + (t >> 8)) >> 8);
}
static inline unsigned char ImGui_ImplCPU_ExpandBits(ImU32 value, int bits)
{
if (bits <= 0)
return 0;
if (bits >= 8)
return (unsigned char)value;
ImU32 max_value = (1u << bits) - 1u;
return (unsigned char)((value * 255u + (max_value >> 1)) / max_value);
}
static inline ImU32 ImGui_ImplCPU_CompressBits(unsigned char value, int bits)
{
if (bits <= 0)
return 0;
if (bits >= 8)
return value;
ImU32 max_value = (1u << bits) - 1u;
return (ImU32)((value * max_value + 127u) / 255u);
}
static inline void ImGui_ImplCPU_UnpackCanonicalRGBA(ImU32 pixel, unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a)
{
*r = (unsigned char)((pixel >> IM_COL32_R_SHIFT) & 0xFF);
*g = (unsigned char)((pixel >> IM_COL32_G_SHIFT) & 0xFF);
*b = (unsigned char)((pixel >> IM_COL32_B_SHIFT) & 0xFF);
*a = (unsigned char)((pixel >> IM_COL32_A_SHIFT) & 0xFF);
}
static inline bool ImGui_ImplCPU_CanUseFastPath(const void* pixels, int pitch)
{
#ifdef IMGUI_IMPL_CPU_DISABLE_FAST_PATH
IM_UNUSED(pixels);
IM_UNUSED(pitch);
return false;
#else
if (pixels == nullptr || pitch <= 0)
return false;
if ((pitch & 3) != 0)
return false;
if ((((uintptr_t)pixels) & 3) != 0)
return false;
return true;
#endif
}
static inline bool ImGui_ImplCPU_MatchesFormat(const ImGui_ImplCPU_PixelFormat* format, const ImGui_ImplCPU_PixelFormat* builtin)
{
return format->BitsPerPixel == builtin->BitsPerPixel
&& format->BytesPerPixel == builtin->BytesPerPixel
&& format->Rmask == builtin->Rmask
&& format->Gmask == builtin->Gmask
&& format->Bmask == builtin->Bmask
&& format->Amask == builtin->Amask
&& format->Rshift == builtin->Rshift
&& format->Gshift == builtin->Gshift
&& format->Bshift == builtin->Bshift
&& format->Ashift == builtin->Ashift
&& format->Rloss == builtin->Rloss
&& format->Gloss == builtin->Gloss
&& format->Bloss == builtin->Bloss
&& format->Aloss == builtin->Aloss;
}
static inline ImGui_ImplCPU_FormatClass ImGui_ImplCPU_ClassifyFormat(const ImGui_ImplCPU_PixelFormat* format)
{
if (format == nullptr)
format = ImGui_ImplCPU_GetDefaultPixelFormat();
if (format->Palette != nullptr && format->PaletteSize > 0)
return ImGui_ImplCPU_FormatClass_Indexed;
if (ImGui_ImplCPU_MatchesFormat(format, ImGui_ImplCPU_GetBuiltinPixelFormat(ImGui_ImplCPU_BuiltinFormat_RGBA8888)))
return ImGui_ImplCPU_FormatClass_FastRGBA32;
if (ImGui_ImplCPU_MatchesFormat(format, ImGui_ImplCPU_GetBuiltinPixelFormat(ImGui_ImplCPU_BuiltinFormat_BGRA8888)))
return ImGui_ImplCPU_FormatClass_FastBGRA32;
if (ImGui_ImplCPU_MatchesFormat(format, ImGui_ImplCPU_GetBuiltinPixelFormat(ImGui_ImplCPU_BuiltinFormat_ARGB8888)))
return ImGui_ImplCPU_FormatClass_FastARGB32;
if (ImGui_ImplCPU_MatchesFormat(format, ImGui_ImplCPU_GetBuiltinPixelFormat(ImGui_ImplCPU_BuiltinFormat_ABGR8888)))
return ImGui_ImplCPU_FormatClass_FastABGR32;
if (ImGui_ImplCPU_MatchesFormat(format, ImGui_ImplCPU_GetBuiltinPixelFormat(ImGui_ImplCPU_BuiltinFormat_RGB565)))
return ImGui_ImplCPU_FormatClass_RGB565;
if (ImGui_ImplCPU_MatchesFormat(format, ImGui_ImplCPU_GetBuiltinPixelFormat(ImGui_ImplCPU_BuiltinFormat_ARGB1555)))
return ImGui_ImplCPU_FormatClass_ARGB1555;
if (ImGui_ImplCPU_MatchesFormat(format, ImGui_ImplCPU_GetBuiltinPixelFormat(ImGui_ImplCPU_BuiltinFormat_RGBA4444)))
return ImGui_ImplCPU_FormatClass_RGBA4444;
if (ImGui_ImplCPU_MatchesFormat(format, ImGui_ImplCPU_GetBuiltinPixelFormat(ImGui_ImplCPU_BuiltinFormat_RGB888)))
return ImGui_ImplCPU_FormatClass_RGB888;
if (ImGui_ImplCPU_MatchesFormat(format, ImGui_ImplCPU_GetBuiltinPixelFormat(ImGui_ImplCPU_BuiltinFormat_BGR888)))
return ImGui_ImplCPU_FormatClass_BGR888;
if (ImGui_ImplCPU_MatchesFormat(format, ImGui_ImplCPU_GetBuiltinPixelFormat(ImGui_ImplCPU_BuiltinFormat_Alpha8)))
return ImGui_ImplCPU_FormatClass_Alpha8;
return ImGui_ImplCPU_FormatClass_Generic;
}
static inline ImGui_ImplCPU_FastFormat32 ImGui_ImplCPU_BuildFastFormat(const ImGui_ImplCPU_PixelFormat* format)
{
ImGui_ImplCPU_FastFormat32 out;
if (format == nullptr)
return out;
if (format->BitsPerPixel != 32 || format->BytesPerPixel != 4)
return out;
if (format->Rloss != 0 || format->Gloss != 0 || format->Bloss != 0)
return out;
if (format->Amask != 0 && format->Aloss != 0)
return out;
out.Rmask = format->Rmask;
out.Gmask = format->Gmask;
out.Bmask = format->Bmask;
out.Amask = format->Amask;
out.Rshift = format->Rshift;
out.Gshift = format->Gshift;
out.Bshift = format->Bshift;
out.Ashift = format->Ashift;
out.HasAlpha = format->Amask != 0;
out.Valid = true;
return out;
}
static inline ImU32 ImGui_ImplCPU_ReadPixelRaw(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y)
{
const unsigned char* p = surface.Pixels + (size_t)y * (size_t)surface.Pitch + (size_t)x * (size_t)surface.Format->BytesPerPixel;
switch (surface.Format->BytesPerPixel)
{
case 1:
return *p;
case 2:
{
ImU16 pixel = 0;
memcpy(&pixel, p, sizeof(pixel));
return pixel;
}
case 3:
#if IMGUI_IMPL_CPU_BIG_ENDIAN
return (ImU32)((p[0] << 16) | (p[1] << 8) | p[2]);
#else
return (ImU32)(p[0] | (p[1] << 8) | (p[2] << 16));
#endif
case 4:
{
ImU32 pixel = 0;
memcpy(&pixel, p, sizeof(pixel));
return pixel;
}
default:
return 0;
}
}
static inline void ImGui_ImplCPU_WritePixelRaw(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y, ImU32 pixel)
{
unsigned char* p = surface.Pixels + (size_t)y * (size_t)surface.Pitch + (size_t)x * (size_t)surface.Format->BytesPerPixel;
switch (surface.Format->BytesPerPixel)
{
case 1:
*p = (unsigned char)pixel;
return;
case 2:
{
ImU16 v = (ImU16)pixel;
memcpy(p, &v, sizeof(v));
return;
}
case 3:
#if IMGUI_IMPL_CPU_BIG_ENDIAN
p[0] = (unsigned char)((pixel >> 16) & 0xFF);
p[1] = (unsigned char)((pixel >> 8) & 0xFF);
p[2] = (unsigned char)(pixel & 0xFF);
#else
p[0] = (unsigned char)(pixel & 0xFF);
p[1] = (unsigned char)((pixel >> 8) & 0xFF);
p[2] = (unsigned char)((pixel >> 16) & 0xFF);
#endif
return;
case 4:
memcpy(p, &pixel, sizeof(pixel));
return;
default:
return;
}
}
static inline void ImGui_ImplCPU_UnpackFastRGBA(const ImGui_ImplCPU_FastFormat32& format, ImU32 pixel, unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a)
{
*r = (unsigned char)((pixel & format.Rmask) >> format.Rshift);
*g = (unsigned char)((pixel & format.Gmask) >> format.Gshift);
*b = (unsigned char)((pixel & format.Bmask) >> format.Bshift);
*a = format.HasAlpha ? (unsigned char)((pixel & format.Amask) >> format.Ashift) : 255;
}
static inline ImU32 ImGui_ImplCPU_PackFastRGBA(const ImGui_ImplCPU_FastFormat32& format, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
ImU32 pixel = (((ImU32)r << format.Rshift) & format.Rmask)
| (((ImU32)g << format.Gshift) & format.Gmask)
| (((ImU32)b << format.Bshift) & format.Bmask);
if (format.HasAlpha)
pixel |= (((ImU32)a << format.Ashift) & format.Amask);
return pixel;
}
static inline void ImGui_ImplCPU_UnpackGenericRGBA(const ImGui_ImplCPU_PixelFormat* format, ImU32 pixel, unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a)
{
*r = (format->Rmask != 0) ? ImGui_ImplCPU_ExpandBits((pixel & format->Rmask) >> format->Rshift, 8 - format->Rloss) : 0;
*g = (format->Gmask != 0) ? ImGui_ImplCPU_ExpandBits((pixel & format->Gmask) >> format->Gshift, 8 - format->Gloss) : 0;
*b = (format->Bmask != 0) ? ImGui_ImplCPU_ExpandBits((pixel & format->Bmask) >> format->Bshift, 8 - format->Bloss) : 0;
*a = (format->Amask != 0) ? ImGui_ImplCPU_ExpandBits((pixel & format->Amask) >> format->Ashift, 8 - format->Aloss) : 255;
}
static inline ImU32 ImGui_ImplCPU_PackGenericRGBA(const ImGui_ImplCPU_PixelFormat* format, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
ImU32 pixel = 0;
if (format->Rmask != 0)
pixel |= (ImGui_ImplCPU_CompressBits(r, 8 - format->Rloss) << format->Rshift) & format->Rmask;
if (format->Gmask != 0)
pixel |= (ImGui_ImplCPU_CompressBits(g, 8 - format->Gloss) << format->Gshift) & format->Gmask;
if (format->Bmask != 0)
pixel |= (ImGui_ImplCPU_CompressBits(b, 8 - format->Bloss) << format->Bshift) & format->Bmask;
if (format->Amask != 0)
pixel |= (ImGui_ImplCPU_CompressBits(a, 8 - format->Aloss) << format->Ashift) & format->Amask;
return pixel;
}
static inline ImU32 ImGui_ImplCPU_MapNearestPaletteColor(const ImGui_ImplCPU_PixelFormat* format, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
if (format->Palette == nullptr || format->PaletteSize <= 0)
return 0;
unsigned int best_distance = 0xFFFFFFFFu;
int best_index = 0;
for (int i = 0; i < format->PaletteSize; i++)
{
unsigned char pr, pg, pb, pa;
ImGui_ImplCPU_UnpackCanonicalRGBA(format->Palette[i], &pr, &pg, &pb, &pa);
int dr = (int)pr - (int)r;
int dg = (int)pg - (int)g;
int db = (int)pb - (int)b;
int da = (int)pa - (int)a;
unsigned int distance = (unsigned int)(dr * dr + dg * dg + db * db + da * da);
if (distance < best_distance)
{
best_distance = distance;
best_index = i;
if (distance == 0)
break;
}
}
return (ImU32)best_index;
}
static inline void ImGui_ImplCPU_ReadRGBAFast32(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y, unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a)
{
ImGui_ImplCPU_UnpackFastRGBA(surface.FastFormat, ImGui_ImplCPU_ReadPixelRaw(surface, x, y), r, g, b, a);
}
static inline unsigned char ImGui_ImplCPU_ReadAlphaFast32(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y)
{
if (!surface.FastFormat.HasAlpha)
return 255;
ImU32 pixel = ImGui_ImplCPU_ReadPixelRaw(surface, x, y);
return (unsigned char)((pixel & surface.FastFormat.Amask) >> surface.FastFormat.Ashift);
}
static inline ImU32 ImGui_ImplCPU_PackFast32(const ImGui_ImplCPU_SurfaceInfo& surface, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
return ImGui_ImplCPU_PackFastRGBA(surface.FastFormat, r, g, b, a);
}
static inline void ImGui_ImplCPU_ReadRGBAGenericSurface(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y, unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a)
{
ImGui_ImplCPU_UnpackGenericRGBA(surface.Format, ImGui_ImplCPU_ReadPixelRaw(surface, x, y), r, g, b, a);
}
static inline unsigned char ImGui_ImplCPU_ReadAlphaGenericSurface(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y)
{
if (surface.Format->Amask == 0)
return 255;
ImU32 pixel = ImGui_ImplCPU_ReadPixelRaw(surface, x, y);
return ImGui_ImplCPU_ExpandBits((pixel & surface.Format->Amask) >> surface.Format->Ashift, 8 - surface.Format->Aloss);
}
static inline ImU32 ImGui_ImplCPU_PackGenericSurface(const ImGui_ImplCPU_SurfaceInfo& surface, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
return ImGui_ImplCPU_PackGenericRGBA(surface.Format, r, g, b, a);
}
static inline void ImGui_ImplCPU_ReadRGBAAlpha8Surface(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y, unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a)
{
IM_UNUSED(surface);
IM_UNUSED(x);
IM_UNUSED(y);
*r = 255;
*g = 255;
*b = 255;
*a = surface.Pixels[(size_t)y * (size_t)surface.Pitch + (size_t)x];
}
static inline unsigned char ImGui_ImplCPU_ReadAlphaAlpha8Surface(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y)
{
return surface.Pixels[(size_t)y * (size_t)surface.Pitch + (size_t)x];
}
static inline ImU32 ImGui_ImplCPU_PackAlpha8Surface(const ImGui_ImplCPU_SurfaceInfo& surface, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
IM_UNUSED(surface);
IM_UNUSED(r);
IM_UNUSED(g);
IM_UNUSED(b);
return a;
}
static inline void ImGui_ImplCPU_ReadRGBAIndexedSurface(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y, unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a)
{
ImU32 index = ImGui_ImplCPU_ReadPixelRaw(surface, x, y);
if (surface.Format->Palette == nullptr || index >= (ImU32)surface.Format->PaletteSize)
{
*r = *g = *b = *a = 0;
return;
}
ImGui_ImplCPU_UnpackCanonicalRGBA(surface.Format->Palette[index], r, g, b, a);
}
static inline unsigned char ImGui_ImplCPU_ReadAlphaIndexedSurface(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y)
{
ImU32 index = ImGui_ImplCPU_ReadPixelRaw(surface, x, y);
if (surface.Format->Palette == nullptr || index >= (ImU32)surface.Format->PaletteSize)
return 0;
return (unsigned char)((surface.Format->Palette[index] >> IM_COL32_A_SHIFT) & 0xFF);
}
static inline ImU32 ImGui_ImplCPU_PackIndexedSurface(const ImGui_ImplCPU_SurfaceInfo& surface, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
if (surface.Format->MapColorFn != nullptr)
return surface.Format->MapColorFn(surface.Format, r, g, b, a);
return ImGui_ImplCPU_MapNearestPaletteColor(surface.Format, r, g, b, a);
}
static inline void ImGui_ImplCPU_ReadRGBARGB565(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y, unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a)
{
ImU32 pixel = ImGui_ImplCPU_ReadPixelRaw(surface, x, y);
*r = ImGui_ImplCPU_ExpandBits((pixel >> 11) & 0x1Fu, 5);
*g = ImGui_ImplCPU_ExpandBits((pixel >> 5) & 0x3Fu, 6);
*b = ImGui_ImplCPU_ExpandBits(pixel & 0x1Fu, 5);
*a = 255;
}
static inline unsigned char ImGui_ImplCPU_ReadAlphaOpaqueSurface(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y)
{
IM_UNUSED(surface);
IM_UNUSED(x);
IM_UNUSED(y);
return 255;
}
static inline ImU32 ImGui_ImplCPU_PackRGB565(const ImGui_ImplCPU_SurfaceInfo& surface, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
IM_UNUSED(surface);
IM_UNUSED(a);
return (ImU32)((ImGui_ImplCPU_CompressBits(r, 5) << 11) | (ImGui_ImplCPU_CompressBits(g, 6) << 5) | ImGui_ImplCPU_CompressBits(b, 5));
}
static inline void ImGui_ImplCPU_ReadRGBAARGB1555(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y, unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a)
{
ImU32 pixel = ImGui_ImplCPU_ReadPixelRaw(surface, x, y);
*r = ImGui_ImplCPU_ExpandBits((pixel >> 10) & 0x1Fu, 5);
*g = ImGui_ImplCPU_ExpandBits((pixel >> 5) & 0x1Fu, 5);
*b = ImGui_ImplCPU_ExpandBits(pixel & 0x1Fu, 5);
*a = (pixel & 0x8000u) ? 255 : 0;
}
static inline unsigned char ImGui_ImplCPU_ReadAlphaARGB1555(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y)
{
return (ImGui_ImplCPU_ReadPixelRaw(surface, x, y) & 0x8000u) ? 255 : 0;
}
static inline ImU32 ImGui_ImplCPU_PackARGB1555(const ImGui_ImplCPU_SurfaceInfo& surface, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
IM_UNUSED(surface);
return (ImU32)(((a >= 128 ? 1u : 0u) << 15) | (ImGui_ImplCPU_CompressBits(r, 5) << 10) | (ImGui_ImplCPU_CompressBits(g, 5) << 5) | ImGui_ImplCPU_CompressBits(b, 5));
}
static inline void ImGui_ImplCPU_ReadRGBARGBA4444(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y, unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a)
{
ImU32 pixel = ImGui_ImplCPU_ReadPixelRaw(surface, x, y);
*r = ImGui_ImplCPU_ExpandBits((pixel >> 12) & 0x0Fu, 4);
*g = ImGui_ImplCPU_ExpandBits((pixel >> 8) & 0x0Fu, 4);
*b = ImGui_ImplCPU_ExpandBits((pixel >> 4) & 0x0Fu, 4);
*a = ImGui_ImplCPU_ExpandBits(pixel & 0x0Fu, 4);
}
static inline unsigned char ImGui_ImplCPU_ReadAlphaRGBA4444(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y)
{
return ImGui_ImplCPU_ExpandBits(ImGui_ImplCPU_ReadPixelRaw(surface, x, y) & 0x0Fu, 4);
}
static inline ImU32 ImGui_ImplCPU_PackRGBA4444(const ImGui_ImplCPU_SurfaceInfo& surface, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
IM_UNUSED(surface);
return (ImU32)((ImGui_ImplCPU_CompressBits(r, 4) << 12) | (ImGui_ImplCPU_CompressBits(g, 4) << 8) | (ImGui_ImplCPU_CompressBits(b, 4) << 4) | ImGui_ImplCPU_CompressBits(a, 4));
}
static inline void ImGui_ImplCPU_ReadRGBARGB888(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y, unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a)
{
const unsigned char* p = surface.Pixels + (size_t)y * (size_t)surface.Pitch + (size_t)x * 3u;
#if IMGUI_IMPL_CPU_BIG_ENDIAN
*r = p[0];
*g = p[1];
*b = p[2];
#else
*r = p[0];
*g = p[1];
*b = p[2];
#endif
*a = 255;
}
static inline ImU32 ImGui_ImplCPU_PackRGB888(const ImGui_ImplCPU_SurfaceInfo& surface, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
IM_UNUSED(surface);
IM_UNUSED(a);
#if IMGUI_IMPL_CPU_BIG_ENDIAN
return (ImU32)((r << 16) | (g << 8) | b);
#else
return (ImU32)(r | (g << 8) | (b << 16));
#endif
}
static inline void ImGui_ImplCPU_ReadRGBABGR888(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y, unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a)
{
const unsigned char* p = surface.Pixels + (size_t)y * (size_t)surface.Pitch + (size_t)x * 3u;
#if IMGUI_IMPL_CPU_BIG_ENDIAN
*b = p[0];
*g = p[1];
*r = p[2];
#else
*b = p[0];
*g = p[1];
*r = p[2];
#endif
*a = 255;
}
static inline ImU32 ImGui_ImplCPU_PackBGR888(const ImGui_ImplCPU_SurfaceInfo& surface, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
IM_UNUSED(surface);
IM_UNUSED(a);
#if IMGUI_IMPL_CPU_BIG_ENDIAN
return (ImU32)((b << 16) | (g << 8) | r);
#else
return (ImU32)(b | (g << 8) | (r << 16));
#endif
}
static void ImGui_ImplCPU_InitSurfaceInfo(ImGui_ImplCPU_SurfaceInfo* out, const unsigned char* pixels, int pitch, int width, int height, const ImGui_ImplCPU_PixelFormat* format)
{
out->Pixels = (unsigned char*)pixels;
out->Pitch = pitch;
out->Width = width;
out->Height = height;
out->Format = format ? format : ImGui_ImplCPU_GetDefaultPixelFormat();
out->Class = ImGui_ImplCPU_ClassifyFormat(out->Format);
out->FastFormat = ImGui_ImplCPU_BuildFastFormat(out->Format);
out->HasAlpha = out->Format->Amask != 0 || out->Class == ImGui_ImplCPU_FormatClass_Alpha8;
out->IsAlphaOnly = out->Class == ImGui_ImplCPU_FormatClass_Alpha8;
out->IsIndexed = out->Class == ImGui_ImplCPU_FormatClass_Indexed;
out->IsFast = (out->Class == ImGui_ImplCPU_FormatClass_FastRGBA32
|| out->Class == ImGui_ImplCPU_FormatClass_FastBGRA32
|| out->Class == ImGui_ImplCPU_FormatClass_FastARGB32
|| out->Class == ImGui_ImplCPU_FormatClass_FastABGR32)
&& out->FastFormat.Valid
&& ImGui_ImplCPU_CanUseFastPath(out->Pixels, out->Pitch);
switch (out->Class)
{
case ImGui_ImplCPU_FormatClass_FastRGBA32:
case ImGui_ImplCPU_FormatClass_FastBGRA32:
case ImGui_ImplCPU_FormatClass_FastARGB32:
case ImGui_ImplCPU_FormatClass_FastABGR32:
out->ReadRGBAFn = ImGui_ImplCPU_ReadRGBAFast32;
out->ReadAlphaFn = ImGui_ImplCPU_ReadAlphaFast32;
out->PackRGBAFn = ImGui_ImplCPU_PackFast32;
break;
case ImGui_ImplCPU_FormatClass_RGB565:
out->ReadRGBAFn = ImGui_ImplCPU_ReadRGBARGB565;
out->ReadAlphaFn = ImGui_ImplCPU_ReadAlphaOpaqueSurface;
out->PackRGBAFn = ImGui_ImplCPU_PackRGB565;
break;
case ImGui_ImplCPU_FormatClass_ARGB1555:
out->ReadRGBAFn = ImGui_ImplCPU_ReadRGBAARGB1555;
out->ReadAlphaFn = ImGui_ImplCPU_ReadAlphaARGB1555;
out->PackRGBAFn = ImGui_ImplCPU_PackARGB1555;
break;
case ImGui_ImplCPU_FormatClass_RGBA4444:
out->ReadRGBAFn = ImGui_ImplCPU_ReadRGBARGBA4444;
out->ReadAlphaFn = ImGui_ImplCPU_ReadAlphaRGBA4444;
out->PackRGBAFn = ImGui_ImplCPU_PackRGBA4444;
break;
case ImGui_ImplCPU_FormatClass_RGB888:
out->ReadRGBAFn = ImGui_ImplCPU_ReadRGBARGB888;
out->ReadAlphaFn = ImGui_ImplCPU_ReadAlphaOpaqueSurface;
out->PackRGBAFn = ImGui_ImplCPU_PackRGB888;
break;
case ImGui_ImplCPU_FormatClass_BGR888:
out->ReadRGBAFn = ImGui_ImplCPU_ReadRGBABGR888;
out->ReadAlphaFn = ImGui_ImplCPU_ReadAlphaOpaqueSurface;
out->PackRGBAFn = ImGui_ImplCPU_PackBGR888;
break;
case ImGui_ImplCPU_FormatClass_Alpha8:
out->ReadRGBAFn = ImGui_ImplCPU_ReadRGBAAlpha8Surface;
out->ReadAlphaFn = ImGui_ImplCPU_ReadAlphaAlpha8Surface;
out->PackRGBAFn = ImGui_ImplCPU_PackAlpha8Surface;
break;
case ImGui_ImplCPU_FormatClass_Indexed:
out->ReadRGBAFn = ImGui_ImplCPU_ReadRGBAIndexedSurface;
out->ReadAlphaFn = ImGui_ImplCPU_ReadAlphaIndexedSurface;
out->PackRGBAFn = ImGui_ImplCPU_PackIndexedSurface;
break;
case ImGui_ImplCPU_FormatClass_Generic:
default:
out->ReadRGBAFn = ImGui_ImplCPU_ReadRGBAGenericSurface;
out->ReadAlphaFn = ImGui_ImplCPU_ReadAlphaGenericSurface;
out->PackRGBAFn = ImGui_ImplCPU_PackGenericSurface;
break;
}
}
static inline void ImGui_ImplCPU_InitTextureInfo(ImGui_ImplCPU_SurfaceInfo* out, const ImGui_ImplCPU_Texture* texture)
{
ImGui_ImplCPU_InitSurfaceInfo(out,
texture ? texture->Pixels : nullptr,
texture ? texture->Pitch : 0,
texture ? texture->Width : 0,
texture ? texture->Height : 0,
texture ? texture->Format : nullptr);
}
static inline void ImGui_ImplCPU_InitBufferInfo(ImGui_ImplCPU_SurfaceInfo* out, ImGui_ImplCPU_Framebuffer* framebuffer)
{
ImGui_ImplCPU_InitSurfaceInfo(out,
framebuffer ? framebuffer->Pixels : nullptr,
framebuffer ? framebuffer->Pitch : 0,
framebuffer ? framebuffer->Width : 0,
framebuffer ? framebuffer->Height : 0,
framebuffer ? framebuffer->Format : nullptr);
}
static inline void ImGui_ImplCPU_ReadPixelRGBA(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y, unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a)
{
surface.ReadRGBAFn(surface, x, y, r, g, b, a);
}
static inline unsigned char ImGui_ImplCPU_ReadPixelAlpha(const ImGui_ImplCPU_SurfaceInfo& surface, int x, int y)
{
return surface.ReadAlphaFn(surface, x, y);
}
static inline void ImGui_ImplCPU_BlendPixelFast(const ImGui_ImplCPU_SurfaceInfo& target, ImU32* dst, unsigned char sr, unsigned char sg, unsigned char sb, unsigned char sa)
{
if (sa == 0)
return;
if (sa == 255)
{
*dst = ImGui_ImplCPU_PackFastRGBA(target.FastFormat, sr, sg, sb, target.FastFormat.HasAlpha ? 255 : 255);
return;
}
unsigned char dr, dg, db, da;
ImGui_ImplCPU_UnpackFastRGBA(target.FastFormat, *dst, &dr, &dg, &db, &da);
unsigned char inv_a = (unsigned char)(255 - sa);
unsigned char out_r = (unsigned char)(ImGui_ImplCPU_Mul255(sr, sa) + ImGui_ImplCPU_Mul255(dr, inv_a));
unsigned char out_g = (unsigned char)(ImGui_ImplCPU_Mul255(sg, sa) + ImGui_ImplCPU_Mul255(dg, inv_a));
unsigned char out_b = (unsigned char)(ImGui_ImplCPU_Mul255(sb, sa) + ImGui_ImplCPU_Mul255(db, inv_a));
unsigned char out_a = target.FastFormat.HasAlpha ? (unsigned char)(sa + ImGui_ImplCPU_Mul255(da, inv_a)) : 255;
*dst = ImGui_ImplCPU_PackFastRGBA(target.FastFormat, out_r, out_g, out_b, out_a);
}
static inline void ImGui_ImplCPU_BlendPixelGeneric(const ImGui_ImplCPU_SurfaceInfo& target, int x, int y, unsigned char sr, unsigned char sg, unsigned char sb, unsigned char sa)
{
if (sa == 0)
return;
if (sa == 255)
{
ImU32 pixel = target.PackRGBAFn(target, sr, sg, sb, target.HasAlpha ? 255 : 255);
ImGui_ImplCPU_WritePixelRaw(target, x, y, pixel);
return;
}
unsigned char dr, dg, db, da;
ImGui_ImplCPU_ReadPixelRGBA(target, x, y, &dr, &dg, &db, &da);
unsigned char inv_a = (unsigned char)(255 - sa);
unsigned char out_r = (unsigned char)(ImGui_ImplCPU_Mul255(sr, sa) + ImGui_ImplCPU_Mul255(dr, inv_a));
unsigned char out_g = (unsigned char)(ImGui_ImplCPU_Mul255(sg, sa) + ImGui_ImplCPU_Mul255(dg, inv_a));
unsigned char out_b = (unsigned char)(ImGui_ImplCPU_Mul255(sb, sa) + ImGui_ImplCPU_Mul255(db, inv_a));
unsigned char out_a = target.HasAlpha ? (unsigned char)(sa + ImGui_ImplCPU_Mul255(da, inv_a)) : 255;
ImU32 pixel = target.PackRGBAFn(target, out_r, out_g, out_b, out_a);
ImGui_ImplCPU_WritePixelRaw(target, x, y, pixel);
}
static inline bool ImGui_ImplCPU_SameColor(const ImGui_ImplCPU_RasterVertex& a, const ImGui_ImplCPU_RasterVertex& b)
{
return a.R == b.R && a.G == b.G && a.B == b.B && a.A == b.A;
}
static inline void ImGui_ImplCPU_SetupRasterVertex(ImGui_ImplCPU_RasterVertex* out, const ImDrawVert& v, const ImVec2& display_pos, const ImVec2& framebuffer_scale)
{
out->X = (v.pos.x - display_pos.x) * framebuffer_scale.x;
out->Y = (v.pos.y - display_pos.y) * framebuffer_scale.y;
out->U = v.uv.x;
out->V = v.uv.y;
out->R = (float)((v.col >> IM_COL32_R_SHIFT) & 0xFF);
out->G = (float)((v.col >> IM_COL32_G_SHIFT) & 0xFF);
out->B = (float)((v.col >> IM_COL32_B_SHIFT) & 0xFF);
out->A = (float)((v.col >> IM_COL32_A_SHIFT) & 0xFF);
}
static void ImGui_ImplCPU_BuildVertexCache(ImVector<ImGui_ImplCPU_RasterVertex>* out, const ImDrawList* draw_list, const ImVec2& display_pos, const ImVec2& framebuffer_scale)
{
out->resize(draw_list->VtxBuffer.Size);
for (int vtx_i = 0; vtx_i < draw_list->VtxBuffer.Size; vtx_i++)
ImGui_ImplCPU_SetupRasterVertex(&out->Data[vtx_i], draw_list->VtxBuffer[vtx_i], display_pos, framebuffer_scale);
}
static void ImGui_ImplCPU_RenderTriangleFast(const ImGui_ImplCPU_SurfaceInfo& target, const ImGui_ImplCPU_SurfaceInfo* texture, bool texture_is_alpha, const ImGui_ImplCPU_RasterVertex& v0, const ImGui_ImplCPU_RasterVertex& v1, const ImGui_ImplCPU_RasterVertex& v2, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y)
{
int min_x = std::max((int)std::floor(std::min(v0.X, std::min(v1.X, v2.X))), clip_min_x);
int min_y = std::max((int)std::floor(std::min(v0.Y, std::min(v1.Y, v2.Y))), clip_min_y);
int max_x = std::min((int)std::ceil(std::max(v0.X, std::max(v1.X, v2.X))) - 1, clip_max_x - 1);
int max_y = std::min((int)std::ceil(std::max(v0.Y, std::max(v1.Y, v2.Y))) - 1, clip_max_y - 1);
if (min_x > max_x || min_y > max_y)
return;
float area = ImGui_ImplCPU_Edge(v0.X, v0.Y, v1.X, v1.Y, v2.X, v2.Y);
if (area == 0.0f)
return;
float sign = (area < 0.0f) ? -1.0f : 1.0f;
area *= sign;
float e0_dx = (v1.Y - v2.Y) * sign;
float e0_dy = (v2.X - v1.X) * sign;
float e1_dx = (v2.Y - v0.Y) * sign;
float e1_dy = (v0.X - v2.X) * sign;
float e2_dx = (v0.Y - v1.Y) * sign;
float e2_dy = (v1.X - v0.X) * sign;
float inv_area = 1.0f / area;
float dw0_dx = e0_dx * inv_area;
float dw0_dy = e0_dy * inv_area;
float dw1_dx = e1_dx * inv_area;
float dw1_dy = e1_dy * inv_area;
float dw2_dx = e2_dx * inv_area;
float dw2_dy = e2_dy * inv_area;
float dr_dx = dw0_dx * v0.R + dw1_dx * v1.R + dw2_dx * v2.R;
float dg_dx = dw0_dx * v0.G + dw1_dx * v1.G + dw2_dx * v2.G;
float db_dx = dw0_dx * v0.B + dw1_dx * v1.B + dw2_dx * v2.B;
float da_dx = dw0_dx * v0.A + dw1_dx * v1.A + dw2_dx * v2.A;
float du_dx = dw0_dx * v0.U + dw1_dx * v1.U + dw2_dx * v2.U;
float dv_dx = dw0_dx * v0.V + dw1_dx * v1.V + dw2_dx * v2.V;
float dr_dy = dw0_dy * v0.R + dw1_dy * v1.R + dw2_dy * v2.R;
float dg_dy = dw0_dy * v0.G + dw1_dy * v1.G + dw2_dy * v2.G;
float db_dy = dw0_dy * v0.B + dw1_dy * v1.B + dw2_dy * v2.B;
float da_dy = dw0_dy * v0.A + dw1_dy * v1.A + dw2_dy * v2.A;
float du_dy = dw0_dy * v0.U + dw1_dy * v1.U + dw2_dy * v2.U;
float dv_dy = dw0_dy * v0.V + dw1_dy * v1.V + dw2_dy * v2.V;
float start_x = (float)min_x + 0.5f;
float start_y = (float)min_y + 0.5f;
float e0_row = ImGui_ImplCPU_Edge(v1.X, v1.Y, v2.X, v2.Y, start_x, start_y) * sign;
float e1_row = ImGui_ImplCPU_Edge(v2.X, v2.Y, v0.X, v0.Y, start_x, start_y) * sign;
float e2_row = ImGui_ImplCPU_Edge(v0.X, v0.Y, v1.X, v1.Y, start_x, start_y) * sign;
float w0_row = e0_row * inv_area;
float w1_row = e1_row * inv_area;
float w2_row = e2_row * inv_area;
float r_row = w0_row * v0.R + w1_row * v1.R + w2_row * v2.R;
float g_row = w0_row * v0.G + w1_row * v1.G + w2_row * v2.G;
float b_row = w0_row * v0.B + w1_row * v1.B + w2_row * v2.B;
float a_row = w0_row * v0.A + w1_row * v1.A + w2_row * v2.A;
float u_row = w0_row * v0.U + w1_row * v1.U + w2_row * v2.U;
float v_row = w0_row * v0.V + w1_row * v1.V + w2_row * v2.V;
int texture_max_x = texture ? (texture->Width - 1) : 0;
int texture_max_y = texture ? (texture->Height - 1) : 0;
if (texture == nullptr)
{
for (int y = min_y; y <= max_y; y++)
{
float e0 = e0_row;
float e1 = e1_row;
float e2 = e2_row;
float r = r_row;
float g = g_row;
float b = b_row;
float a = a_row;
ImU32* dst = (ImU32*)(void*)(target.Pixels + (size_t)y * (size_t)target.Pitch) + min_x;
for (int x = min_x; x <= max_x; x++)
{
if (e0 >= 0.0f && e1 >= 0.0f && e2 >= 0.0f)
ImGui_ImplCPU_BlendPixelFast(target, dst, ImGui_ImplCPU_ClampByte(r), ImGui_ImplCPU_ClampByte(g), ImGui_ImplCPU_ClampByte(b), ImGui_ImplCPU_ClampByte(a));
dst++;
e0 += e0_dx;
e1 += e1_dx;
e2 += e2_dx;
r += dr_dx;
g += dg_dx;
b += db_dx;
a += da_dx;
}
e0_row += e0_dy;
e1_row += e1_dy;
e2_row += e2_dy;
r_row += dr_dy;
g_row += dg_dy;
b_row += db_dy;
a_row += da_dy;
}
return;
}
if (texture_is_alpha)
{
for (int y = min_y; y <= max_y; y++)
{
float e0 = e0_row;
float e1 = e1_row;
float e2 = e2_row;
float r = r_row;
float g = g_row;
float b = b_row;
float a = a_row;
float u = u_row;
float v = v_row;
ImU32* dst = (ImU32*)(void*)(target.Pixels + (size_t)y * (size_t)target.Pitch) + min_x;
for (int x = min_x; x <= max_x; x++)
{
if (e0 >= 0.0f && e1 >= 0.0f && e2 >= 0.0f)
{
int tx = ImGui_ImplCPU_ClampInt((int)(u * (float)texture_max_x + 0.5f), 0, texture_max_x);
int ty = ImGui_ImplCPU_ClampInt((int)(v * (float)texture_max_y + 0.5f), 0, texture_max_y);
ImGui_ImplCPU_BlendPixelFast(target, dst,
ImGui_ImplCPU_ClampByte(r),
ImGui_ImplCPU_ClampByte(g),
ImGui_ImplCPU_ClampByte(b),
ImGui_ImplCPU_Mul255(ImGui_ImplCPU_ClampByte(a), ImGui_ImplCPU_ReadPixelAlpha(*texture, tx, ty)));
}
dst++;
e0 += e0_dx;
e1 += e1_dx;
e2 += e2_dx;
r += dr_dx;
g += dg_dx;
b += db_dx;
a += da_dx;
u += du_dx;
v += dv_dx;
}
e0_row += e0_dy;
e1_row += e1_dy;
e2_row += e2_dy;
r_row += dr_dy;
g_row += dg_dy;
b_row += db_dy;
a_row += da_dy;
u_row += du_dy;
v_row += dv_dy;
}
return;
}
for (int y = min_y; y <= max_y; y++)
{
float e0 = e0_row;
float e1 = e1_row;
float e2 = e2_row;
float r = r_row;
float g = g_row;
float b = b_row;
float a = a_row;
float u = u_row;
float v = v_row;
ImU32* dst = (ImU32*)(void*)(target.Pixels + (size_t)y * (size_t)target.Pitch) + min_x;
for (int x = min_x; x <= max_x; x++)
{
if (e0 >= 0.0f && e1 >= 0.0f && e2 >= 0.0f)
{
int tx = ImGui_ImplCPU_ClampInt((int)(u * (float)texture_max_x + 0.5f), 0, texture_max_x);
int ty = ImGui_ImplCPU_ClampInt((int)(v * (float)texture_max_y + 0.5f), 0, texture_max_y);
unsigned char tr, tg, tb, ta;
ImGui_ImplCPU_ReadPixelRGBA(*texture, tx, ty, &tr, &tg, &tb, &ta);
ImGui_ImplCPU_BlendPixelFast(target, dst,
ImGui_ImplCPU_Mul255(ImGui_ImplCPU_ClampByte(r), tr),
ImGui_ImplCPU_Mul255(ImGui_ImplCPU_ClampByte(g), tg),
ImGui_ImplCPU_Mul255(ImGui_ImplCPU_ClampByte(b), tb),
ImGui_ImplCPU_Mul255(ImGui_ImplCPU_ClampByte(a), ta));
}
dst++;
e0 += e0_dx;
e1 += e1_dx;
e2 += e2_dx;
r += dr_dx;
g += dg_dx;
b += db_dx;
a += da_dx;
u += du_dx;
v += dv_dx;
}
e0_row += e0_dy;
e1_row += e1_dy;
e2_row += e2_dy;
r_row += dr_dy;
g_row += dg_dy;
b_row += db_dy;
a_row += da_dy;
u_row += du_dy;
v_row += dv_dy;
}
}
static void ImGui_ImplCPU_RenderTriangleGeneric(const ImGui_ImplCPU_SurfaceInfo& target, const ImGui_ImplCPU_SurfaceInfo* texture, bool texture_is_alpha, const ImGui_ImplCPU_RasterVertex& v0, const ImGui_ImplCPU_RasterVertex& v1, const ImGui_ImplCPU_RasterVertex& v2, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y)
{
int min_x = std::max((int)std::floor(std::min(v0.X, std::min(v1.X, v2.X))), clip_min_x);
int min_y = std::max((int)std::floor(std::min(v0.Y, std::min(v1.Y, v2.Y))), clip_min_y);
int max_x = std::min((int)std::ceil(std::max(v0.X, std::max(v1.X, v2.X))) - 1, clip_max_x - 1);
int max_y = std::min((int)std::ceil(std::max(v0.Y, std::max(v1.Y, v2.Y))) - 1, clip_max_y - 1);
if (min_x > max_x || min_y > max_y)
return;
float area = ImGui_ImplCPU_Edge(v0.X, v0.Y, v1.X, v1.Y, v2.X, v2.Y);
if (area == 0.0f)
return;
float sign = (area < 0.0f) ? -1.0f : 1.0f;
area *= sign;
float e0_dx = (v1.Y - v2.Y) * sign;
float e0_dy = (v2.X - v1.X) * sign;
float e1_dx = (v2.Y - v0.Y) * sign;
float e1_dy = (v0.X - v2.X) * sign;
float e2_dx = (v0.Y - v1.Y) * sign;
float e2_dy = (v1.X - v0.X) * sign;
float inv_area = 1.0f / area;
float dw0_dx = e0_dx * inv_area;
float dw0_dy = e0_dy * inv_area;
float dw1_dx = e1_dx * inv_area;
float dw1_dy = e1_dy * inv_area;
float dw2_dx = e2_dx * inv_area;
float dw2_dy = e2_dy * inv_area;
float dr_dx = dw0_dx * v0.R + dw1_dx * v1.R + dw2_dx * v2.R;
float dg_dx = dw0_dx * v0.G + dw1_dx * v1.G + dw2_dx * v2.G;
float db_dx = dw0_dx * v0.B + dw1_dx * v1.B + dw2_dx * v2.B;
float da_dx = dw0_dx * v0.A + dw1_dx * v1.A + dw2_dx * v2.A;
float du_dx = dw0_dx * v0.U + dw1_dx * v1.U + dw2_dx * v2.U;
float dv_dx = dw0_dx * v0.V + dw1_dx * v1.V + dw2_dx * v2.V;
float dr_dy = dw0_dy * v0.R + dw1_dy * v1.R + dw2_dy * v2.R;
float dg_dy = dw0_dy * v0.G + dw1_dy * v1.G + dw2_dy * v2.G;
float db_dy = dw0_dy * v0.B + dw1_dy * v1.B + dw2_dy * v2.B;
float da_dy = dw0_dy * v0.A + dw1_dy * v1.A + dw2_dy * v2.A;
float du_dy = dw0_dy * v0.U + dw1_dy * v1.U + dw2_dy * v2.U;
float dv_dy = dw0_dy * v0.V + dw1_dy * v1.V + dw2_dy * v2.V;
float start_x = (float)min_x + 0.5f;
float start_y = (float)min_y + 0.5f;
float e0_row = ImGui_ImplCPU_Edge(v1.X, v1.Y, v2.X, v2.Y, start_x, start_y) * sign;
float e1_row = ImGui_ImplCPU_Edge(v2.X, v2.Y, v0.X, v0.Y, start_x, start_y) * sign;
float e2_row = ImGui_ImplCPU_Edge(v0.X, v0.Y, v1.X, v1.Y, start_x, start_y) * sign;
float w0_row = e0_row * inv_area;
float w1_row = e1_row * inv_area;
float w2_row = e2_row * inv_area;
float r_row = w0_row * v0.R + w1_row * v1.R + w2_row * v2.R;
float g_row = w0_row * v0.G + w1_row * v1.G + w2_row * v2.G;
float b_row = w0_row * v0.B + w1_row * v1.B + w2_row * v2.B;
float a_row = w0_row * v0.A + w1_row * v1.A + w2_row * v2.A;
float u_row = w0_row * v0.U + w1_row * v1.U + w2_row * v2.U;
float v_row = w0_row * v0.V + w1_row * v1.V + w2_row * v2.V;
int texture_max_x = texture ? (texture->Width - 1) : 0;
int texture_max_y = texture ? (texture->Height - 1) : 0;
if (texture == nullptr)
{
for (int y = min_y; y <= max_y; y++)
{
float e0 = e0_row;
float e1 = e1_row;
float e2 = e2_row;
float r = r_row;
float g = g_row;
float b = b_row;
float a = a_row;
for (int x = min_x; x <= max_x; x++)
{
if (e0 >= 0.0f && e1 >= 0.0f && e2 >= 0.0f)
ImGui_ImplCPU_BlendPixelGeneric(target, x, y, ImGui_ImplCPU_ClampByte(r), ImGui_ImplCPU_ClampByte(g), ImGui_ImplCPU_ClampByte(b), ImGui_ImplCPU_ClampByte(a));
e0 += e0_dx;
e1 += e1_dx;
e2 += e2_dx;
r += dr_dx;
g += dg_dx;
b += db_dx;
a += da_dx;
}
e0_row += e0_dy;
e1_row += e1_dy;
e2_row += e2_dy;
r_row += dr_dy;
g_row += dg_dy;
b_row += db_dy;
a_row += da_dy;
}
return;
}
if (texture_is_alpha)
{
for (int y = min_y; y <= max_y; y++)
{
float e0 = e0_row;
float e1 = e1_row;
float e2 = e2_row;
float r = r_row;
float g = g_row;
float b = b_row;
float a = a_row;
float u = u_row;
float v = v_row;
for (int x = min_x; x <= max_x; x++)
{
if (e0 >= 0.0f && e1 >= 0.0f && e2 >= 0.0f)
{
int tx = ImGui_ImplCPU_ClampInt((int)(u * (float)texture_max_x + 0.5f), 0, texture_max_x);
int ty = ImGui_ImplCPU_ClampInt((int)(v * (float)texture_max_y + 0.5f), 0, texture_max_y);
ImGui_ImplCPU_BlendPixelGeneric(target, x, y,
ImGui_ImplCPU_ClampByte(r),
ImGui_ImplCPU_ClampByte(g),
ImGui_ImplCPU_ClampByte(b),
ImGui_ImplCPU_Mul255(ImGui_ImplCPU_ClampByte(a), ImGui_ImplCPU_ReadPixelAlpha(*texture, tx, ty)));
}
e0 += e0_dx;
e1 += e1_dx;
e2 += e2_dx;
r += dr_dx;
g += dg_dx;
b += db_dx;
a += da_dx;
u += du_dx;
v += dv_dx;
}
e0_row += e0_dy;
e1_row += e1_dy;
e2_row += e2_dy;
r_row += dr_dy;
g_row += dg_dy;
b_row += db_dy;
a_row += da_dy;
u_row += du_dy;
v_row += dv_dy;
}
return;
}
for (int y = min_y; y <= max_y; y++)
{
float e0 = e0_row;
float e1 = e1_row;
float e2 = e2_row;
float r = r_row;
float g = g_row;
float b = b_row;
float a = a_row;
float u = u_row;
float v = v_row;
for (int x = min_x; x <= max_x; x++)
{
if (e0 >= 0.0f && e1 >= 0.0f && e2 >= 0.0f)
{
int tx = ImGui_ImplCPU_ClampInt((int)(u * (float)texture_max_x + 0.5f), 0, texture_max_x);
int ty = ImGui_ImplCPU_ClampInt((int)(v * (float)texture_max_y + 0.5f), 0, texture_max_y);
unsigned char tr, tg, tb, ta;
ImGui_ImplCPU_ReadPixelRGBA(*texture, tx, ty, &tr, &tg, &tb, &ta);
ImGui_ImplCPU_BlendPixelGeneric(target, x, y,
ImGui_ImplCPU_Mul255(ImGui_ImplCPU_ClampByte(r), tr),
ImGui_ImplCPU_Mul255(ImGui_ImplCPU_ClampByte(g), tg),
ImGui_ImplCPU_Mul255(ImGui_ImplCPU_ClampByte(b), tb),
ImGui_ImplCPU_Mul255(ImGui_ImplCPU_ClampByte(a), ta));
}
e0 += e0_dx;
e1 += e1_dx;
e2 += e2_dx;
r += dr_dx;
g += dg_dx;
b += db_dx;
a += da_dx;
u += du_dx;
v += dv_dx;
}
e0_row += e0_dy;
e1_row += e1_dy;
e2_row += e2_dy;
r_row += dr_dy;
g_row += dg_dy;
b_row += db_dy;
a_row += da_dy;
u_row += du_dy;
v_row += dv_dy;
}
}
static bool ImGui_ImplCPU_DetectQuad(const ImGui_ImplCPU_RasterVertex* vertices, const ImDrawIdx* idx_buffer, int vtx_offset, unsigned int elem_offset, ImGui_ImplCPU_Quad* out)
{
int unique_indices[4];
int unique_count = 0;
for (int i = 0; i < 6; i++)
{
int idx = vtx_offset + idx_buffer[elem_offset + i];
bool found = false;
for (int j = 0; j < unique_count; j++)
if (unique_indices[j] == idx)
found = true;
if (!found)
{
if (unique_count >= 4)
return false;
unique_indices[unique_count++] = idx;
}
}
if (unique_count != 4)
return false;
const ImGui_ImplCPU_RasterVertex* candidates[4];
for (int i = 0; i < 4; i++)
candidates[i] = &vertices[unique_indices[i]];
float min_x = candidates[0]->X;
float max_x = candidates[0]->X;
float min_y = candidates[0]->Y;
float max_y = candidates[0]->Y;
for (int i = 1; i < 4; i++)
{
min_x = std::min(min_x, candidates[i]->X);
max_x = std::max(max_x, candidates[i]->X);
min_y = std::min(min_y, candidates[i]->Y);
max_y = std::max(max_y, candidates[i]->Y);
}
if (!(max_x > min_x) || !(max_y > min_y))
return false;
out->TL = nullptr;
out->TR = nullptr;
out->BR = nullptr;
out->BL = nullptr;
for (int i = 0; i < 4; i++)
{
const ImGui_ImplCPU_RasterVertex* v = candidates[i];
if (ImGui_ImplCPU_NearEqual(v->X, min_x) && ImGui_ImplCPU_NearEqual(v->Y, min_y))
out->TL = v;
else if (ImGui_ImplCPU_NearEqual(v->X, max_x) && ImGui_ImplCPU_NearEqual(v->Y, min_y))
out->TR = v;
else if (ImGui_ImplCPU_NearEqual(v->X, max_x) && ImGui_ImplCPU_NearEqual(v->Y, max_y))
out->BR = v;
else if (ImGui_ImplCPU_NearEqual(v->X, min_x) && ImGui_ImplCPU_NearEqual(v->Y, max_y))
out->BL = v;
else
return false;
}
return out->TL != nullptr && out->TR != nullptr && out->BR != nullptr && out->BL != nullptr;
}
static bool ImGui_ImplCPU_QuadHasConstantColor(const ImGui_ImplCPU_Quad& quad)
{
return ImGui_ImplCPU_SameColor(*quad.TL, *quad.TR) && ImGui_ImplCPU_SameColor(*quad.TL, *quad.BR) && ImGui_ImplCPU_SameColor(*quad.TL, *quad.BL);
}
static bool ImGui_ImplCPU_QuadHasLinearUV(const ImGui_ImplCPU_Quad& quad)
{
if (!ImGui_ImplCPU_NearEqual(quad.TL->Y, quad.TR->Y) || !ImGui_ImplCPU_NearEqual(quad.BL->Y, quad.BR->Y) || !ImGui_ImplCPU_NearEqual(quad.TL->X, quad.BL->X) || !ImGui_ImplCPU_NearEqual(quad.TR->X, quad.BR->X))
return false;
float u = quad.TL->U + (quad.TR->U - quad.TL->U) + (quad.BL->U - quad.TL->U);
float v = quad.TL->V + (quad.TR->V - quad.TL->V) + (quad.BL->V - quad.TL->V);
return ImGui_ImplCPU_NearEqual(u, quad.BR->U) && ImGui_ImplCPU_NearEqual(v, quad.BR->V);
}
static bool ImGui_ImplCPU_QuadGetConstantTexel(const ImGui_ImplCPU_SurfaceInfo& texture, const ImGui_ImplCPU_Quad& quad, int* out_x, int* out_y)
{
int max_x = texture.Width - 1;
int max_y = texture.Height - 1;
int tx = ImGui_ImplCPU_ClampInt((int)(quad.TL->U * (float)max_x + 0.5f), 0, max_x);
int ty = ImGui_ImplCPU_ClampInt((int)(quad.TL->V * (float)max_y + 0.5f), 0, max_y);
int corner_tx[3] =
{
ImGui_ImplCPU_ClampInt((int)(quad.TR->U * (float)max_x + 0.5f), 0, max_x),
ImGui_ImplCPU_ClampInt((int)(quad.BR->U * (float)max_x + 0.5f), 0, max_x),
ImGui_ImplCPU_ClampInt((int)(quad.BL->U * (float)max_x + 0.5f), 0, max_x)
};
int corner_ty[3] =
{
ImGui_ImplCPU_ClampInt((int)(quad.TR->V * (float)max_y + 0.5f), 0, max_y),
ImGui_ImplCPU_ClampInt((int)(quad.BR->V * (float)max_y + 0.5f), 0, max_y),
ImGui_ImplCPU_ClampInt((int)(quad.BL->V * (float)max_y + 0.5f), 0, max_y)
};
for (int i = 0; i < 3; i++)
if (corner_tx[i] != tx || corner_ty[i] != ty)
return false;
*out_x = tx;
*out_y = ty;
return true;
}
static void ImGui_ImplCPU_RenderSolidRectFast(const ImGui_ImplCPU_SurfaceInfo& target, const ImGui_ImplCPU_Quad& quad, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
int min_x = std::max((int)ceilf(quad.TL->X - 0.5f), clip_min_x);
int max_x = std::min((int)floorf(quad.TR->X - 0.5f), clip_max_x - 1);
int min_y = std::max((int)ceilf(quad.TL->Y - 0.5f), clip_min_y);
int max_y = std::min((int)floorf(quad.BL->Y - 0.5f), clip_max_y - 1);
if (min_x > max_x || min_y > max_y)
return;
int count = max_x - min_x + 1;
if (a == 255)
{
ImU32 pixel = ImGui_ImplCPU_PackFastRGBA(target.FastFormat, r, g, b, target.FastFormat.HasAlpha ? 255 : 255);
for (int y = min_y; y <= max_y; y++)
{
ImU32* dst = (ImU32*)(void*)(target.Pixels + (size_t)y * (size_t)target.Pitch) + min_x;
std::fill_n(dst, count, pixel);
}
return;
}
for (int y = min_y; y <= max_y; y++)
{
ImU32* dst = (ImU32*)(void*)(target.Pixels + (size_t)y * (size_t)target.Pitch) + min_x;
for (int x = 0; x < count; x++)
ImGui_ImplCPU_BlendPixelFast(target, dst + x, r, g, b, a);
}
}
static void ImGui_ImplCPU_RenderSolidRectGeneric(const ImGui_ImplCPU_SurfaceInfo& target, const ImGui_ImplCPU_Quad& quad, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
int min_x = std::max((int)ceilf(quad.TL->X - 0.5f), clip_min_x);
int max_x = std::min((int)floorf(quad.TR->X - 0.5f), clip_max_x - 1);
int min_y = std::max((int)ceilf(quad.TL->Y - 0.5f), clip_min_y);
int max_y = std::min((int)floorf(quad.BL->Y - 0.5f), clip_max_y - 1);
if (min_x > max_x || min_y > max_y)
return;
ImU32 opaque_pixel = 0;
if (a == 255)
opaque_pixel = target.PackRGBAFn(target, r, g, b, target.HasAlpha ? 255 : 255);
for (int y = min_y; y <= max_y; y++)
for (int x = min_x; x <= max_x; x++)
if (a == 255)
ImGui_ImplCPU_WritePixelRaw(target, x, y, opaque_pixel);
else
ImGui_ImplCPU_BlendPixelGeneric(target, x, y, r, g, b, a);
}
static void ImGui_ImplCPU_RenderTexturedRectFast(const ImGui_ImplCPU_SurfaceInfo& target, const ImGui_ImplCPU_SurfaceInfo& texture, bool texture_is_alpha, const ImGui_ImplCPU_Quad& quad, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y)
{
int min_x = std::max((int)ceilf(quad.TL->X - 0.5f), clip_min_x);
int max_x = std::min((int)floorf(quad.TR->X - 0.5f), clip_max_x - 1);
int min_y = std::max((int)ceilf(quad.TL->Y - 0.5f), clip_min_y);
int max_y = std::min((int)floorf(quad.BL->Y - 0.5f), clip_max_y - 1);
if (min_x > max_x || min_y > max_y)
return;
unsigned char color_r = (unsigned char)quad.TL->R;
unsigned char color_g = (unsigned char)quad.TL->G;
unsigned char color_b = (unsigned char)quad.TL->B;
unsigned char color_a = (unsigned char)quad.TL->A;
float du_dx = (quad.TR->U - quad.TL->U) / (quad.TR->X - quad.TL->X);
float dv_dx = (quad.TR->V - quad.TL->V) / (quad.TR->X - quad.TL->X);
float du_dy = (quad.BL->U - quad.TL->U) / (quad.BL->Y - quad.TL->Y);
float dv_dy = (quad.BL->V - quad.TL->V) / (quad.BL->Y - quad.TL->Y);
float u_const = quad.TL->U - quad.TL->X * du_dx - quad.TL->Y * du_dy;
float v_const = quad.TL->V - quad.TL->X * dv_dx - quad.TL->Y * dv_dy;
int texture_max_x = texture.Width - 1;
int texture_max_y = texture.Height - 1;
for (int y = min_y; y <= max_y; y++)
{
float py = (float)y + 0.5f;
float px = (float)min_x + 0.5f;
float u = u_const + px * du_dx + py * du_dy;
float v = v_const + px * dv_dx + py * dv_dy;
ImU32* dst = (ImU32*)(void*)(target.Pixels + (size_t)y * (size_t)target.Pitch) + min_x;
if (texture_is_alpha)
{
for (int x = min_x; x <= max_x; x++)
{
int tx = ImGui_ImplCPU_ClampInt((int)(u * (float)texture_max_x + 0.5f), 0, texture_max_x);
int ty = ImGui_ImplCPU_ClampInt((int)(v * (float)texture_max_y + 0.5f), 0, texture_max_y);
ImGui_ImplCPU_BlendPixelFast(target, dst, color_r, color_g, color_b, ImGui_ImplCPU_Mul255(color_a, ImGui_ImplCPU_ReadPixelAlpha(texture, tx, ty)));
dst++;
u += du_dx;
v += dv_dx;
}
}
else
{
for (int x = min_x; x <= max_x; x++)
{
int tx = ImGui_ImplCPU_ClampInt((int)(u * (float)texture_max_x + 0.5f), 0, texture_max_x);
int ty = ImGui_ImplCPU_ClampInt((int)(v * (float)texture_max_y + 0.5f), 0, texture_max_y);
unsigned char tr, tg, tb, ta;
ImGui_ImplCPU_ReadPixelRGBA(texture, tx, ty, &tr, &tg, &tb, &ta);
ImGui_ImplCPU_BlendPixelFast(target, dst,
ImGui_ImplCPU_Mul255(color_r, tr),
ImGui_ImplCPU_Mul255(color_g, tg),
ImGui_ImplCPU_Mul255(color_b, tb),
ImGui_ImplCPU_Mul255(color_a, ta));
dst++;
u += du_dx;
v += dv_dx;
}
}
}
}
static void ImGui_ImplCPU_RenderTexturedRectGeneric(const ImGui_ImplCPU_SurfaceInfo& target, const ImGui_ImplCPU_SurfaceInfo& texture, bool texture_is_alpha, const ImGui_ImplCPU_Quad& quad, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y)
{
int min_x = std::max((int)ceilf(quad.TL->X - 0.5f), clip_min_x);
int max_x = std::min((int)floorf(quad.TR->X - 0.5f), clip_max_x - 1);
int min_y = std::max((int)ceilf(quad.TL->Y - 0.5f), clip_min_y);
int max_y = std::min((int)floorf(quad.BL->Y - 0.5f), clip_max_y - 1);
if (min_x > max_x || min_y > max_y)
return;
unsigned char color_r = (unsigned char)quad.TL->R;
unsigned char color_g = (unsigned char)quad.TL->G;
unsigned char color_b = (unsigned char)quad.TL->B;
unsigned char color_a = (unsigned char)quad.TL->A;
float du_dx = (quad.TR->U - quad.TL->U) / (quad.TR->X - quad.TL->X);
float dv_dx = (quad.TR->V - quad.TL->V) / (quad.TR->X - quad.TL->X);
float du_dy = (quad.BL->U - quad.TL->U) / (quad.BL->Y - quad.TL->Y);
float dv_dy = (quad.BL->V - quad.TL->V) / (quad.BL->Y - quad.TL->Y);
float u_const = quad.TL->U - quad.TL->X * du_dx - quad.TL->Y * du_dy;
float v_const = quad.TL->V - quad.TL->X * dv_dx - quad.TL->Y * dv_dy;
int texture_max_x = texture.Width - 1;
int texture_max_y = texture.Height - 1;
for (int y = min_y; y <= max_y; y++)
{
float py = (float)y + 0.5f;
float px = (float)min_x + 0.5f;
float u = u_const + px * du_dx + py * du_dy;
float v = v_const + px * dv_dx + py * dv_dy;
if (texture_is_alpha)
{
for (int x = min_x; x <= max_x; x++)
{
int tx = ImGui_ImplCPU_ClampInt((int)(u * (float)texture_max_x + 0.5f), 0, texture_max_x);
int ty = ImGui_ImplCPU_ClampInt((int)(v * (float)texture_max_y + 0.5f), 0, texture_max_y);
ImGui_ImplCPU_BlendPixelGeneric(target, x, y, color_r, color_g, color_b, ImGui_ImplCPU_Mul255(color_a, ImGui_ImplCPU_ReadPixelAlpha(texture, tx, ty)));
u += du_dx;
v += dv_dx;
}
}
else
{
for (int x = min_x; x <= max_x; x++)
{
int tx = ImGui_ImplCPU_ClampInt((int)(u * (float)texture_max_x + 0.5f), 0, texture_max_x);
int ty = ImGui_ImplCPU_ClampInt((int)(v * (float)texture_max_y + 0.5f), 0, texture_max_y);
unsigned char tr, tg, tb, ta;
ImGui_ImplCPU_ReadPixelRGBA(texture, tx, ty, &tr, &tg, &tb, &ta);
ImGui_ImplCPU_BlendPixelGeneric(target, x, y,
ImGui_ImplCPU_Mul255(color_r, tr),
ImGui_ImplCPU_Mul255(color_g, tg),
ImGui_ImplCPU_Mul255(color_b, tb),
ImGui_ImplCPU_Mul255(color_a, ta));
u += du_dx;
v += dv_dx;
}
}
}
}
static bool ImGui_ImplCPU_TryRenderQuadFast(const ImGui_ImplCPU_SurfaceInfo& target, const ImGui_ImplCPU_SurfaceInfo* texture, bool texture_is_alpha, const ImGui_ImplCPU_Quad& quad, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y)
{
if (!ImGui_ImplCPU_QuadHasConstantColor(quad))
return false;
unsigned char r = (unsigned char)quad.TL->R;
unsigned char g = (unsigned char)quad.TL->G;
unsigned char b = (unsigned char)quad.TL->B;
unsigned char a = (unsigned char)quad.TL->A;
if (texture == nullptr)
{
ImGui_ImplCPU_RenderSolidRectFast(target, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y, r, g, b, a);
return true;
}
int tx = 0;
int ty = 0;
if (ImGui_ImplCPU_QuadGetConstantTexel(*texture, quad, &tx, &ty))
{
if (texture_is_alpha)
a = ImGui_ImplCPU_Mul255(a, ImGui_ImplCPU_ReadPixelAlpha(*texture, tx, ty));
else
{
unsigned char tr, tg, tb, ta;
ImGui_ImplCPU_ReadPixelRGBA(*texture, tx, ty, &tr, &tg, &tb, &ta);
r = ImGui_ImplCPU_Mul255(r, tr);
g = ImGui_ImplCPU_Mul255(g, tg);
b = ImGui_ImplCPU_Mul255(b, tb);
a = ImGui_ImplCPU_Mul255(a, ta);
}
ImGui_ImplCPU_RenderSolidRectFast(target, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y, r, g, b, a);
return true;
}
if (!ImGui_ImplCPU_QuadHasLinearUV(quad))
return false;
ImGui_ImplCPU_RenderTexturedRectFast(target, *texture, texture_is_alpha, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y);
return true;
}
static bool ImGui_ImplCPU_TryRenderQuadGeneric(const ImGui_ImplCPU_SurfaceInfo& target, const ImGui_ImplCPU_SurfaceInfo* texture, bool texture_is_alpha, const ImGui_ImplCPU_Quad& quad, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y)
{
if (!ImGui_ImplCPU_QuadHasConstantColor(quad))
return false;
unsigned char r = (unsigned char)quad.TL->R;
unsigned char g = (unsigned char)quad.TL->G;
unsigned char b = (unsigned char)quad.TL->B;
unsigned char a = (unsigned char)quad.TL->A;
if (texture == nullptr)
{
ImGui_ImplCPU_RenderSolidRectGeneric(target, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y, r, g, b, a);
return true;
}
int tx = 0;
int ty = 0;
if (ImGui_ImplCPU_QuadGetConstantTexel(*texture, quad, &tx, &ty))
{
if (texture_is_alpha)
a = ImGui_ImplCPU_Mul255(a, ImGui_ImplCPU_ReadPixelAlpha(*texture, tx, ty));
else
{
unsigned char tr, tg, tb, ta;
ImGui_ImplCPU_ReadPixelRGBA(*texture, tx, ty, &tr, &tg, &tb, &ta);
r = ImGui_ImplCPU_Mul255(r, tr);
g = ImGui_ImplCPU_Mul255(g, tg);
b = ImGui_ImplCPU_Mul255(b, tb);
a = ImGui_ImplCPU_Mul255(a, ta);
}
ImGui_ImplCPU_RenderSolidRectGeneric(target, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y, r, g, b, a);
return true;
}
if (!ImGui_ImplCPU_QuadHasLinearUV(quad))
return false;
ImGui_ImplCPU_RenderTexturedRectGeneric(target, *texture, texture_is_alpha, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y);
return true;
}
bool ImGui_ImplCPU_Init()
{
ImGuiIO& io = ImGui::GetIO();
IMGUI_CHECKVERSION();
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
ImGui_ImplCPU_Data* bd = IM_NEW(ImGui_ImplCPU_Data)();
io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_cpu";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures;
return true;
}
void ImGui_ImplCPU_UpdateTexture(ImTextureData* tex)
{
const ImGui_ImplCPU_PixelFormat* format = nullptr;
if (tex->Format == ImTextureFormat_RGBA32)
format = ImGui_ImplCPU_GetBuiltinPixelFormat(ImGui_ImplCPU_BuiltinFormat_RGBA8888);
else if (tex->Format == ImTextureFormat_Alpha8)
format = ImGui_ImplCPU_GetBuiltinPixelFormat(ImGui_ImplCPU_BuiltinFormat_Alpha8);
else
IM_ASSERT(false && "Unsupported ImTextureData format for imgui_impl_cpu");
if (tex->Status == ImTextureStatus_WantCreate)
{
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr);
ImGui_ImplCPU_Texture* cpu_texture = IM_NEW(ImGui_ImplCPU_Texture)();
cpu_texture->Pixels = (const unsigned char*)tex->GetPixels();
cpu_texture->Width = tex->Width;
cpu_texture->Height = tex->Height;
cpu_texture->Pitch = tex->GetPitch();
cpu_texture->Format = format;
tex->BackendUserData = cpu_texture;
tex->SetTexID((ImTextureID)(intptr_t)cpu_texture);
tex->SetStatus(ImTextureStatus_OK);
}
else if (tex->Status == ImTextureStatus_WantUpdates)
{
ImGui_ImplCPU_Texture* cpu_texture = (ImGui_ImplCPU_Texture*)tex->BackendUserData;
IM_ASSERT(cpu_texture != nullptr);
cpu_texture->Pixels = (const unsigned char*)tex->GetPixels();
cpu_texture->Width = tex->Width;
cpu_texture->Height = tex->Height;
cpu_texture->Pitch = tex->GetPitch();
cpu_texture->Format = format;
tex->SetStatus(ImTextureStatus_OK);
}
else if (tex->Status == ImTextureStatus_WantDestroy)
{
ImGui_ImplCPU_Texture* cpu_texture = (ImGui_ImplCPU_Texture*)tex->BackendUserData;
if (cpu_texture != nullptr)
IM_DELETE(cpu_texture);
tex->BackendUserData = nullptr;
tex->SetTexID(ImTextureID_Invalid);
tex->SetStatus(ImTextureStatus_Destroyed);
}
}
static void ImGui_ImplCPU_DestroyDeviceObjects()
{
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
if (tex->RefCount == 1)
{
tex->SetStatus(ImTextureStatus_WantDestroy);
ImGui_ImplCPU_UpdateTexture(tex);
}
}
void ImGui_ImplCPU_Shutdown()
{
ImGui_ImplCPU_Data* bd = ImGui_ImplCPU_GetBackendData();
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
ImGui_ImplCPU_DestroyDeviceObjects();
io.BackendRendererName = nullptr;
io.BackendRendererUserData = nullptr;
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures);
platform_io.ClearRendererHandlers();
IM_DELETE(bd);
}
void ImGui_ImplCPU_NewFrame()
{
ImGui_ImplCPU_Data* bd = ImGui_ImplCPU_GetBackendData();
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplCPU_Init()?");
IM_UNUSED(bd);
}
void ImGui_ImplCPU_RenderDrawData(ImDrawData* draw_data, ImGui_ImplCPU_Framebuffer* framebuffer)
{
ImGui_ImplCPU_Data* bd = ImGui_ImplCPU_GetBackendData();
if (bd == nullptr || draw_data == nullptr)
return;
if (draw_data->Textures != nullptr)
for (ImTextureData* tex : *draw_data->Textures)
if (tex->Status != ImTextureStatus_OK)
ImGui_ImplCPU_UpdateTexture(tex);
if (framebuffer == nullptr || framebuffer->Pixels == nullptr || framebuffer->Width <= 0 || framebuffer->Height <= 0 || framebuffer->Pitch <= 0)
return;
ImGui_ImplCPU_SurfaceInfo target;
ImGui_ImplCPU_InitBufferInfo(&target, framebuffer);
if (target.Format == nullptr || target.Format->BytesPerPixel <= 0)
return;
const ImVec2 clip_off = draw_data->DisplayPos;
const ImVec2 clip_scale = draw_data->FramebufferScale;
const ImGui_ImplCPU_Texture* active_texture = nullptr;
ImGui_ImplCPU_SurfaceInfo active_texture_info;
bool active_texture_valid = false;
bool active_texture_is_alpha = false;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* draw_list = draw_data->CmdLists[n];
const ImDrawIdx* idx_buffer = draw_list->IdxBuffer.Data;
ImGui_ImplCPU_BuildVertexCache(&bd->VertexCache, draw_list, clip_off, clip_scale);
const ImGui_ImplCPU_RasterVertex* vertex_cache = bd->VertexCache.Data;
for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != nullptr)
{
if (pcmd->UserCallback != ImDrawCallback_ResetRenderState)
pcmd->UserCallback(draw_list, pcmd);
continue;
}
if (pcmd->ElemCount == 0)
continue;
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
if (clip_min.x < 0.0f) clip_min.x = 0.0f;
if (clip_min.y < 0.0f) clip_min.y = 0.0f;
if (clip_max.x > (float)target.Width) clip_max.x = (float)target.Width;
if (clip_max.y > (float)target.Height) clip_max.y = (float)target.Height;
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
continue;
int clip_min_x = (int)clip_min.x;
int clip_min_y = (int)clip_min.y;
int clip_max_x = (int)clip_max.x;
int clip_max_y = (int)clip_max.y;
const ImGui_ImplCPU_Texture* texture = (const ImGui_ImplCPU_Texture*)(intptr_t)pcmd->GetTexID();
if (texture != active_texture)
{
active_texture = texture;
active_texture_valid = false;
active_texture_is_alpha = false;
if (texture != nullptr)
{
ImGui_ImplCPU_InitTextureInfo(&active_texture_info, texture);
active_texture_valid = active_texture_info.Pixels != nullptr && active_texture_info.Width > 0 && active_texture_info.Height > 0 && active_texture_info.Pitch > 0 && active_texture_info.Format != nullptr;
active_texture_is_alpha = active_texture_valid && active_texture_info.IsAlphaOnly;
}
}
const ImGui_ImplCPU_SurfaceInfo* texture_ptr = active_texture_valid ? &active_texture_info : nullptr;
for (unsigned int idx = 0; idx + 2 < (unsigned int)pcmd->ElemCount;)
{
if (idx + 5 < (unsigned int)pcmd->ElemCount)
{
ImGui_ImplCPU_Quad quad;
if (ImGui_ImplCPU_DetectQuad(vertex_cache, idx_buffer, pcmd->VtxOffset, pcmd->IdxOffset + idx, &quad))
{
bool rendered = target.IsFast
? ImGui_ImplCPU_TryRenderQuadFast(target, texture_ptr, active_texture_is_alpha, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y)
: ImGui_ImplCPU_TryRenderQuadGeneric(target, texture_ptr, active_texture_is_alpha, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y);
if (rendered)
{
idx += 6;
continue;
}
}
}
ImDrawIdx idx0 = idx_buffer[pcmd->IdxOffset + idx + 0];
ImDrawIdx idx1 = idx_buffer[pcmd->IdxOffset + idx + 1];
ImDrawIdx idx2 = idx_buffer[pcmd->IdxOffset + idx + 2];
const ImGui_ImplCPU_RasterVertex& v0 = vertex_cache[pcmd->VtxOffset + idx0];
const ImGui_ImplCPU_RasterVertex& v1 = vertex_cache[pcmd->VtxOffset + idx1];
const ImGui_ImplCPU_RasterVertex& v2 = vertex_cache[pcmd->VtxOffset + idx2];
if (target.IsFast)
ImGui_ImplCPU_RenderTriangleFast(target, texture_ptr, active_texture_is_alpha, v0, v1, v2, clip_min_x, clip_min_y, clip_max_x, clip_max_y);
else
ImGui_ImplCPU_RenderTriangleGeneric(target, texture_ptr, active_texture_is_alpha, v0, v1, v2, clip_min_x, clip_min_y, clip_max_x, clip_max_y);
idx += 3;
}
}
}
}
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif // #ifndef IMGUI_DISABLE