diff --git a/.gitignore b/.gitignore index 1aedcf77..3653bea5 100644 --- a/.gitignore +++ b/.gitignore @@ -70,10 +70,12 @@ examples/example_null/example_null examples/example_sdl2_metal/example_sdl2_metal examples/example_sdl2_opengl2/example_sdl2_opengl2 examples/example_sdl2_opengl3/example_sdl2_opengl3 +examples/example_sdl2_surface/example_sdl2_surface examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2 examples/example_sdl2_vulkan/example_sdl2_vulkan examples/example_sdl3_metal/example_sdl3_metal examples/example_sdl3_opengl3/example_sdl3_opengl3 +examples/example_sdl3_surface/example_sdl3_surface examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3 examples/example_sdl3_sdlrenderer3/example_sdl3_sdlrenderer3 examples/example_sdl3_vulkan/example_sdl3_vulkan diff --git a/backends/imgui_impl_sdlsurface2.cpp b/backends/imgui_impl_sdlsurface2.cpp index e9ea0488..36672742 100644 --- a/backends/imgui_impl_sdlsurface2.cpp +++ b/backends/imgui_impl_sdlsurface2.cpp @@ -38,15 +38,6 @@ struct ImGui_ImplSDLSurface2_SurfaceInfo ImGui_ImplSDLSurface2_SurfaceInfo() { memset((void*)this, 0, sizeof(*this)); } }; -struct ImGui_ImplSDLSurface2_Data -{ - SDL_Surface* TargetSurface; - SDL_Surface* FontSurface; - ImGui_ImplSDLSurface2_SurfaceInfo TargetInfo; - - ImGui_ImplSDLSurface2_Data() { memset((void*)this, 0, sizeof(*this)); } -}; - struct ImGui_ImplSDLSurface2_RasterVertex { float X; @@ -59,6 +50,24 @@ struct ImGui_ImplSDLSurface2_RasterVertex float A; }; +struct ImGui_ImplSDLSurface2_Quad +{ + const ImGui_ImplSDLSurface2_RasterVertex* TL; + const ImGui_ImplSDLSurface2_RasterVertex* TR; + const ImGui_ImplSDLSurface2_RasterVertex* BR; + const ImGui_ImplSDLSurface2_RasterVertex* BL; +}; + +struct ImGui_ImplSDLSurface2_Data +{ + SDL_Surface* TargetSurface; + SDL_Surface* FontSurface; + ImGui_ImplSDLSurface2_SurfaceInfo TargetInfo; + ImVector VertexCache; + + ImGui_ImplSDLSurface2_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + static ImGui_ImplSDLSurface2_Data* ImGui_ImplSDLSurface2_GetBackendData() { return ImGui::GetCurrentContext() ? (ImGui_ImplSDLSurface2_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; @@ -69,6 +78,11 @@ static inline float ImGui_ImplSDLSurface2_Edge(float ax, float ay, float bx, flo return (bx - ax) * (py - ay) - (by - ay) * (px - ax); } +static inline bool ImGui_ImplSDLSurface2_NearEqual(float a, float b) +{ + return fabsf(a - b) <= 0.0001f; +} + static inline int ImGui_ImplSDLSurface2_ClampInt(int v, int lo, int hi) { return (v < lo) ? lo : (v > hi ? hi : v); @@ -227,6 +241,20 @@ static inline void ImGui_ImplSDLSurface2_ReadPixelRGBA(const ImGui_ImplSDLSurfac SDL_GetRGBA(pixel, surface.Format, r, g, b, a); } +static inline Uint8 ImGui_ImplSDLSurface2_ReadPixelAlpha(const ImGui_ImplSDLSurface2_SurfaceInfo& surface, int x, int y) +{ + if (surface.IsFast) + { + const Uint32* src = (const Uint32*)(surface.Pixels + y * surface.Pitch) + x; + return surface.FastFormat.HasAlpha ? (Uint8)((*src & surface.FastFormat.Amask) >> surface.FastFormat.Ashift) : 255; + } + + Uint8 a = 255; + Uint32 pixel = ImGui_ImplSDLSurface2_ReadPixelRaw(surface, x, y); + SDL_GetRGBA(pixel, surface.Format, nullptr, nullptr, nullptr, &a); + return a; +} + static inline void ImGui_ImplSDLSurface2_BlendPixelFast(const ImGui_ImplSDLSurface2_SurfaceInfo& target, Uint32* dst, Uint8 sr, Uint8 sg, Uint8 sb, Uint8 sa) { if (sa == 0) @@ -270,6 +298,11 @@ static inline void ImGui_ImplSDLSurface2_BlendPixelGeneric(const ImGui_ImplSDLSu ImGui_ImplSDLSurface2_WritePixelRaw(target, x, y, pixel); } +static inline bool ImGui_ImplSDLSurface2_SameColor(const ImGui_ImplSDLSurface2_RasterVertex& a, const ImGui_ImplSDLSurface2_RasterVertex& b) +{ + return a.R == b.R && a.G == b.G && a.B == b.B && a.A == b.A; +} + static inline void ImGui_ImplSDLSurface2_SetupRasterVertex(ImGui_ImplSDLSurface2_RasterVertex* out, const ImDrawVert& v, const ImVec2& display_pos, const ImVec2& framebuffer_scale) { out->X = (v.pos.x - display_pos.x) * framebuffer_scale.x; @@ -282,8 +315,16 @@ static inline void ImGui_ImplSDLSurface2_SetupRasterVertex(ImGui_ImplSDLSurface2 out->A = (float)((v.col >> IM_COL32_A_SHIFT) & 0xFF); } -static void ImGui_ImplSDLSurface2_RenderTriangleFast(const ImGui_ImplSDLSurface2_SurfaceInfo& target, const ImGui_ImplSDLSurface2_SurfaceInfo* texture, const ImGui_ImplSDLSurface2_RasterVertex& v0, const ImGui_ImplSDLSurface2_RasterVertex& v1, const ImGui_ImplSDLSurface2_RasterVertex& v2, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y) +static void ImGui_ImplSDLSurface2_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_ImplSDLSurface2_SetupRasterVertex(&out->Data[vtx_i], draw_list->VtxBuffer[vtx_i], display_pos, framebuffer_scale); +} + +static void ImGui_ImplSDLSurface2_RenderTriangleFast(const ImGui_ImplSDLSurface2_SurfaceInfo& target, const ImGui_ImplSDLSurface2_SurfaceInfo* texture, bool texture_is_font, const ImGui_ImplSDLSurface2_RasterVertex& v0, const ImGui_ImplSDLSurface2_RasterVertex& v1, const ImGui_ImplSDLSurface2_RasterVertex& v2, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y) +{ + IM_UNUSED(texture_is_font); 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); @@ -409,8 +450,9 @@ static void ImGui_ImplSDLSurface2_RenderTriangleFast(const ImGui_ImplSDLSurface2 } } -static void ImGui_ImplSDLSurface2_RenderTriangleGeneric(const ImGui_ImplSDLSurface2_SurfaceInfo& target, const ImGui_ImplSDLSurface2_SurfaceInfo* texture, const ImGui_ImplSDLSurface2_RasterVertex& v0, const ImGui_ImplSDLSurface2_RasterVertex& v1, const ImGui_ImplSDLSurface2_RasterVertex& v2, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y) +static void ImGui_ImplSDLSurface2_RenderTriangleGeneric(const ImGui_ImplSDLSurface2_SurfaceInfo& target, const ImGui_ImplSDLSurface2_SurfaceInfo* texture, bool texture_is_font, const ImGui_ImplSDLSurface2_RasterVertex& v0, const ImGui_ImplSDLSurface2_RasterVertex& v1, const ImGui_ImplSDLSurface2_RasterVertex& v2, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y) { + IM_UNUSED(texture_is_font); 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); @@ -534,6 +576,359 @@ static void ImGui_ImplSDLSurface2_RenderTriangleGeneric(const ImGui_ImplSDLSurfa } } +static bool ImGui_ImplSDLSurface2_DetectQuad(const ImGui_ImplSDLSurface2_RasterVertex* vertices, const ImDrawIdx* idx_buffer, int vtx_offset, unsigned int elem_offset, ImGui_ImplSDLSurface2_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_ImplSDLSurface2_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_ImplSDLSurface2_RasterVertex* v = candidates[i]; + if (ImGui_ImplSDLSurface2_NearEqual(v->X, min_x) && ImGui_ImplSDLSurface2_NearEqual(v->Y, min_y)) + out->TL = v; + else if (ImGui_ImplSDLSurface2_NearEqual(v->X, max_x) && ImGui_ImplSDLSurface2_NearEqual(v->Y, min_y)) + out->TR = v; + else if (ImGui_ImplSDLSurface2_NearEqual(v->X, max_x) && ImGui_ImplSDLSurface2_NearEqual(v->Y, max_y)) + out->BR = v; + else if (ImGui_ImplSDLSurface2_NearEqual(v->X, min_x) && ImGui_ImplSDLSurface2_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_ImplSDLSurface2_QuadHasConstantColor(const ImGui_ImplSDLSurface2_Quad& quad) +{ + return ImGui_ImplSDLSurface2_SameColor(*quad.TL, *quad.TR) && ImGui_ImplSDLSurface2_SameColor(*quad.TL, *quad.BR) && ImGui_ImplSDLSurface2_SameColor(*quad.TL, *quad.BL); +} + +static bool ImGui_ImplSDLSurface2_QuadHasLinearUV(const ImGui_ImplSDLSurface2_Quad& quad) +{ + if (!ImGui_ImplSDLSurface2_NearEqual(quad.TL->Y, quad.TR->Y) || !ImGui_ImplSDLSurface2_NearEqual(quad.BL->Y, quad.BR->Y) || !ImGui_ImplSDLSurface2_NearEqual(quad.TL->X, quad.BL->X) || !ImGui_ImplSDLSurface2_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_ImplSDLSurface2_NearEqual(u, quad.BR->U) && ImGui_ImplSDLSurface2_NearEqual(v, quad.BR->V); +} + +static bool ImGui_ImplSDLSurface2_QuadGetConstantTexel(const ImGui_ImplSDLSurface2_SurfaceInfo& texture, const ImGui_ImplSDLSurface2_Quad& quad, int* out_x, int* out_y) +{ + int max_x = texture.Width - 1; + int max_y = texture.Height - 1; + int tx = ImGui_ImplSDLSurface2_ClampInt((int)(quad.TL->U * (float)max_x + 0.5f), 0, max_x); + int ty = ImGui_ImplSDLSurface2_ClampInt((int)(quad.TL->V * (float)max_y + 0.5f), 0, max_y); + int corner_tx[3] = + { + ImGui_ImplSDLSurface2_ClampInt((int)(quad.TR->U * (float)max_x + 0.5f), 0, max_x), + ImGui_ImplSDLSurface2_ClampInt((int)(quad.BR->U * (float)max_x + 0.5f), 0, max_x), + ImGui_ImplSDLSurface2_ClampInt((int)(quad.BL->U * (float)max_x + 0.5f), 0, max_x) + }; + int corner_ty[3] = + { + ImGui_ImplSDLSurface2_ClampInt((int)(quad.TR->V * (float)max_y + 0.5f), 0, max_y), + ImGui_ImplSDLSurface2_ClampInt((int)(quad.BR->V * (float)max_y + 0.5f), 0, max_y), + ImGui_ImplSDLSurface2_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_ImplSDLSurface2_RenderSolidRectFast(const ImGui_ImplSDLSurface2_SurfaceInfo& target, const ImGui_ImplSDLSurface2_Quad& quad, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y, Uint8 r, Uint8 g, Uint8 b, Uint8 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) + { + Uint32 pixel = ImGui_ImplSDLSurface2_PackFastRGBA(target.FastFormat, r, g, b, 255); + for (int y = min_y; y <= max_y; y++) + { + Uint32* dst = (Uint32*)(target.Pixels + y * target.Pitch) + min_x; + std::fill_n(dst, count, pixel); + } + return; + } + + for (int y = min_y; y <= max_y; y++) + { + Uint32* dst = (Uint32*)(target.Pixels + y * target.Pitch) + min_x; + for (int x = 0; x < count; x++) + ImGui_ImplSDLSurface2_BlendPixelFast(target, dst + x, r, g, b, a); + } +} + +static void ImGui_ImplSDLSurface2_RenderSolidRectGeneric(const ImGui_ImplSDLSurface2_SurfaceInfo& target, const ImGui_ImplSDLSurface2_Quad& quad, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y, Uint8 r, Uint8 g, Uint8 b, Uint8 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; + + Uint32 opaque_pixel = 0; + if (a == 255) + opaque_pixel = SDL_MapRGBA(target.Format, r, g, b, 255); + + for (int y = min_y; y <= max_y; y++) + { + for (int x = min_x; x <= max_x; x++) + { + if (a == 255) + ImGui_ImplSDLSurface2_WritePixelRaw(target, x, y, opaque_pixel); + else + ImGui_ImplSDLSurface2_BlendPixelGeneric(target, x, y, r, g, b, a); + } + } +} + +static void ImGui_ImplSDLSurface2_RenderTexturedRectFast(const ImGui_ImplSDLSurface2_SurfaceInfo& target, const ImGui_ImplSDLSurface2_SurfaceInfo& texture, bool texture_is_font, const ImGui_ImplSDLSurface2_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; + + Uint8 color_r = (Uint8)quad.TL->R; + Uint8 color_g = (Uint8)quad.TL->G; + Uint8 color_b = (Uint8)quad.TL->B; + Uint8 color_a = (Uint8)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; + Uint32* dst = (Uint32*)(target.Pixels + y * target.Pitch) + min_x; + for (int x = min_x; x <= max_x; x++) + { + int tx = ImGui_ImplSDLSurface2_ClampInt((int)(u * (float)texture_max_x + 0.5f), 0, texture_max_x); + int ty = ImGui_ImplSDLSurface2_ClampInt((int)(v * (float)texture_max_y + 0.5f), 0, texture_max_y); + Uint8 out_r = color_r; + Uint8 out_g = color_g; + Uint8 out_b = color_b; + Uint8 out_a = color_a; + if (texture_is_font) + { + out_a = ImGui_ImplSDLSurface2_Mul255(out_a, ImGui_ImplSDLSurface2_ReadPixelAlpha(texture, tx, ty)); + ImGui_ImplSDLSurface2_BlendPixelFast(target, dst, out_r, out_g, out_b, out_a); + } + else + { + Uint8 tr, tg, tb, ta; + ImGui_ImplSDLSurface2_ReadPixelRGBA(texture, tx, ty, &tr, &tg, &tb, &ta); + out_r = ImGui_ImplSDLSurface2_Mul255(out_r, tr); + out_g = ImGui_ImplSDLSurface2_Mul255(out_g, tg); + out_b = ImGui_ImplSDLSurface2_Mul255(out_b, tb); + out_a = ImGui_ImplSDLSurface2_Mul255(out_a, ta); + ImGui_ImplSDLSurface2_BlendPixelFast(target, dst, out_r, out_g, out_b, out_a); + } + dst++; + u += du_dx; + v += dv_dx; + } + } +} + +static void ImGui_ImplSDLSurface2_RenderTexturedRectGeneric(const ImGui_ImplSDLSurface2_SurfaceInfo& target, const ImGui_ImplSDLSurface2_SurfaceInfo& texture, bool texture_is_font, const ImGui_ImplSDLSurface2_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; + + Uint8 color_r = (Uint8)quad.TL->R; + Uint8 color_g = (Uint8)quad.TL->G; + Uint8 color_b = (Uint8)quad.TL->B; + Uint8 color_a = (Uint8)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; + for (int x = min_x; x <= max_x; x++) + { + int tx = ImGui_ImplSDLSurface2_ClampInt((int)(u * (float)texture_max_x + 0.5f), 0, texture_max_x); + int ty = ImGui_ImplSDLSurface2_ClampInt((int)(v * (float)texture_max_y + 0.5f), 0, texture_max_y); + Uint8 out_r = color_r; + Uint8 out_g = color_g; + Uint8 out_b = color_b; + Uint8 out_a = color_a; + if (texture_is_font) + { + out_a = ImGui_ImplSDLSurface2_Mul255(out_a, ImGui_ImplSDLSurface2_ReadPixelAlpha(texture, tx, ty)); + } + else + { + Uint8 tr, tg, tb, ta; + ImGui_ImplSDLSurface2_ReadPixelRGBA(texture, tx, ty, &tr, &tg, &tb, &ta); + out_r = ImGui_ImplSDLSurface2_Mul255(out_r, tr); + out_g = ImGui_ImplSDLSurface2_Mul255(out_g, tg); + out_b = ImGui_ImplSDLSurface2_Mul255(out_b, tb); + out_a = ImGui_ImplSDLSurface2_Mul255(out_a, ta); + } + ImGui_ImplSDLSurface2_BlendPixelGeneric(target, x, y, out_r, out_g, out_b, out_a); + u += du_dx; + v += dv_dx; + } + } +} + +static bool ImGui_ImplSDLSurface2_TryRenderQuadFast(const ImGui_ImplSDLSurface2_SurfaceInfo& target, const ImGui_ImplSDLSurface2_SurfaceInfo* texture, bool texture_is_font, const ImGui_ImplSDLSurface2_Quad& quad, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y) +{ + if (!ImGui_ImplSDLSurface2_QuadHasConstantColor(quad)) + return false; + + Uint8 r = (Uint8)quad.TL->R; + Uint8 g = (Uint8)quad.TL->G; + Uint8 b = (Uint8)quad.TL->B; + Uint8 a = (Uint8)quad.TL->A; + if (texture == nullptr) + { + ImGui_ImplSDLSurface2_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_ImplSDLSurface2_QuadGetConstantTexel(*texture, quad, &tx, &ty)) + { + if (texture_is_font) + { + a = ImGui_ImplSDLSurface2_Mul255(a, ImGui_ImplSDLSurface2_ReadPixelAlpha(*texture, tx, ty)); + } + else + { + Uint8 tr, tg, tb, ta; + ImGui_ImplSDLSurface2_ReadPixelRGBA(*texture, tx, ty, &tr, &tg, &tb, &ta); + r = ImGui_ImplSDLSurface2_Mul255(r, tr); + g = ImGui_ImplSDLSurface2_Mul255(g, tg); + b = ImGui_ImplSDLSurface2_Mul255(b, tb); + a = ImGui_ImplSDLSurface2_Mul255(a, ta); + } + ImGui_ImplSDLSurface2_RenderSolidRectFast(target, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y, r, g, b, a); + return true; + } + + if (!ImGui_ImplSDLSurface2_QuadHasLinearUV(quad)) + return false; + ImGui_ImplSDLSurface2_RenderTexturedRectFast(target, *texture, texture_is_font, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y); + return true; +} + +static bool ImGui_ImplSDLSurface2_TryRenderQuadGeneric(const ImGui_ImplSDLSurface2_SurfaceInfo& target, const ImGui_ImplSDLSurface2_SurfaceInfo* texture, bool texture_is_font, const ImGui_ImplSDLSurface2_Quad& quad, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y) +{ + if (!ImGui_ImplSDLSurface2_QuadHasConstantColor(quad)) + return false; + + Uint8 r = (Uint8)quad.TL->R; + Uint8 g = (Uint8)quad.TL->G; + Uint8 b = (Uint8)quad.TL->B; + Uint8 a = (Uint8)quad.TL->A; + if (texture == nullptr) + { + ImGui_ImplSDLSurface2_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_ImplSDLSurface2_QuadGetConstantTexel(*texture, quad, &tx, &ty)) + { + if (texture_is_font) + { + a = ImGui_ImplSDLSurface2_Mul255(a, ImGui_ImplSDLSurface2_ReadPixelAlpha(*texture, tx, ty)); + } + else + { + Uint8 tr, tg, tb, ta; + ImGui_ImplSDLSurface2_ReadPixelRGBA(*texture, tx, ty, &tr, &tg, &tb, &ta); + r = ImGui_ImplSDLSurface2_Mul255(r, tr); + g = ImGui_ImplSDLSurface2_Mul255(g, tg); + b = ImGui_ImplSDLSurface2_Mul255(b, tb); + a = ImGui_ImplSDLSurface2_Mul255(a, ta); + } + ImGui_ImplSDLSurface2_RenderSolidRectGeneric(target, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y, r, g, b, a); + return true; + } + + if (!ImGui_ImplSDLSurface2_QuadHasLinearUV(quad)) + return false; + ImGui_ImplSDLSurface2_RenderTexturedRectGeneric(target, *texture, texture_is_font, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y); + return true; +} + SDL_Surface* ImGui_ImplSDLSurface2_CreateFontAtlasSurface() { ImGuiIO& io = ImGui::GetIO(); @@ -627,14 +1022,20 @@ void ImGui_ImplSDLSurface2_RenderDrawData(ImDrawData* draw_data) return; } + SDL_Surface* active_texture_surface = nullptr; + ImGui_ImplSDLSurface2_SurfaceInfo active_texture_info; + bool active_texture_valid = false; + bool active_texture_locked = false; + bool active_texture_is_font = false; const ImVec2 clip_off = draw_data->DisplayPos; const ImVec2 clip_scale = draw_data->FramebufferScale; for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* draw_list = draw_data->CmdLists[n]; - const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data; const ImDrawIdx* idx_buffer = draw_list->IdxBuffer.Data; + ImGui_ImplSDLSurface2_BuildVertexCache(&bd->VertexCache, draw_list, clip_off, clip_scale); + const ImGui_ImplSDLSurface2_RasterVertex* vertex_cache = bd->VertexCache.Data; for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) { @@ -663,51 +1064,68 @@ void ImGui_ImplSDLSurface2_RenderDrawData(ImDrawData* draw_data) int clip_max_y = (int)clip_max.y; SDL_Surface* texture_surface = (SDL_Surface*)pcmd->GetTexID(); - ImGui_ImplSDLSurface2_SurfaceInfo texture_info; - ImGui_ImplSDLSurface2_SurfaceInfo* texture_ptr = nullptr; - bool texture_locked = false; - - if (texture_surface != nullptr) + if (texture_surface != active_texture_surface) { - if (SDL_MUSTLOCK(texture_surface)) - { - if (SDL_LockSurface(texture_surface) != 0) - texture_surface = nullptr; - else - texture_locked = true; - } + if (active_texture_locked && active_texture_surface != nullptr) + SDL_UnlockSurface(active_texture_surface); + active_texture_surface = texture_surface; + active_texture_valid = false; + active_texture_locked = false; + active_texture_is_font = false; if (texture_surface != nullptr) { - ImGui_ImplSDLSurface2_InitSurfaceInfo(&texture_info, texture_surface); - if (texture_info.Width > 0 && texture_info.Height > 0) - texture_ptr = &texture_info; + if (SDL_MUSTLOCK(texture_surface)) + { + if (SDL_LockSurface(texture_surface) != 0) + active_texture_surface = texture_surface; + else + active_texture_locked = true; + } + if (!SDL_MUSTLOCK(texture_surface) || active_texture_locked) + { + ImGui_ImplSDLSurface2_InitSurfaceInfo(&active_texture_info, texture_surface); + active_texture_valid = active_texture_info.Width > 0 && active_texture_info.Height > 0; + active_texture_is_font = texture_surface == bd->FontSurface; + } } } - for (unsigned int idx = 0; idx + 2 < (unsigned int)pcmd->ElemCount; idx += 3) + const ImGui_ImplSDLSurface2_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_ImplSDLSurface2_Quad quad; + if (ImGui_ImplSDLSurface2_DetectQuad(vertex_cache, idx_buffer, pcmd->VtxOffset, pcmd->IdxOffset + idx, &quad)) + { + bool rendered = bd->TargetInfo.IsFast + ? ImGui_ImplSDLSurface2_TryRenderQuadFast(bd->TargetInfo, texture_ptr, active_texture_is_font, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y) + : ImGui_ImplSDLSurface2_TryRenderQuadGeneric(bd->TargetInfo, texture_ptr, active_texture_is_font, 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]; - - ImGui_ImplSDLSurface2_RasterVertex v0; - ImGui_ImplSDLSurface2_RasterVertex v1; - ImGui_ImplSDLSurface2_RasterVertex v2; - ImGui_ImplSDLSurface2_SetupRasterVertex(&v0, vtx_buffer[pcmd->VtxOffset + idx0], clip_off, clip_scale); - ImGui_ImplSDLSurface2_SetupRasterVertex(&v1, vtx_buffer[pcmd->VtxOffset + idx1], clip_off, clip_scale); - ImGui_ImplSDLSurface2_SetupRasterVertex(&v2, vtx_buffer[pcmd->VtxOffset + idx2], clip_off, clip_scale); - + const ImGui_ImplSDLSurface2_RasterVertex& v0 = vertex_cache[pcmd->VtxOffset + idx0]; + const ImGui_ImplSDLSurface2_RasterVertex& v1 = vertex_cache[pcmd->VtxOffset + idx1]; + const ImGui_ImplSDLSurface2_RasterVertex& v2 = vertex_cache[pcmd->VtxOffset + idx2]; if (bd->TargetInfo.IsFast) - ImGui_ImplSDLSurface2_RenderTriangleFast(bd->TargetInfo, texture_ptr, v0, v1, v2, clip_min_x, clip_min_y, clip_max_x, clip_max_y); + ImGui_ImplSDLSurface2_RenderTriangleFast(bd->TargetInfo, texture_ptr, active_texture_is_font, v0, v1, v2, clip_min_x, clip_min_y, clip_max_x, clip_max_y); else - ImGui_ImplSDLSurface2_RenderTriangleGeneric(bd->TargetInfo, texture_ptr, v0, v1, v2, clip_min_x, clip_min_y, clip_max_x, clip_max_y); + ImGui_ImplSDLSurface2_RenderTriangleGeneric(bd->TargetInfo, texture_ptr, active_texture_is_font, v0, v1, v2, clip_min_x, clip_min_y, clip_max_x, clip_max_y); + idx += 3; } - - if (texture_locked) - SDL_UnlockSurface(texture_surface); } } + if (active_texture_locked && active_texture_surface != nullptr) + SDL_UnlockSurface(active_texture_surface); if (SDL_MUSTLOCK(bd->TargetSurface)) SDL_UnlockSurface(bd->TargetSurface); } diff --git a/backends/imgui_impl_sdlsurface3.cpp b/backends/imgui_impl_sdlsurface3.cpp index cea8d2c6..1afaeb71 100644 --- a/backends/imgui_impl_sdlsurface3.cpp +++ b/backends/imgui_impl_sdlsurface3.cpp @@ -38,15 +38,6 @@ struct ImGui_ImplSDLSurface3_SurfaceInfo ImGui_ImplSDLSurface3_SurfaceInfo() { memset((void*)this, 0, sizeof(*this)); } }; -struct ImGui_ImplSDLSurface3_Data -{ - SDL_Surface* TargetSurface; - SDL_Surface* FontSurface; - ImGui_ImplSDLSurface3_SurfaceInfo TargetInfo; - - ImGui_ImplSDLSurface3_Data() { memset((void*)this, 0, sizeof(*this)); } -}; - struct ImGui_ImplSDLSurface3_RasterVertex { float X; @@ -59,6 +50,24 @@ struct ImGui_ImplSDLSurface3_RasterVertex float A; }; +struct ImGui_ImplSDLSurface3_Quad +{ + const ImGui_ImplSDLSurface3_RasterVertex* TL; + const ImGui_ImplSDLSurface3_RasterVertex* TR; + const ImGui_ImplSDLSurface3_RasterVertex* BR; + const ImGui_ImplSDLSurface3_RasterVertex* BL; +}; + +struct ImGui_ImplSDLSurface3_Data +{ + SDL_Surface* TargetSurface; + SDL_Surface* FontSurface; + ImGui_ImplSDLSurface3_SurfaceInfo TargetInfo; + ImVector VertexCache; + + ImGui_ImplSDLSurface3_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + static ImGui_ImplSDLSurface3_Data* ImGui_ImplSDLSurface3_GetBackendData() { return ImGui::GetCurrentContext() ? (ImGui_ImplSDLSurface3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; @@ -69,6 +78,11 @@ static inline float ImGui_ImplSDLSurface3_Edge(float ax, float ay, float bx, flo return (bx - ax) * (py - ay) - (by - ay) * (px - ax); } +static inline bool ImGui_ImplSDLSurface3_NearEqual(float a, float b) +{ + return fabsf(a - b) <= 0.0001f; +} + static inline int ImGui_ImplSDLSurface3_ClampInt(int v, int lo, int hi) { return (v < lo) ? lo : (v > hi ? hi : v); @@ -227,6 +241,20 @@ static inline void ImGui_ImplSDLSurface3_ReadPixelRGBA(const ImGui_ImplSDLSurfac SDL_GetRGBA(pixel, surface.Format, surface.Palette, r, g, b, a); } +static inline Uint8 ImGui_ImplSDLSurface3_ReadPixelAlpha(const ImGui_ImplSDLSurface3_SurfaceInfo& surface, int x, int y) +{ + if (surface.IsFast) + { + const Uint32* src = (const Uint32*)(surface.Pixels + y * surface.Pitch) + x; + return surface.FastFormat.HasAlpha ? (Uint8)((*src & surface.FastFormat.Amask) >> surface.FastFormat.Ashift) : 255; + } + + Uint8 a = 255; + Uint32 pixel = ImGui_ImplSDLSurface3_ReadPixelRaw(surface, x, y); + SDL_GetRGBA(pixel, surface.Format, surface.Palette, nullptr, nullptr, nullptr, &a); + return a; +} + static inline void ImGui_ImplSDLSurface3_BlendPixelFast(const ImGui_ImplSDLSurface3_SurfaceInfo& target, Uint32* dst, Uint8 sr, Uint8 sg, Uint8 sb, Uint8 sa) { if (sa == 0) @@ -270,6 +298,11 @@ static inline void ImGui_ImplSDLSurface3_BlendPixelGeneric(const ImGui_ImplSDLSu ImGui_ImplSDLSurface3_WritePixelRaw(target, x, y, pixel); } +static inline bool ImGui_ImplSDLSurface3_SameColor(const ImGui_ImplSDLSurface3_RasterVertex& a, const ImGui_ImplSDLSurface3_RasterVertex& b) +{ + return a.R == b.R && a.G == b.G && a.B == b.B && a.A == b.A; +} + static inline void ImGui_ImplSDLSurface3_SetupRasterVertex(ImGui_ImplSDLSurface3_RasterVertex* out, const ImDrawVert& v, const ImVec2& display_pos, const ImVec2& framebuffer_scale) { out->X = (v.pos.x - display_pos.x) * framebuffer_scale.x; @@ -282,8 +315,16 @@ static inline void ImGui_ImplSDLSurface3_SetupRasterVertex(ImGui_ImplSDLSurface3 out->A = (float)((v.col >> IM_COL32_A_SHIFT) & 0xFF); } -static void ImGui_ImplSDLSurface3_RenderTriangleFast(const ImGui_ImplSDLSurface3_SurfaceInfo& target, const ImGui_ImplSDLSurface3_SurfaceInfo* texture, const ImGui_ImplSDLSurface3_RasterVertex& v0, const ImGui_ImplSDLSurface3_RasterVertex& v1, const ImGui_ImplSDLSurface3_RasterVertex& v2, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y) +static void ImGui_ImplSDLSurface3_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_ImplSDLSurface3_SetupRasterVertex(&out->Data[vtx_i], draw_list->VtxBuffer[vtx_i], display_pos, framebuffer_scale); +} + +static void ImGui_ImplSDLSurface3_RenderTriangleFast(const ImGui_ImplSDLSurface3_SurfaceInfo& target, const ImGui_ImplSDLSurface3_SurfaceInfo* texture, bool texture_is_font, const ImGui_ImplSDLSurface3_RasterVertex& v0, const ImGui_ImplSDLSurface3_RasterVertex& v1, const ImGui_ImplSDLSurface3_RasterVertex& v2, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y) +{ + IM_UNUSED(texture_is_font); 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); @@ -409,8 +450,9 @@ static void ImGui_ImplSDLSurface3_RenderTriangleFast(const ImGui_ImplSDLSurface3 } } -static void ImGui_ImplSDLSurface3_RenderTriangleGeneric(const ImGui_ImplSDLSurface3_SurfaceInfo& target, const ImGui_ImplSDLSurface3_SurfaceInfo* texture, const ImGui_ImplSDLSurface3_RasterVertex& v0, const ImGui_ImplSDLSurface3_RasterVertex& v1, const ImGui_ImplSDLSurface3_RasterVertex& v2, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y) +static void ImGui_ImplSDLSurface3_RenderTriangleGeneric(const ImGui_ImplSDLSurface3_SurfaceInfo& target, const ImGui_ImplSDLSurface3_SurfaceInfo* texture, bool texture_is_font, const ImGui_ImplSDLSurface3_RasterVertex& v0, const ImGui_ImplSDLSurface3_RasterVertex& v1, const ImGui_ImplSDLSurface3_RasterVertex& v2, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y) { + IM_UNUSED(texture_is_font); 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); @@ -534,6 +576,359 @@ static void ImGui_ImplSDLSurface3_RenderTriangleGeneric(const ImGui_ImplSDLSurfa } } +static bool ImGui_ImplSDLSurface3_DetectQuad(const ImGui_ImplSDLSurface3_RasterVertex* vertices, const ImDrawIdx* idx_buffer, int vtx_offset, unsigned int elem_offset, ImGui_ImplSDLSurface3_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_ImplSDLSurface3_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_ImplSDLSurface3_RasterVertex* v = candidates[i]; + if (ImGui_ImplSDLSurface3_NearEqual(v->X, min_x) && ImGui_ImplSDLSurface3_NearEqual(v->Y, min_y)) + out->TL = v; + else if (ImGui_ImplSDLSurface3_NearEqual(v->X, max_x) && ImGui_ImplSDLSurface3_NearEqual(v->Y, min_y)) + out->TR = v; + else if (ImGui_ImplSDLSurface3_NearEqual(v->X, max_x) && ImGui_ImplSDLSurface3_NearEqual(v->Y, max_y)) + out->BR = v; + else if (ImGui_ImplSDLSurface3_NearEqual(v->X, min_x) && ImGui_ImplSDLSurface3_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_ImplSDLSurface3_QuadHasConstantColor(const ImGui_ImplSDLSurface3_Quad& quad) +{ + return ImGui_ImplSDLSurface3_SameColor(*quad.TL, *quad.TR) && ImGui_ImplSDLSurface3_SameColor(*quad.TL, *quad.BR) && ImGui_ImplSDLSurface3_SameColor(*quad.TL, *quad.BL); +} + +static bool ImGui_ImplSDLSurface3_QuadHasLinearUV(const ImGui_ImplSDLSurface3_Quad& quad) +{ + if (!ImGui_ImplSDLSurface3_NearEqual(quad.TL->Y, quad.TR->Y) || !ImGui_ImplSDLSurface3_NearEqual(quad.BL->Y, quad.BR->Y) || !ImGui_ImplSDLSurface3_NearEqual(quad.TL->X, quad.BL->X) || !ImGui_ImplSDLSurface3_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_ImplSDLSurface3_NearEqual(u, quad.BR->U) && ImGui_ImplSDLSurface3_NearEqual(v, quad.BR->V); +} + +static bool ImGui_ImplSDLSurface3_QuadGetConstantTexel(const ImGui_ImplSDLSurface3_SurfaceInfo& texture, const ImGui_ImplSDLSurface3_Quad& quad, int* out_x, int* out_y) +{ + int max_x = texture.Width - 1; + int max_y = texture.Height - 1; + int tx = ImGui_ImplSDLSurface3_ClampInt((int)(quad.TL->U * (float)max_x + 0.5f), 0, max_x); + int ty = ImGui_ImplSDLSurface3_ClampInt((int)(quad.TL->V * (float)max_y + 0.5f), 0, max_y); + int corner_tx[3] = + { + ImGui_ImplSDLSurface3_ClampInt((int)(quad.TR->U * (float)max_x + 0.5f), 0, max_x), + ImGui_ImplSDLSurface3_ClampInt((int)(quad.BR->U * (float)max_x + 0.5f), 0, max_x), + ImGui_ImplSDLSurface3_ClampInt((int)(quad.BL->U * (float)max_x + 0.5f), 0, max_x) + }; + int corner_ty[3] = + { + ImGui_ImplSDLSurface3_ClampInt((int)(quad.TR->V * (float)max_y + 0.5f), 0, max_y), + ImGui_ImplSDLSurface3_ClampInt((int)(quad.BR->V * (float)max_y + 0.5f), 0, max_y), + ImGui_ImplSDLSurface3_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_ImplSDLSurface3_RenderSolidRectFast(const ImGui_ImplSDLSurface3_SurfaceInfo& target, const ImGui_ImplSDLSurface3_Quad& quad, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y, Uint8 r, Uint8 g, Uint8 b, Uint8 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) + { + Uint32 pixel = ImGui_ImplSDLSurface3_PackFastRGBA(target.FastFormat, r, g, b, 255); + for (int y = min_y; y <= max_y; y++) + { + Uint32* dst = (Uint32*)(target.Pixels + y * target.Pitch) + min_x; + std::fill_n(dst, count, pixel); + } + return; + } + + for (int y = min_y; y <= max_y; y++) + { + Uint32* dst = (Uint32*)(target.Pixels + y * target.Pitch) + min_x; + for (int x = 0; x < count; x++) + ImGui_ImplSDLSurface3_BlendPixelFast(target, dst + x, r, g, b, a); + } +} + +static void ImGui_ImplSDLSurface3_RenderSolidRectGeneric(const ImGui_ImplSDLSurface3_SurfaceInfo& target, const ImGui_ImplSDLSurface3_Quad& quad, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y, Uint8 r, Uint8 g, Uint8 b, Uint8 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; + + Uint32 opaque_pixel = 0; + if (a == 255) + opaque_pixel = SDL_MapRGBA(target.Format, target.Palette, r, g, b, 255); + + for (int y = min_y; y <= max_y; y++) + { + for (int x = min_x; x <= max_x; x++) + { + if (a == 255) + ImGui_ImplSDLSurface3_WritePixelRaw(target, x, y, opaque_pixel); + else + ImGui_ImplSDLSurface3_BlendPixelGeneric(target, x, y, r, g, b, a); + } + } +} + +static void ImGui_ImplSDLSurface3_RenderTexturedRectFast(const ImGui_ImplSDLSurface3_SurfaceInfo& target, const ImGui_ImplSDLSurface3_SurfaceInfo& texture, bool texture_is_font, const ImGui_ImplSDLSurface3_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; + + Uint8 color_r = (Uint8)quad.TL->R; + Uint8 color_g = (Uint8)quad.TL->G; + Uint8 color_b = (Uint8)quad.TL->B; + Uint8 color_a = (Uint8)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; + Uint32* dst = (Uint32*)(target.Pixels + y * target.Pitch) + min_x; + for (int x = min_x; x <= max_x; x++) + { + int tx = ImGui_ImplSDLSurface3_ClampInt((int)(u * (float)texture_max_x + 0.5f), 0, texture_max_x); + int ty = ImGui_ImplSDLSurface3_ClampInt((int)(v * (float)texture_max_y + 0.5f), 0, texture_max_y); + Uint8 out_r = color_r; + Uint8 out_g = color_g; + Uint8 out_b = color_b; + Uint8 out_a = color_a; + if (texture_is_font) + { + out_a = ImGui_ImplSDLSurface3_Mul255(out_a, ImGui_ImplSDLSurface3_ReadPixelAlpha(texture, tx, ty)); + ImGui_ImplSDLSurface3_BlendPixelFast(target, dst, out_r, out_g, out_b, out_a); + } + else + { + Uint8 tr, tg, tb, ta; + ImGui_ImplSDLSurface3_ReadPixelRGBA(texture, tx, ty, &tr, &tg, &tb, &ta); + out_r = ImGui_ImplSDLSurface3_Mul255(out_r, tr); + out_g = ImGui_ImplSDLSurface3_Mul255(out_g, tg); + out_b = ImGui_ImplSDLSurface3_Mul255(out_b, tb); + out_a = ImGui_ImplSDLSurface3_Mul255(out_a, ta); + ImGui_ImplSDLSurface3_BlendPixelFast(target, dst, out_r, out_g, out_b, out_a); + } + dst++; + u += du_dx; + v += dv_dx; + } + } +} + +static void ImGui_ImplSDLSurface3_RenderTexturedRectGeneric(const ImGui_ImplSDLSurface3_SurfaceInfo& target, const ImGui_ImplSDLSurface3_SurfaceInfo& texture, bool texture_is_font, const ImGui_ImplSDLSurface3_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; + + Uint8 color_r = (Uint8)quad.TL->R; + Uint8 color_g = (Uint8)quad.TL->G; + Uint8 color_b = (Uint8)quad.TL->B; + Uint8 color_a = (Uint8)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; + for (int x = min_x; x <= max_x; x++) + { + int tx = ImGui_ImplSDLSurface3_ClampInt((int)(u * (float)texture_max_x + 0.5f), 0, texture_max_x); + int ty = ImGui_ImplSDLSurface3_ClampInt((int)(v * (float)texture_max_y + 0.5f), 0, texture_max_y); + Uint8 out_r = color_r; + Uint8 out_g = color_g; + Uint8 out_b = color_b; + Uint8 out_a = color_a; + if (texture_is_font) + { + out_a = ImGui_ImplSDLSurface3_Mul255(out_a, ImGui_ImplSDLSurface3_ReadPixelAlpha(texture, tx, ty)); + } + else + { + Uint8 tr, tg, tb, ta; + ImGui_ImplSDLSurface3_ReadPixelRGBA(texture, tx, ty, &tr, &tg, &tb, &ta); + out_r = ImGui_ImplSDLSurface3_Mul255(out_r, tr); + out_g = ImGui_ImplSDLSurface3_Mul255(out_g, tg); + out_b = ImGui_ImplSDLSurface3_Mul255(out_b, tb); + out_a = ImGui_ImplSDLSurface3_Mul255(out_a, ta); + } + ImGui_ImplSDLSurface3_BlendPixelGeneric(target, x, y, out_r, out_g, out_b, out_a); + u += du_dx; + v += dv_dx; + } + } +} + +static bool ImGui_ImplSDLSurface3_TryRenderQuadFast(const ImGui_ImplSDLSurface3_SurfaceInfo& target, const ImGui_ImplSDLSurface3_SurfaceInfo* texture, bool texture_is_font, const ImGui_ImplSDLSurface3_Quad& quad, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y) +{ + if (!ImGui_ImplSDLSurface3_QuadHasConstantColor(quad)) + return false; + + Uint8 r = (Uint8)quad.TL->R; + Uint8 g = (Uint8)quad.TL->G; + Uint8 b = (Uint8)quad.TL->B; + Uint8 a = (Uint8)quad.TL->A; + if (texture == nullptr) + { + ImGui_ImplSDLSurface3_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_ImplSDLSurface3_QuadGetConstantTexel(*texture, quad, &tx, &ty)) + { + if (texture_is_font) + { + a = ImGui_ImplSDLSurface3_Mul255(a, ImGui_ImplSDLSurface3_ReadPixelAlpha(*texture, tx, ty)); + } + else + { + Uint8 tr, tg, tb, ta; + ImGui_ImplSDLSurface3_ReadPixelRGBA(*texture, tx, ty, &tr, &tg, &tb, &ta); + r = ImGui_ImplSDLSurface3_Mul255(r, tr); + g = ImGui_ImplSDLSurface3_Mul255(g, tg); + b = ImGui_ImplSDLSurface3_Mul255(b, tb); + a = ImGui_ImplSDLSurface3_Mul255(a, ta); + } + ImGui_ImplSDLSurface3_RenderSolidRectFast(target, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y, r, g, b, a); + return true; + } + + if (!ImGui_ImplSDLSurface3_QuadHasLinearUV(quad)) + return false; + ImGui_ImplSDLSurface3_RenderTexturedRectFast(target, *texture, texture_is_font, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y); + return true; +} + +static bool ImGui_ImplSDLSurface3_TryRenderQuadGeneric(const ImGui_ImplSDLSurface3_SurfaceInfo& target, const ImGui_ImplSDLSurface3_SurfaceInfo* texture, bool texture_is_font, const ImGui_ImplSDLSurface3_Quad& quad, int clip_min_x, int clip_min_y, int clip_max_x, int clip_max_y) +{ + if (!ImGui_ImplSDLSurface3_QuadHasConstantColor(quad)) + return false; + + Uint8 r = (Uint8)quad.TL->R; + Uint8 g = (Uint8)quad.TL->G; + Uint8 b = (Uint8)quad.TL->B; + Uint8 a = (Uint8)quad.TL->A; + if (texture == nullptr) + { + ImGui_ImplSDLSurface3_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_ImplSDLSurface3_QuadGetConstantTexel(*texture, quad, &tx, &ty)) + { + if (texture_is_font) + { + a = ImGui_ImplSDLSurface3_Mul255(a, ImGui_ImplSDLSurface3_ReadPixelAlpha(*texture, tx, ty)); + } + else + { + Uint8 tr, tg, tb, ta; + ImGui_ImplSDLSurface3_ReadPixelRGBA(*texture, tx, ty, &tr, &tg, &tb, &ta); + r = ImGui_ImplSDLSurface3_Mul255(r, tr); + g = ImGui_ImplSDLSurface3_Mul255(g, tg); + b = ImGui_ImplSDLSurface3_Mul255(b, tb); + a = ImGui_ImplSDLSurface3_Mul255(a, ta); + } + ImGui_ImplSDLSurface3_RenderSolidRectGeneric(target, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y, r, g, b, a); + return true; + } + + if (!ImGui_ImplSDLSurface3_QuadHasLinearUV(quad)) + return false; + ImGui_ImplSDLSurface3_RenderTexturedRectGeneric(target, *texture, texture_is_font, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y); + return true; +} + SDL_Surface* ImGui_ImplSDLSurface3_CreateFontAtlasSurface() { ImGuiIO& io = ImGui::GetIO(); @@ -627,14 +1022,20 @@ void ImGui_ImplSDLSurface3_RenderDrawData(ImDrawData* draw_data) return; } + SDL_Surface* active_texture_surface = nullptr; + ImGui_ImplSDLSurface3_SurfaceInfo active_texture_info; + bool active_texture_valid = false; + bool active_texture_locked = false; + bool active_texture_is_font = false; const ImVec2 clip_off = draw_data->DisplayPos; const ImVec2 clip_scale = draw_data->FramebufferScale; for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* draw_list = draw_data->CmdLists[n]; - const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data; const ImDrawIdx* idx_buffer = draw_list->IdxBuffer.Data; + ImGui_ImplSDLSurface3_BuildVertexCache(&bd->VertexCache, draw_list, clip_off, clip_scale); + const ImGui_ImplSDLSurface3_RasterVertex* vertex_cache = bd->VertexCache.Data; for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) { @@ -663,51 +1064,68 @@ void ImGui_ImplSDLSurface3_RenderDrawData(ImDrawData* draw_data) int clip_max_y = (int)clip_max.y; SDL_Surface* texture_surface = (SDL_Surface*)pcmd->GetTexID(); - ImGui_ImplSDLSurface3_SurfaceInfo texture_info; - ImGui_ImplSDLSurface3_SurfaceInfo* texture_ptr = nullptr; - bool texture_locked = false; - - if (texture_surface != nullptr) + if (texture_surface != active_texture_surface) { - if (SDL_MUSTLOCK(texture_surface)) - { - if (!SDL_LockSurface(texture_surface)) - texture_surface = nullptr; - else - texture_locked = true; - } + if (active_texture_locked && active_texture_surface != nullptr) + SDL_UnlockSurface(active_texture_surface); + active_texture_surface = texture_surface; + active_texture_valid = false; + active_texture_locked = false; + active_texture_is_font = false; if (texture_surface != nullptr) { - ImGui_ImplSDLSurface3_InitSurfaceInfo(&texture_info, texture_surface); - if (texture_info.Width > 0 && texture_info.Height > 0) - texture_ptr = &texture_info; + if (SDL_MUSTLOCK(texture_surface)) + { + if (!SDL_LockSurface(texture_surface)) + active_texture_surface = texture_surface; + else + active_texture_locked = true; + } + if (!SDL_MUSTLOCK(texture_surface) || active_texture_locked) + { + ImGui_ImplSDLSurface3_InitSurfaceInfo(&active_texture_info, texture_surface); + active_texture_valid = active_texture_info.Width > 0 && active_texture_info.Height > 0; + active_texture_is_font = texture_surface == bd->FontSurface; + } } } - for (unsigned int idx = 0; idx + 2 < (unsigned int)pcmd->ElemCount; idx += 3) + const ImGui_ImplSDLSurface3_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_ImplSDLSurface3_Quad quad; + if (ImGui_ImplSDLSurface3_DetectQuad(vertex_cache, idx_buffer, pcmd->VtxOffset, pcmd->IdxOffset + idx, &quad)) + { + bool rendered = bd->TargetInfo.IsFast + ? ImGui_ImplSDLSurface3_TryRenderQuadFast(bd->TargetInfo, texture_ptr, active_texture_is_font, quad, clip_min_x, clip_min_y, clip_max_x, clip_max_y) + : ImGui_ImplSDLSurface3_TryRenderQuadGeneric(bd->TargetInfo, texture_ptr, active_texture_is_font, 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]; - - ImGui_ImplSDLSurface3_RasterVertex v0; - ImGui_ImplSDLSurface3_RasterVertex v1; - ImGui_ImplSDLSurface3_RasterVertex v2; - ImGui_ImplSDLSurface3_SetupRasterVertex(&v0, vtx_buffer[pcmd->VtxOffset + idx0], clip_off, clip_scale); - ImGui_ImplSDLSurface3_SetupRasterVertex(&v1, vtx_buffer[pcmd->VtxOffset + idx1], clip_off, clip_scale); - ImGui_ImplSDLSurface3_SetupRasterVertex(&v2, vtx_buffer[pcmd->VtxOffset + idx2], clip_off, clip_scale); - + const ImGui_ImplSDLSurface3_RasterVertex& v0 = vertex_cache[pcmd->VtxOffset + idx0]; + const ImGui_ImplSDLSurface3_RasterVertex& v1 = vertex_cache[pcmd->VtxOffset + idx1]; + const ImGui_ImplSDLSurface3_RasterVertex& v2 = vertex_cache[pcmd->VtxOffset + idx2]; if (bd->TargetInfo.IsFast) - ImGui_ImplSDLSurface3_RenderTriangleFast(bd->TargetInfo, texture_ptr, v0, v1, v2, clip_min_x, clip_min_y, clip_max_x, clip_max_y); + ImGui_ImplSDLSurface3_RenderTriangleFast(bd->TargetInfo, texture_ptr, active_texture_is_font, v0, v1, v2, clip_min_x, clip_min_y, clip_max_x, clip_max_y); else - ImGui_ImplSDLSurface3_RenderTriangleGeneric(bd->TargetInfo, texture_ptr, v0, v1, v2, clip_min_x, clip_min_y, clip_max_x, clip_max_y); + ImGui_ImplSDLSurface3_RenderTriangleGeneric(bd->TargetInfo, texture_ptr, active_texture_is_font, v0, v1, v2, clip_min_x, clip_min_y, clip_max_x, clip_max_y); + idx += 3; } - - if (texture_locked) - SDL_UnlockSurface(texture_surface); } } + if (active_texture_locked && active_texture_surface != nullptr) + SDL_UnlockSurface(active_texture_surface); if (SDL_MUSTLOCK(bd->TargetSurface)) SDL_UnlockSurface(bd->TargetSurface); } diff --git a/examples/example_sdl2_surface/Makefile b/examples/example_sdl2_surface/Makefile index 7778ae9f..99f333f0 100644 --- a/examples/example_sdl2_surface/Makefile +++ b/examples/example_sdl2_surface/Makefile @@ -17,7 +17,8 @@ OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) UNAME_S := $(shell uname -s) CXXFLAGS = -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends -CXXFLAGS += -g -Wall -Wformat +OPTFLAGS ?= -O2 +CXXFLAGS += -g $(OPTFLAGS) -Wall -Wformat LIBS = ##--------------------------------------------------------------------- diff --git a/examples/example_sdl2_surface/build_win32.bat b/examples/example_sdl2_surface/build_win32.bat index 526d7ee7..d1944735 100644 --- a/examples/example_sdl2_surface/build_win32.bat +++ b/examples/example_sdl2_surface/build_win32.bat @@ -5,4 +5,4 @@ @set SOURCES=main.cpp ..\..\backends\imgui_impl_sdl2.cpp ..\..\backends\imgui_impl_sdlsurface2.cpp ..\..\imgui*.cpp @set LIBS=/LIBPATH:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib mkdir %OUT_DIR% -cl /nologo /Zi /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console +cl /nologo /Zi /O2 /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console diff --git a/examples/example_sdl3_surface/Makefile b/examples/example_sdl3_surface/Makefile index 5aed33f0..32288ecb 100644 --- a/examples/example_sdl3_surface/Makefile +++ b/examples/example_sdl3_surface/Makefile @@ -7,7 +7,8 @@ OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) UNAME_S := $(shell uname -s) CXXFLAGS = -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends -CXXFLAGS += -g -Wall -Wformat +OPTFLAGS ?= -O2 +CXXFLAGS += -g $(OPTFLAGS) -Wall -Wformat LIBS = ifeq ($(UNAME_S), Linux) diff --git a/examples/example_sdl3_surface/build_win32.bat b/examples/example_sdl3_surface/build_win32.bat index 98c087a9..9b90d654 100644 --- a/examples/example_sdl3_surface/build_win32.bat +++ b/examples/example_sdl3_surface/build_win32.bat @@ -4,4 +4,4 @@ @set SOURCES=main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_sdlsurface3.cpp ..\..\imgui*.cpp @set LIBS=/LIBPATH:%SDL3_DIR%\lib\x86 SDL3.lib mkdir %OUT_DIR% -cl /nologo /Zi /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console +cl /nologo /Zi /O2 /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console