diff --git a/examples/VulkanUI/raygen.glsl b/examples/VulkanUI/raygen.glsl index 364122e..849b983 100644 --- a/examples/VulkanUI/raygen.glsl +++ b/examples/VulkanUI/raygen.glsl @@ -38,9 +38,9 @@ void main() for (uint16_t i = 1us; i < UITransformBuffer[bufferStart].count+1; i++) { if(pixel.x > UITransformBuffer[bufferStart].data[i].x && pixel.x < UITransformBuffer[bufferStart].data[i].x + UITransformBuffer[bufferStart].data[i].sizeX && pixel.y > UITransformBuffer[bufferStart].data[i].y && pixel.y < UITransformBuffer[bufferStart].data[i].y + UITransformBuffer[bufferStart].data[i].sizeY) { - int16_t srcX = int16_t((pixel.x - UITransformBuffer[bufferStart].data[i].x) * UITransformBuffer[bufferStart].data[i].bufferX / UITransformBuffer[bufferStart].data[i].sizeX); - int16_t srcY = int16_t((pixel.y - UITransformBuffer[bufferStart].data[i].y) * UITransformBuffer[bufferStart].data[i].bufferY / UITransformBuffer[bufferStart].data[i].sizeY); - hitValue = vec4(UIPixelBuffer[bufferStart + UITransformBuffer[bufferStart].count].pixels[srcY * UITransformBuffer[bufferStart].data[i].bufferX + srcX]); + int16_t srcX = int16_t(float(pixel.x - UITransformBuffer[bufferStart].data[i].x) * float(UITransformBuffer[bufferStart].data[i].bufferX) / float(UITransformBuffer[bufferStart].data[i].sizeX)); + int16_t srcY = int16_t(float(pixel.y - UITransformBuffer[bufferStart].data[i].y) * float(UITransformBuffer[bufferStart].data[i].bufferY) / float(UITransformBuffer[bufferStart].data[i].sizeY)); + hitValue = vec4(UIPixelBuffer[bufferStart + 1].pixels[srcY * UITransformBuffer[bufferStart].data[i].bufferX + srcX]); } } diff --git a/implementations/Crafter.Graphics-Rendertarget.cpp b/implementations/Crafter.Graphics-Rendertarget.cpp index f453ca3..c01fb3b 100644 --- a/implementations/Crafter.Graphics-Rendertarget.cpp +++ b/implementations/Crafter.Graphics-Rendertarget.cpp @@ -53,17 +53,23 @@ RendertargetVulkan::RendertargetVulkan(std::uint16_t sizeX, std::uint16_t sizeY) } -void RendertargetVulkan::UpdateBuffer(std::uint8_t frame) { +void RendertargetVulkan::UpdateElements() { elements.clear(); + std::sort(transform->children.begin(), transform->children.end(), [](Transform2D* a, Transform2D* b){ return a->anchor.z < b->anchor.z; }); for(Transform2D* child : transform.children) { SetOrderResursive(child); } + for(std::uint16_t i = 0; i < elements.size(); i++) { + elements[i]->ScaleElement(transform); + } +} + +void RendertargetVulkan::UpdateBuffer(std::uint8_t frame) { transformBuffer[frame].Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, elements.size()+1); RenderingElement2DVulkanTransformInfo* val = reinterpret_cast(reinterpret_cast(transformBuffer[frame].value) + sizeof(RenderingElement2DVulkanTransformInfo)); std::uint16_t* sizePtr = reinterpret_cast(transformBuffer[frame].value); *sizePtr = static_cast(elements.size()); for(std::uint16_t i = 0; i < elements.size(); i++) { - elements[i]->ScaleElement(transform); val[i].scaled = elements[i]->scaled; val[i].bufferX = elements[i]->bufferX; val[i].bufferY = elements[i]->bufferY; @@ -73,10 +79,6 @@ void RendertargetVulkan::UpdateBuffer(std::uint8_t frame) { void RendertargetVulkan::ReorderBuffer(std::uint8_t frame) { RenderingElement2DVulkanTransformInfo* val = reinterpret_cast(reinterpret_cast(transformBuffer[frame].value) + sizeof(RenderingElement2DVulkanTransformInfo)); - elements.clear(); - for(Transform2D* child : transform.children) { - SetOrderResursive(child); - } for(std::uint16_t i = 0; i < elements.size(); i++) { val[i].scaled = elements[i]->scaled; val[i].bufferX = elements[i]->bufferX; @@ -128,7 +130,7 @@ void RendertargetVulkan::WriteDescriptors(std::span infos[start + i] = { .sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT, .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .data = { .pAddressRange = &bufferRanges[i]} + .data = { .pAddressRange = &bufferRanges[i]} }; bufferRanges[rangeOffset + i] = { .address = elements[i]->buffers[i2]->address, @@ -147,13 +149,13 @@ void RendertargetVulkan::WriteDescriptors(std::span } void RendertargetVulkan::SetOrderResursive(Transform2D* elementTransform) { + RenderingElement2DVulkanBase* renderer = dynamic_cast(elementTransform); + if(renderer) { + renderer->index = elements.size(); + elements.push_back(renderer); + } std::sort(elementTransform->children.begin(), elementTransform->children.end(), [](Transform2D* a, Transform2D* b){ return a->anchor.z < b->anchor.z; }); for(Transform2D* childTransform : elementTransform->children) { - RenderingElement2DVulkanBase* renderer = dynamic_cast(childTransform); - if(renderer) { - renderer->index = elements.size(); - elements.push_back(renderer); - } SetOrderResursive(childTransform); } } diff --git a/interfaces/Crafter.Graphics-RenderingElement2DVulkan.cppm b/interfaces/Crafter.Graphics-RenderingElement2DVulkan.cppm index f8faf00..d177978 100644 --- a/interfaces/Crafter.Graphics-RenderingElement2DVulkan.cppm +++ b/interfaces/Crafter.Graphics-RenderingElement2DVulkan.cppm @@ -31,6 +31,7 @@ import :VulkanBuffer; import :Types; import :Window; import :DescriptorHeapVulkan; +import :Font; export namespace Crafter { struct RenderingElement2DVulkanBase : Transform2D { @@ -51,20 +52,36 @@ export namespace Crafter { template struct RenderingElement2DVulkan : RenderingElement2DVulkanBase { - RenderingElement2DVulkan(Anchor2D anchor, RendertargetBase& target, Transform2D& parent) requires(Owning) : RenderingElement2DVulkanBase(anchor) { + RenderingElement2DVulkan(Anchor2D anchor, RendertargetBase& target, Transform2D& parent, bool single = false) requires(Owning) : RenderingElement2DVulkanBase(anchor) { GetScale(target, parent); this->bufferX = this->scaled.size.x; this->bufferY = this->scaled.size.y; - for(std::uint8_t i = 0; i < Window::numFrames; i++) { - buffers[i] = new VulkanBuffer, Mapped>(); - static_cast, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY); + if(single) { + buffers[0] = new VulkanBuffer, Mapped>(); + static_cast, Mapped>*>(buffers[0])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY); + for(std::uint8_t i = 1; i < Window::numFrames; i++) { + buffers[i] = buffers[0]; + } + } else { + for(std::uint8_t i = 0; i < Window::numFrames; i++) { + buffers[i] = new VulkanBuffer, Mapped>(); + static_cast, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY); + } } } - RenderingElement2DVulkan(Anchor2D anchor, std::uint16_t bufferX, std::uint16_t bufferY) requires(Owning) : RenderingElement2DVulkanBase(anchor, bufferX, bufferY) { - for(std::uint8_t i = 0; i < Window::numFrames; i++) { - buffers[i] = new VulkanBuffer, Mapped>(); - static_cast, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY); + RenderingElement2DVulkan(Anchor2D anchor, std::uint16_t bufferX, std::uint16_t bufferY, bool single = false) requires(Owning) : RenderingElement2DVulkanBase(anchor, bufferX, bufferY) { + if(single) { + buffers[0] = new VulkanBuffer, Mapped>(); + static_cast, Mapped>*>(buffers[0])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY); + for(std::uint8_t i = 1; i < Window::numFrames; i++) { + buffers[i] = buffers[0]; + } + } else { + for(std::uint8_t i = 0; i < Window::numFrames; i++) { + buffers[i] = new VulkanBuffer, Mapped>(); + static_cast, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY); + } } } @@ -72,7 +89,7 @@ export namespace Crafter { } - RenderingElement2DVulkan(Anchor2D anchor, const std::filesystem::path& assetPath, bool single) requires(Owning && Mapped) : RenderingElement2DVulkanBase(anchor) { + RenderingElement2DVulkan(Anchor2D anchor, const std::filesystem::path& assetPath, bool single = false) requires(Owning && Mapped) : RenderingElement2DVulkanBase(anchor) { TextureAssetInfo info = TextureAsset<_Float16>::LoadInfo(assetPath); this->bufferX = info.sizeX; this->bufferY = info.sizeY; @@ -82,13 +99,13 @@ export namespace Crafter { for(std::uint8_t i = 1; i < Window::numFrames; i++) { buffers[i] = buffers[0]; } - TextureAsset<_Float16>::Load(assetPath, reinterpret_cast<_Float16*>(static_cast, Mapped>*>(buffers[0])->value), this->bufferX, this->bufferY); + TextureAsset>::Load(assetPath, static_cast, Mapped>*>(buffers[0])->value, this->bufferX, this->bufferY); } else { for(std::uint8_t i = 0; i < Window::numFrames; i++) { buffers[i] = new VulkanBuffer, Mapped>(); static_cast, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY); } - TextureAsset<_Float16>::Load(assetPath, reinterpret_cast<_Float16*>(static_cast, Mapped>*>(buffers[0])->value), this->bufferX, this->bufferY); + TextureAsset>::Load(assetPath, static_cast, Mapped>*>(buffers[0])->value, this->bufferX, this->bufferY); for(std::uint8_t i = 1; i < Window::numFrames; i++) { std::memcpy(static_cast, Mapped>*>(buffers[i])->value, static_cast, Mapped>*>(buffers[0])->value, this->bufferX * this->bufferY * sizeof(_Float16)); } @@ -122,6 +139,229 @@ export namespace Crafter { child->UpdatePosition(window, *this); } } + + int utf8_decode(const char* s, int* bytes_consumed) { + unsigned char c = s[0]; + if (c < 0x80) { + *bytes_consumed = 1; + return c; + } else if ((c & 0xE0) == 0xC0) { + *bytes_consumed = 2; + return ((c & 0x1F) << 6) | (s[1] & 0x3F); + } else if ((c & 0xF0) == 0xE0) { + *bytes_consumed = 3; + return ((c & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F); + } else if ((c & 0xF8) == 0xF0) { + *bytes_consumed = 4; + return ((c & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F); + } + *bytes_consumed = 1; + return 0xFFFD; // replacement char + } + + void RenderText(std::span lines, float size, Vector<_Float16, 4> color, Font& font, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) requires(Mapped) { + float scale = stbtt_ScaleForPixelHeight(&font.font, size); + int baseline = (int)(font.ascent * scale); + std::uint32_t lineHeight = (font.ascent - font.descent) * scale; + std::uint32_t currentY = baseline; + for(std::string_view line : lines) { + + std::uint32_t lineWidth = 0; + for (const char c : line) { + int advance, lsb; + stbtt_GetCodepointHMetrics(&font.font, c, &advance, &lsb); + lineWidth += (int)(advance * scale); + } + + std::uint32_t x = 0; + switch (alignment) { + case TextAlignment::Left: + x = 0; + break; + case TextAlignment::Center: + x = (this->scaled.size.x - lineWidth) / 2; + break; + case TextAlignment::Right: + x = this->scaled.size.x - lineWidth; + break; + } + + const char* p = line.data(); + const char* end = p + line.size(); + + while (p < end) { + int bytes; + int codepoint = utf8_decode(p, &bytes); + p += bytes; + + int ax; + int lsb; + stbtt_GetCodepointHMetrics(&font.font, codepoint, &ax, &lsb); + + int c_x1, c_y1, c_x2, c_y2; + stbtt_GetCodepointBitmapBox(&font.font, codepoint, scale, scale, &c_x1, &c_y1, &c_x2, &c_y2); + + int w = c_x2 - c_x1; + int h = c_y2 - c_y1; + + std::vector bitmap(w * h); + stbtt_MakeCodepointBitmap(&font.font, bitmap.data(), w, h, w, scale, scale, codepoint); + + // Only render characters that fit within the scaled bounds + switch(opaque) { + case OpaqueType::FullyOpaque: { + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + int bufferX = x + i + c_x1 + offsetX; + int bufferY = currentY + j + c_y1 + offsetY; + + if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) { + for(std::uint8_t frame = 0; frame < Window::numFrames; frame++) { + static_cast, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = {color.r, color.g, color.b, static_cast<_Float16>(bitmap[j * w + i])}; + } + } + } + } + break; + } + case OpaqueType::SemiOpaque: + case OpaqueType::Transparent: { + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + int bufferX = x + i + c_x1 + offsetX; + int bufferY = currentY + j + c_y1 + offsetY; + + if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) { + std::uint8_t alpha = bitmap[j * w + i]; + _Float16 srcA = (_Float16(alpha)/_Float16(255.0f))*color.a; + for(std::uint8_t frame = 0; frame < Window::numFrames; frame++) { + Vector<_Float16, 4, 4> dst = static_cast, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX]; + + _Float16 outA = srcA + dst.a * (1.0f - srcA); + static_cast, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = Vector<_Float16, 4, 4>( + (color.r * srcA + dst.r * dst.a * (1.0f - srcA)), + (color.g * srcA + dst.g * dst.a * (1.0f - srcA)), + (color.b * srcA + dst.b * dst.a * (1.0f - srcA)), + outA + ); + } + } + } + } + break; + } + } + + x += (int)(ax * scale); + + if (p + 1 < end) { + int next; + x += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, utf8_decode(p+1, &next)); + } + } + currentY += lineHeight; + } + } + + void RenderText(std::span lines, float size, Vector<_Float16, 4> color, Font& font, std::uint8_t frame, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) requires(Mapped) { + float scale = stbtt_ScaleForPixelHeight(&font.font, size); + int baseline = (int)(font.ascent * scale); + std::uint32_t lineHeight = (font.ascent - font.descent) * scale; + std::uint32_t currentY = baseline; + for(std::string_view line : lines) { + + std::uint32_t lineWidth = 0; + for (const char c : line) { + int advance, lsb; + stbtt_GetCodepointHMetrics(&font.font, c, &advance, &lsb); + lineWidth += (int)(advance * scale); + } + + std::uint32_t x = 0; + switch (alignment) { + case TextAlignment::Left: + x = 0; + break; + case TextAlignment::Center: + x = (this->scaled.size.x - lineWidth) / 2; + break; + case TextAlignment::Right: + x = this->scaled.size.x - lineWidth; + break; + } + + const char* p = line.data(); + const char* end = p + line.size(); + + while (p < end) { + int bytes; + int codepoint = utf8_decode(p, &bytes); + p += bytes; + + int ax; + int lsb; + stbtt_GetCodepointHMetrics(&font.font, codepoint, &ax, &lsb); + + int c_x1, c_y1, c_x2, c_y2; + stbtt_GetCodepointBitmapBox(&font.font, codepoint, scale, scale, &c_x1, &c_y1, &c_x2, &c_y2); + + int w = c_x2 - c_x1; + int h = c_y2 - c_y1; + + std::vector bitmap(w * h); + stbtt_MakeCodepointBitmap(&font.font, bitmap.data(), w, h, w, scale, scale, codepoint); + + // Only render characters that fit within the scaled bounds + switch(opaque) { + case OpaqueType::FullyOpaque: { + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + int bufferX = x + i + c_x1 + offsetX; + int bufferY = currentY + j + c_y1 + offsetY; + + if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) { + static_cast, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = {color.r, color.g, color.b, static_cast<_Float16>(bitmap[j * w + i])}; + } + } + } + break; + } + case OpaqueType::SemiOpaque: + case OpaqueType::Transparent: { + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + int bufferX = x + i + c_x1 + offsetX; + int bufferY = currentY + j + c_y1 + offsetY; + + if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) { + std::uint8_t alpha = bitmap[j * w + i]; + _Float16 srcA = (_Float16(alpha)/_Float16(255.0f))*color.a; + Vector<_Float16, 4, 4> dst = static_cast, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX]; + + _Float16 outA = srcA + dst.a * (1.0f - srcA); + static_cast, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = Vector<_Float16, 4, 4>( + (color.r * srcA + dst.r * dst.a * (1.0f - srcA)), + (color.g * srcA + dst.g * dst.a * (1.0f - srcA)), + (color.b * srcA + dst.b * dst.a * (1.0f - srcA)), + outA + ); + } + } + } + break; + } + } + + x += (int)(ax * scale); + + if (p + 1 < end) { + int next; + x += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, utf8_decode(p+1, &next)); + } + } + currentY += lineHeight; + } + } }; } #endif \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Rendertarget.cppm b/interfaces/Crafter.Graphics-Rendertarget.cppm index 8ca5c6f..db0b9b0 100644 --- a/interfaces/Crafter.Graphics-Rendertarget.cppm +++ b/interfaces/Crafter.Graphics-Rendertarget.cppm @@ -71,6 +71,7 @@ export namespace Crafter { RendertargetVulkan() = default; RendertargetVulkan(std::uint16_t sizeX, std::uint16_t sizeY, std::vector&& elements); RendertargetVulkan(std::uint16_t sizeX, std::uint16_t sizeY); + void UpdateElements(); void UpdateBuffer(std::uint8_t frame); void ReorderBuffer(std::uint8_t frame); void WriteDescriptors(std::span infos, std::span ranges, std::uint16_t start, std::uint32_t bufferOffset, DescriptorHeapVulkan& descriptorHeap); @@ -183,16 +184,16 @@ export namespace Crafter { void AddOldRects(Transform2D* elementTransform, std::uint8_t frame, std::vector& clipRects) { RenderingElement2DBase* element = dynamic_cast*>(elementTransform); if(element) { - // if(element->scaled.position.x != element->oldScale[frame].position.x || element->scaled.position.y != element->oldScale[frame].position.y || element->scaled.size.x != element->oldScale[frame].size.x || element->scaled.size.y != element->oldScale[frame].size.y || element->redraw[frame]) { - // clipRects.emplace_back(std::max(element->scaled.position.x, std::uint16_t(0)), std::min(std::uint16_t(element->scaled.position.x + element->scaled.size.x), this->sizeX), std::max(element->scaled.position.y, std::uint16_t(0)), std::min(std::uint16_t(element->scaled.position.y + element->scaled.size.y), this->sizeY)); - // clipRects.emplace_back(std::max(element->oldScale[frame].position.x, std::uint16_t(0)), std::min(std::uint16_t(element->oldScale[frame].position.x + element->oldScale[frame].size.x), this->sizeX), std::max(element->oldScale[frame].position.y, std::uint16_t(0)), std::min(std::uint16_t(element->oldScale[frame].position.y + element->oldScale[frame].size.y), this->sizeY)); - // element->oldScale[frame] = element->scaled; - // element->redraw[frame] = false; - // } else if(element->redraw[frame]) { - // clipRects.emplace_back(std::max(element->scaled.position.x, std::uint16_t(0)), std::min(std::uint16_t(element->scaled.position.x + element->scaled.size.x), this->sizeX), std::max(element->scaled.position.y, std::uint16_t(0)), std::min(std::uint16_t(element->scaled.position.y + element->scaled.size.y), this->sizeY)); - // element->oldScale[frame] = element->scaled; - // element->redraw[frame] = false; - // } + if(element->scaled.position.x != element->oldScale[frame].position.x || element->scaled.position.y != element->oldScale[frame].position.y || element->scaled.size.x != element->oldScale[frame].size.x || element->scaled.size.y != element->oldScale[frame].size.y || element->redraw[frame]) { + clipRects.emplace_back(std::max(element->scaled.position.x, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.x + element->scaled.size.x), std::int16_t(this->sizeX)), std::max(element->scaled.position.y, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.y + element->scaled.size.y), std::int16_t(this->sizeY))); + clipRects.emplace_back(std::max(element->oldScale[frame].position.x, std::int16_t(0)), std::min(std::int16_t(element->oldScale[frame].position.x + element->oldScale[frame].size.x), std::int16_t(this->sizeX)), std::max(element->oldScale[frame].position.y, std::int16_t(0)), std::min(std::int16_t(element->oldScale[frame].position.y + element->oldScale[frame].size.y), std::int16_t(this->sizeY))); + element->oldScale[frame] = element->scaled; + element->redraw[frame] = false; + } else if(element->redraw[frame]) { + clipRects.emplace_back(std::max(element->scaled.position.x, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.x + element->scaled.size.x), std::int16_t(this->sizeX)), std::max(element->scaled.position.y, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.y + element->scaled.size.y), std::int16_t(this->sizeY))); + element->oldScale[frame] = element->scaled; + element->redraw[frame] = false; + } } for(Transform2D* child : elementTransform->children) { AddOldRects(child, frame, clipRects);