// 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 #include #include #include #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 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* 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