-
Notifications
You must be signed in to change notification settings - Fork 2
fix: match Scratch SVG MIP power-of-two scaling rule #321
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: spx4.4.1
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| #include "svg_mgr.h" | ||
|
|
||
| #include "core/io/image_loader.h" | ||
| #include "core/math/math_funcs.h" | ||
|
|
||
| #include "spx.h" | ||
| #include "spx_engine.h" | ||
|
|
@@ -201,22 +202,21 @@ void SvgManager::update_caches(const Vector<String> &files) { | |
| } | ||
|
|
||
| int SvgManager::calculate_svg_scale(Vector2 required_scale) { | ||
| float scale = MAX(required_scale.x, required_scale.y); | ||
| float scale = MAX(Math::abs(required_scale.x), Math::abs(required_scale.y)); | ||
| return calculate_svg_scale(scale); | ||
| } | ||
|
|
||
| int SvgManager::calculate_svg_scale(float required_scale) { | ||
| // Use powers of 2: 1, 2, 4, 8, 16... | ||
| if (required_scale <= 1.5f) { | ||
| float scale = Math::abs(required_scale); | ||
| if (scale <= 1.0f) { | ||
| return 1; | ||
| } | ||
|
|
||
| if (required_scale <= 3.0f) { | ||
| return 2; | ||
| // Match Scratch's SVG MIP rule: choose the smallest power-of-two scale | ||
| // whose rasterized texture is not smaller than the requested render scale. | ||
| int target_scale = 1; | ||
| while ((float)target_scale < scale && target_scale < 1024) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Scale cap raised 8 → 1024 (high-impact enabler). The old code hard-capped the rasterization multiplier at 8; this loop caps at 1024. Because the loader allocates Consider capping the final pixel dimensions (width×height after scaling) rather than only the multiplier, and confirm 1024 is intentional. Also note the comment above ("smallest power-of-two … not smaller than the requested render scale") no longer holds once |
||
| target_scale <<= 1; | ||
| } | ||
|
|
||
| if (required_scale <= 6.0f) { | ||
| return 4; | ||
| } | ||
| return 8; | ||
| return target_scale; | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -36,6 +36,7 @@ | |||||||||||||||
|
|
||||||||||||||||
| #include <lunasvg.h> | ||||||||||||||||
|
|
||||||||||||||||
| #include <cstring> | ||||||||||||||||
| #include <iostream> | ||||||||||||||||
|
|
||||||||||||||||
| HashMap<Color, Color> ImageLoaderSVG::forced_color_map = HashMap<Color, Color>(); | ||||||||||||||||
|
|
@@ -98,21 +99,16 @@ Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const ui | |||||||||||||||
| height *= p_scale; | ||||||||||||||||
|
|
||||||||||||||||
| auto bitmap = document->renderToBitmap(width, height, 0x00000000); | ||||||||||||||||
| bitmap.convertToRGBA(); | ||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing null/empty-bitmap check; copy bounded by requested rather than actual dimensions (high). Recommend: after
Comment on lines
101
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If
Suggested change
|
||||||||||||||||
|
|
||||||||||||||||
| Vector<uint8_t> result; | ||||||||||||||||
| result.resize(width * height * 4); | ||||||||||||||||
|
|
||||||||||||||||
| uint32_t *buffer = (uint32_t *)bitmap.data(); | ||||||||||||||||
|
|
||||||||||||||||
| const uint8_t *buffer = bitmap.data(); | ||||||||||||||||
| const int stride = bitmap.stride(); | ||||||||||||||||
| uint8_t *dst = result.ptrw(); | ||||||||||||||||
| for (uint32_t y = 0; y < height; y++) { | ||||||||||||||||
| for (uint32_t x = 0; x < width; x++) { | ||||||||||||||||
| uint32_t n = buffer[y * width + x]; | ||||||||||||||||
| const size_t offset = sizeof(uint32_t) * width * y + sizeof(uint32_t) * x; | ||||||||||||||||
| result.write[offset + 0] = (n >> 16) & 0xff; | ||||||||||||||||
| result.write[offset + 1] = (n >> 8) & 0xff; | ||||||||||||||||
| result.write[offset + 2] = n & 0xff; | ||||||||||||||||
| result.write[offset + 3] = (n >> 24) & 0xff; | ||||||||||||||||
| } | ||||||||||||||||
| memcpy(dst + (width * 4 * y), buffer + (stride * y), width * 4); | ||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Integer overflow in the allocation size (high). Compute the size in 64-bit and reject/clamp before allocating, e.g. |
||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| p_image->set_data(width, height, false, Image::FORMAT_RGBA8, result); | ||||||||||||||||
|
|
||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Behavior change: threshold shifted 1.5 → 1.0. The old code returned scale
1for anyrequired_scale <= 1.5; this returns1only forscale <= 1.0and2for anything in(1.0, 2.0]. So content rendered at 1.1x–1.5x (a common near-native range) now rasterizes at 2x — roughly 4x the texture memory and raster cost. If matching Scratch's "not smaller than requested" rule is the explicit intent, this is correct by design; flagging so reviewers confirm the trade-off is intended.