mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-01-09 06:23:21 +08:00
Test a multi-shape image
This commit is contained in:
parent
71c8f0a9bb
commit
74874bb81e
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -64,4 +64,4 @@
|
||||
url = https://github.com/hrydgard/freetype.git
|
||||
[submodule "ext/nanosvg"]
|
||||
path = ext/nanosvg
|
||||
url = git@github.com:memononen/nanosvg.git
|
||||
url = https://github.com/hrydgard/nanosvg.git
|
||||
|
||||
@ -80,6 +80,7 @@ void Bucket::AddImage(Image &&img, int id) {
|
||||
dat.ey = (int)img.height();
|
||||
dat.w = dat.ex;
|
||||
dat.h = dat.ey;
|
||||
dat.scale = img.scale;
|
||||
dat.redToWhiteAlpha = false;
|
||||
images.emplace_back(std::move(img));
|
||||
data.push_back(dat);
|
||||
@ -173,8 +174,9 @@ AtlasImage ToAtlasImage(int id, std::string_view name, float tw, float th, const
|
||||
img.v1 = results[i].sy / th + toffy;
|
||||
img.u2 = results[i].ex / tw - toffx;
|
||||
img.v2 = results[i].ey / th - toffy;
|
||||
img.w = results[i].ex - results[i].sx;
|
||||
img.h = results[i].ey - results[i].sy;
|
||||
// The w and h here is the UI-pixels width/height. So if we rasterized at another DPI than 1.0f, we need to scale here.
|
||||
img.w = results[i].w / results[i].scale;
|
||||
img.h = results[i].h / results[i].scale;
|
||||
truncate_cpy(img.name, name);
|
||||
return img;
|
||||
}
|
||||
|
||||
@ -33,6 +33,8 @@ struct Image {
|
||||
Image(Image &&) = default;
|
||||
Image &operator=(Image &&) = default;
|
||||
|
||||
float scale = 1.0f;
|
||||
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
|
||||
@ -82,6 +84,8 @@ struct Data {
|
||||
// distance to move the origin forward
|
||||
float wx;
|
||||
|
||||
float scale;
|
||||
|
||||
bool redToWhiteAlpha;
|
||||
int charNum;
|
||||
};
|
||||
|
||||
@ -159,7 +159,21 @@ Draw::Texture *GenerateUIAtlas(Draw::DrawContext *draw, Atlas *atlas) {
|
||||
// There's a couple of approaches here, either we can pick apart the SVG and render each piece separately,
|
||||
// or we just rasterize the whole thing in one go and use the bounding boxes to pick out the sub-images.
|
||||
// We'll start with the latter, although the momentary memory requirements are higher.
|
||||
std::vector<NSVGshape *> usedShapes;
|
||||
struct UsedShape {
|
||||
float minX = 1000000.0f;
|
||||
float maxX = -1000000.0f;
|
||||
float minY = 1000000.0f;
|
||||
float maxY = -1000000.0f;
|
||||
|
||||
void Merge(NSVGshape *shape) {
|
||||
if (shape->bounds[0] < minX) minX = shape->bounds[0];
|
||||
if (shape->bounds[1] < minY) minY = shape->bounds[1];
|
||||
if (shape->bounds[2] > maxX) maxX = shape->bounds[2];
|
||||
if (shape->bounds[3] > maxY) maxY = shape->bounds[3];
|
||||
}
|
||||
};
|
||||
|
||||
std::map<std::string, UsedShape> usedShapes;
|
||||
if (image) {
|
||||
// Loop through the shapes to list them, and to hide them if irrelevant.
|
||||
NSVGshape *shape = image->shapes;
|
||||
@ -169,8 +183,12 @@ Draw::Texture *GenerateUIAtlas(Draw::DrawContext *draw, Atlas *atlas) {
|
||||
INFO_LOG(Log::G3D, "Ignoring shape %s", shape->id);
|
||||
shape->flags &= ~NSVG_FLAGS_VISIBLE;
|
||||
} else {
|
||||
INFO_LOG(Log::G3D, "Found shape: %s (%0.2f %0.2f %0.2f %0.2f)", shape->id, shape->bounds[0], shape->bounds[1], shape->bounds[2], shape->bounds[3]);
|
||||
usedShapes.push_back(shape);
|
||||
if (usedShapes.find(shape->id) != usedShapes.end()) {
|
||||
INFO_LOG(Log::G3D, "Duplicate shape ID in SVG, merging bboxes: %s", shape->id);
|
||||
} else {
|
||||
INFO_LOG(Log::G3D, "Found shape: %s (%0.2f %0.2f %0.2f %0.2f)", shape->id, shape->bounds[0], shape->bounds[1], shape->bounds[2], shape->bounds[3]);
|
||||
}
|
||||
usedShapes[shape->id].Merge(shape);
|
||||
}
|
||||
shape = shape->next;
|
||||
}
|
||||
@ -180,46 +198,53 @@ Draw::Texture *GenerateUIAtlas(Draw::DrawContext *draw, Atlas *atlas) {
|
||||
// Rasterize here, and add into image list.
|
||||
rast = nsvgCreateRasterizer();
|
||||
|
||||
INFO_LOG(Log::G3D, "Rasterizing SVG: %d x %d", (int)image->width, (int)image->height);
|
||||
float scale = 2.0f;
|
||||
int svgWidth = image->width * scale;
|
||||
int svgHeight = image->height * scale;
|
||||
|
||||
char *svgImg = new char[(int)image->width * (int)image->height * 4];
|
||||
memset(svgImg, 0, (int)image->width * (int)image->height * 4);
|
||||
nsvgRasterize(rast, image, 0, 0, 1.0f, (unsigned char *)svgImg, (int)image->width, (int)image->height, (int)image->width * 4);
|
||||
INFO_LOG(Log::G3D, "Rasterizing SVG: %d x %d at scale %0.2f", svgWidth, svgHeight, scale);
|
||||
|
||||
char *svgImg = new char[svgWidth * svgHeight * 4];
|
||||
memset(svgImg, 0, svgWidth * svgHeight * 4);
|
||||
nsvgRasterize(rast, image, 0, 0, scale, (unsigned char *)svgImg, svgWidth, svgHeight, svgWidth * 4);
|
||||
|
||||
// Now, loop through the shapes again and copy out the ones we care about.
|
||||
for (auto shape : usedShapes) {
|
||||
int index = GetImageIndex(shape->id);
|
||||
for (auto &[shapeId, bounds] : usedShapes) {
|
||||
int index = GetImageIndex(shapeId);
|
||||
_dbg_assert_(index != -1);
|
||||
if (index == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Image &img = images[index];
|
||||
int minX = std::max(0, (int)floorf(shape->bounds[0]));
|
||||
int minY = std::max(0, (int)floorf(shape->bounds[1]));
|
||||
int maxX = std::min((int)image->width, (int)ceilf(shape->bounds[2]));
|
||||
int maxY = std::min((int)image->height, (int)ceilf(shape->bounds[3]));
|
||||
int minX = std::max(0, (int)floorf(bounds.minX * scale));
|
||||
int minY = std::max(0, (int)floorf(bounds.minY * scale));
|
||||
int maxX = std::min(svgWidth, (int)ceilf(bounds.maxX * scale));
|
||||
int maxY = std::min(svgHeight, (int)ceilf(bounds.maxY * scale));
|
||||
int w = maxX - minX;
|
||||
int h = maxY - minY;
|
||||
if (w <= 0 || h <= 0) {
|
||||
ERROR_LOG(Log::G3D, "Invalid size for %s: %dx%d", shape->id, w, h);
|
||||
ERROR_LOG(Log::G3D, "Invalid size for %s: %dx%d", shapeId.c_str(), w, h);
|
||||
continue;
|
||||
}
|
||||
img.resize(w, h);
|
||||
for (int y = 0; y < h; y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
int sx = (int)(shape->bounds[0] + x);
|
||||
int sy = (int)(shape->bounds[1] + y);
|
||||
const u32 *src = (u32 *)svgImg + (sy * (int)image->width + sx);
|
||||
int sx = minX + x;
|
||||
int sy = minY + y;
|
||||
const u32 *src = (u32 *)svgImg + (sy * svgWidth + sx);
|
||||
u32 col = *src;
|
||||
img.set1(x, y, col);
|
||||
}
|
||||
}
|
||||
|
||||
img.scale = scale;
|
||||
|
||||
// pngSave(Path(std::string("../buttons_") + PNGNameFromID(shape->id)), img.data(), img.width(), img.height(), 4);
|
||||
}
|
||||
|
||||
// pngSave(Path("../buttons_rasterized.png"), svgImg, (int)image->width, (int)image->height, 4);
|
||||
|
||||
// pngSave(Path("../buttons_rasterized.png"), svgImg, svgWidth, svgHeight, 4);
|
||||
delete[] svgImg;
|
||||
|
||||
nsvgDeleteRasterizer(rast);
|
||||
@ -227,7 +252,7 @@ Draw::Texture *GenerateUIAtlas(Draw::DrawContext *draw, Atlas *atlas) {
|
||||
}
|
||||
}
|
||||
|
||||
INFO_LOG(Log::G3D, " - Rasterized svg image in %.2f ms\n", images.size(), svgStart.ElapsedMs());
|
||||
INFO_LOG(Log::G3D, " - Rasterized svg image in %0.2f ms\n", svgStart.ElapsedMs());
|
||||
|
||||
Instant pngStart = Instant::Now();
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||
sodipodi:docname="buttons.svg"
|
||||
sodipodi:docname="images.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@ -178,6 +178,76 @@
|
||||
operator="over"
|
||||
result="composite2" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter5132-3"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
width="1.5621654"
|
||||
height="1.1492101"
|
||||
x="-0.2810827"
|
||||
y="-0.074605025">
|
||||
<feFlood
|
||||
id="feFlood5134-8"
|
||||
flood-opacity="0.20000000000000001"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood" />
|
||||
<feComposite
|
||||
id="feComposite5136-8"
|
||||
in2="SourceGraphic"
|
||||
in="flood"
|
||||
operator="in"
|
||||
result="composite1" />
|
||||
<feGaussianBlur
|
||||
id="feGaussianBlur5138-1"
|
||||
stdDeviation="0.5"
|
||||
result="blur" />
|
||||
<feOffset
|
||||
id="feOffset5140-6"
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset" />
|
||||
<feComposite
|
||||
id="feComposite5142-3"
|
||||
in2="offset"
|
||||
in="SourceGraphic"
|
||||
operator="over"
|
||||
result="composite2" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter2"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
width="3.3160448"
|
||||
height="2.4104021"
|
||||
x="-1.1216338"
|
||||
y="-0.70520079">
|
||||
<feFlood
|
||||
id="feFlood1"
|
||||
flood-opacity="0.20000000000000001"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood" />
|
||||
<feComposite
|
||||
id="feComposite1"
|
||||
in2="SourceGraphic"
|
||||
in="flood"
|
||||
operator="in"
|
||||
result="composite1" />
|
||||
<feGaussianBlur
|
||||
id="feGaussianBlur1"
|
||||
stdDeviation="0.5"
|
||||
result="blur" />
|
||||
<feOffset
|
||||
id="feOffset1"
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset" />
|
||||
<feComposite
|
||||
id="feComposite2"
|
||||
in2="offset"
|
||||
in="SourceGraphic"
|
||||
operator="over"
|
||||
result="composite2" />
|
||||
</filter>
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
@ -220,5 +290,27 @@
|
||||
inkscape:export-xdpi="135"
|
||||
inkscape:export-ydpi="135"
|
||||
transform="matrix(0.39648704,0,0,0.39648704,-24.975676,6.2728297)" />
|
||||
<g
|
||||
transform="matrix(0.38834816,0,0,0.38834816,-66.861088,-102.74791)"
|
||||
id="I_PAUSE"
|
||||
inkscape:export-xdpi="133.41901"
|
||||
inkscape:export-ydpi="133.41901">
|
||||
<rect
|
||||
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;filter:url(#filter5132-3)"
|
||||
id="rect1484"
|
||||
width="6.9374599"
|
||||
height="26.13765"
|
||||
x="263.30356"
|
||||
y="245.75505"
|
||||
transform="matrix(0.9517119,0,0,0.85404454,13.366799,63.169462)" />
|
||||
<rect
|
||||
transform="matrix(0.9517119,0,0,0.85404454,27.341328,63.169462)"
|
||||
y="245.75505"
|
||||
x="263.30356"
|
||||
height="26.13765"
|
||||
width="6.9374599"
|
||||
id="rect1486"
|
||||
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;filter:url(#filter5132-3)" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 10 KiB |
@ -1 +1 @@
|
||||
Subproject commit ea6a6aca009422bba0dbad4c80df6e6ba0c82183
|
||||
Subproject commit 478dbb8f7ed11c3d9b20b3986a8ee2283f483ef7
|
||||
Loading…
x
Reference in New Issue
Block a user