Merge pull request #20817 from hrydgard/ui-atlas-more-cleanup

UI image atlas: More cleanup
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2025-09-15 14:58:05 -06:00
commit 645653879e
12 files changed
+120 -135

No files matched your search

+35
View File
@@ -5,6 +5,7 @@
#include "Common/Data/Format/PNGLoad.h"
#include "Common/Log.h"
#include "Common/File/FileUtil.h"
// *image_data_ptr should be deleted with free()
// return value of 1 == success.
@@ -131,3 +132,37 @@ bool PNGHeaderPeek::IsValidPNGHeader() const {
}
return true;
}
bool pngSave(const Path &filename, const void *buffer, int w, int h, int bytesPerPixel) {
png_image png{};
png.version = PNG_IMAGE_VERSION;
png.format = bytesPerPixel == 3 ? PNG_FORMAT_RGB : PNG_FORMAT_RGBA;
png.width = w;
png.height = h;
const int row_stride = w * bytesPerPixel;
FILE *fp = File::OpenCFile(filename, "wb");
if (!fp) {
ERROR_LOG(Log::IO, "Unable to open png file for writing: %s", filename.c_str());
return false;
}
int result = png_image_write_to_stdio(&png, fp, 0, buffer, row_stride, nullptr);
if (png.warning_or_error >= 2) {
ERROR_LOG(Log::IO, "Saving image to PNG produced errors.");
}
png_image_free(&png);
fclose(fp);
if (!result) {
// Should we even do this?
File::Delete(filename);
ERROR_LOG(Log::IO, "PNG encode failed.");
return false;
}
return true;
}
+3
View File
@@ -3,6 +3,7 @@
#include <cstdint>
#include "Common/BitSet.h"
#include "Common/File/Path.h"
// *image_data_ptr should be deleted with free()
// return value of 1 == success.
@@ -31,3 +32,5 @@ struct PNGHeaderPeek {
int Width() const { return swap32(be_width); }
int Height() const { return swap32(be_height); }
};
bool pngSave(const Path &filename, const void *buffer, int w, int h, int bytesPerPixel);
+1 -25
View File
@@ -65,14 +65,6 @@ void Image::copyfrom(const Image &img, int ox, int oy, Effect effect) {
}
}
void Image::set(int sx, int sy, int ex, int ey, u32 fil) {
for (int y = sy; y < ey; y++) {
for (int x = sx; x < ex; x++) {
dat[y * w + x] = fil;
}
}
}
bool Image::LoadPNG(const char *png_name) {
unsigned char *img_data;
int w, h;
@@ -89,23 +81,7 @@ bool Image::LoadPNG(const char *png_name) {
}
void Image::SavePNG(const char *png_name) {
// Save PNG
FILE *fil = fopen(png_name, "wb");
png_structp png_ptr;
png_infop info_ptr;
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
assert(png_ptr);
info_ptr = png_create_info_struct(png_ptr);
assert(info_ptr);
png_init_io(png_ptr, fil);
//png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
png_set_IHDR(png_ptr, info_ptr, w, h, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(png_ptr, info_ptr);
for (int y = 0; y < height(); y++) {
png_write_row(png_ptr, (png_byte*)(dat.data() + y * w));
}
png_write_end(png_ptr, NULL);
png_destroy_write_struct(&png_ptr, &info_ptr);
pngSave(Path(png_name), dat.data(), w, h, 4);
}
void Image::SaveZIM(const char *zim_name, int zim_format) {
-1
View File
@@ -68,7 +68,6 @@ struct Image {
}
u32 get1(int x, int y) const { return dat[y * w + x]; }
void copyfrom(const Image &img, int ox, int oy, Effect effect);
void set(int sx, int sy, int ex, int ey, u32 fil);
bool LoadPNG(const char *png_name);
void SavePNG(const char *png_name);
void SaveZIM(const char *zim_name, int zim_format);
+10 -15
View File
@@ -42,22 +42,17 @@ void UIContext::Init(Draw::DrawContext *thin3d, Draw::Pipeline *uipipe, Draw::Pi
void UIContext::BeginFrame() {
frameStartTime_ = time_now_d();
if (!uitexture_) {
uitexture_ = CreateTextureFromFile(draw_, "ui_atlas.zim", ImageFileType::ZIM, false);
if (!fontTexture_) {
#if PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)
// Don't bother with loading font_atlas.zim
#else
fontTexture_ = CreateTextureFromFile(draw_, "font_atlas.zim", ImageFileType::ZIM, false);
#endif
if (!fontTexture_) {
// Load the smaller ascii font only, like on Android. For debug ui etc.
fontTexture_ = CreateTextureFromFile(draw_, "asciifont_atlas.zim", ImageFileType::ZIM, false);
if (!fontTexture_) {
WARN_LOG(Log::System, "Failed to load font_atlas.zim or asciifont_atlas.zim");
}
}
}
AtlasData data = atlasProvider_(draw_, AtlasChoice::General);
uitexture_ = data.texture;
ui_draw2d.SetAtlas(data.atlas);
}
if (!fontTexture_) {
AtlasData data = atlasProvider_(draw_, AtlasChoice::Font);
fontTexture_ = data.texture;
ui_draw2d.SetFontAtlas(data.atlas);
}
uidrawbuffer_->SetCurZ(0.0f);
ActivateTopScissor();
}
+16
View File
@@ -4,6 +4,7 @@
#include <vector>
#include <cstdint>
#include <string>
#include <functional>
#include "Common/Math/geom2d.h"
#include "Common/Math/lin/vec3.h"
@@ -43,6 +44,18 @@ struct UITransform {
float alpha;
};
enum class AtlasChoice : int {
General,
Font,
};
struct AtlasData {
Atlas *atlas;
Draw::Texture *texture;
};
typedef std::function<AtlasData(Draw::DrawContext *, AtlasChoice)> UIAtlasProviderFunc;
class UIContext {
public:
UIContext();
@@ -112,6 +125,7 @@ public:
Bounds TransformBounds(const Bounds &bounds);
void SetTheme(const UI::Theme *theme) { this->theme = theme; }
void SetAtlasProvider(UIAtlasProviderFunc func) { atlasProvider_ = func; }
private:
Draw::DrawContext *draw_ = nullptr;
@@ -136,4 +150,6 @@ private:
std::vector<Bounds> scissorStack_;
std::vector<UITransform> transformStack_;
UIAtlasProviderFunc atlasProvider_{};
};
+4 -2
View File
@@ -1833,8 +1833,10 @@ void DrawIconShine(UIContext &dc, const Bounds &bounds, float shine, bool animat
dc.Begin();
}
const AtlasImage *img = dc.Draw()->GetAtlas()->getImage(ImageID("I_DROP_SHADOW"));
float scale = bounds.w / img->w;
dc.Draw()->DrawImage(ImageID("I_DROP_SHADOW"), bounds.centerX(), bounds.centerY(), scale * 1.7f, colorAlpha(0xFF3EC5FF, 0.75f * shine), ALIGN_CENTER);
if (img) {
float scale = bounds.w / img->w;
dc.Draw()->DrawImage(ImageID("I_DROP_SHADOW"), bounds.centerX(), bounds.centerY(), scale * 1.7f, colorAlpha(0xFF3EC5FF, 0.75f * shine), ALIGN_CENTER);
}
dc.Flush();
}
+4 -48
View File
@@ -25,6 +25,7 @@
#include "Common/Data/Convert/ColorConv.h"
#include "Common/File/FileUtil.h"
#include "Common/File/Path.h"
#include "Common/Data/Format/PNGLoad.h"
#include "Common/Log.h"
#include "Common/System/System.h"
#include "Common/System/Display.h"
@@ -38,8 +39,7 @@
// This is used to make non-ASCII paths work for filename.
// Technically only needed on Windows.
class JPEGFileStream : public jpge::output_stream
{
class JPEGFileStream : public jpge::output_stream {
public:
JPEGFileStream(const Path &filename) {
fp_ = File::OpenCFile(filename, "wb");
@@ -101,25 +101,6 @@ static bool WriteScreenshotToJPEG(const Path &filename, int width, int height, i
return dst_stream.Valid();
}
static bool WriteScreenshotToPNG(png_imagep image, const Path &filename, int convert_to_8bit, const void *buffer, png_int_32 row_stride, const void *colormap) {
FILE *fp = File::OpenCFile(filename, "wb");
if (!fp) {
ERROR_LOG(Log::IO, "Unable to open screenshot file for writing.");
return false;
}
if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer, row_stride, colormap)) {
fclose(fp);
return true;
} else {
ERROR_LOG(Log::IO, "Screenshot PNG encode failed.");
fclose(fp);
// Should we even do this?
File::Delete(filename);
return false;
}
}
static bool ConvertPixelTo8888RGBA(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, u8 &a, const void *buffer, int offset, bool rev) {
const u8 *buf8 = (const u8 *)buffer;
const u16 *buf16 = (const u16 *)buffer;
@@ -383,20 +364,7 @@ ScreenshotResult TakeGameScreenshot(Draw::DrawContext *draw, const Path &filenam
bool Save888RGBScreenshot(const Path &filename, ScreenshotFormat fmt, const u8 *bufferRGB888, int w, int h) {
if (fmt == ScreenshotFormat::PNG) {
png_image png;
memset(&png, 0, sizeof(png));
png.version = PNG_IMAGE_VERSION;
png.format = PNG_FORMAT_RGB;
png.width = w;
png.height = h;
bool success = WriteScreenshotToPNG(&png, filename, 0, bufferRGB888, w * 3, nullptr);
png_image_free(&png);
if (png.warning_or_error >= 2) {
ERROR_LOG(Log::IO, "Saving screenshot to PNG produced errors.");
success = false;
}
return success;
return pngSave(filename, bufferRGB888, w, h, 3);
} else if (fmt == ScreenshotFormat::JPG) {
jpge::params params;
params.m_quality = 90;
@@ -407,19 +375,7 @@ bool Save888RGBScreenshot(const Path &filename, ScreenshotFormat fmt, const u8 *
}
bool Save8888RGBAScreenshot(const Path &filename, const u8 *buffer, int w, int h) {
png_image png{};
png.version = PNG_IMAGE_VERSION;
png.format = PNG_FORMAT_RGBA;
png.width = w;
png.height = h;
bool success = WriteScreenshotToPNG(&png, filename, 0, buffer, w * 4, nullptr);
png_image_free(&png);
if (png.warning_or_error >= 2) {
ERROR_LOG(Log::IO, "Saving screenshot to PNG produced errors.");
success = false;
}
return success;
return pngSave(filename, buffer, w, h, 4);
}
bool Save8888RGBAScreenshot(std::vector<uint8_t> &bufferPNG, const u8 *bufferRGBA8888, int w, int h) {
+4 -30
View File
@@ -25,6 +25,7 @@
#include "ext/xxhash.h"
#include "Common/Data/Format/IniFile.h"
#include "Common/Data/Format/PNGLoad.h"
#include "Common/Data/Text/I18n.h"
#include "Common/Data/Text/Parsers.h"
#include "Common/File/VFS/DirectoryReader.h"
@@ -679,25 +680,6 @@ ReplacedTexture *TextureReplacer::FindReplacement(u64 cachekey, u32 hash, int w,
return texture;
}
static bool WriteTextureToPNG(png_imagep image, const Path &filename, int convert_to_8bit, const void *buffer, png_int_32 row_stride, const void *colormap) {
FILE *fp = File::OpenCFile(filename, "wb");
if (!fp) {
ERROR_LOG(Log::TexReplacement, "Save texture: Unable to open texture file '%s' for writing.", filename.c_str());
return false;
}
if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer, row_stride, colormap)) {
fclose(fp);
return true;
} else {
// This shouldn't really happen.
ERROR_LOG(Log::TexReplacement, "Texture PNG encode failed.");
fclose(fp);
remove(filename.c_str());
return false;
}
}
// We save textures on threadpool tasks since it's a fire-and-forget task, and both I/O and png compression
// can be pretty slow.
class SaveTextureTask : public Task {
@@ -747,19 +729,11 @@ public:
// going to write to to .png.
saveFilename = saveFilename.WithReplacedExtension(".png");
png_image png{};
png.version = PNG_IMAGE_VERSION;
png.format = PNG_FORMAT_RGBA;
png.width = w;
png.height = h;
bool success = WriteTextureToPNG(&png, saveFilename, 0, rgbaData, w * 4, nullptr);
png_image_free(&png);
if (png.warning_or_error >= 2) {
bool success = pngSave(saveFilename, rgbaData, w, h, 4);
if (!success) {
ERROR_LOG(Log::TexReplacement, "Saving texture to PNG produced errors.");
} else if (success) {
NOTICE_LOG(Log::TexReplacement, "Saving texture for replacement: %08x / %dx%d in '%s'", replacedInfoHash, w, h, saveFilename.ToVisualString().c_str());
} else {
ERROR_LOG(Log::TexReplacement, "Failed to write '%s'", saveFilename.c_str());
NOTICE_LOG(Log::TexReplacement, "Saving texture for replacement: %08x / %dx%d in '%s'", replacedInfoHash, w, h, saveFilename.ToVisualString().c_str());
}
}
};
+1 -1
View File
@@ -87,7 +87,6 @@
#include "Common/VR/PPSSPPVR.h"
#include "Common/Thread/ThreadManager.h"
#include "Common/Audio/AudioBackend.h"
#include "Core/ControlMapper.h"
#include "Core/Config.h"
#include "Core/ConfigValues.h"
@@ -843,6 +842,7 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext) {
uiContext = new UIContext();
uiContext->SetTheme(GetTheme());
uiContext->SetAtlasProvider(&AtlasProvider);
UpdateTheme();
ui_draw2d.Init(g_draw, texColorPipeline);
+40 -13
View File
@@ -26,6 +26,7 @@
#include "Common/Data/Format/IniFile.h"
#include "Common/File/DirListing.h"
#include "Common/Log/LogManager.h"
#include "Common/Render/ManagedTexture.h"
#include "Core/Config.h"
@@ -171,15 +172,12 @@ static UI::Style MakeStyle(uint32_t fg, uint32_t bg) {
return s;
}
static void LoadAtlasMetadata(Atlas &metadata, const char *filename, bool required) {
static void LoadAtlasMetadata(Atlas &metadata, const char *filename) {
size_t atlas_data_size = 0;
const uint8_t *atlas_data = g_VFS.ReadFile(filename, &atlas_data_size);
bool load_success = atlas_data != nullptr && metadata.Load(atlas_data, atlas_data_size);
if (!load_success) {
if (required)
ERROR_LOG(Log::G3D, "Failed to load %s - graphics will be broken", filename);
else
WARN_LOG(Log::G3D, "Failed to load %s", filename);
ERROR_LOG(Log::G3D, "Failed to load %s - graphics may be broken", filename);
// Stumble along with broken visuals instead of dying...
}
delete[] atlas_data;
@@ -251,14 +249,6 @@ void UpdateTheme() {
ui_theme.popupSliderColor = themeInfo.uPopupSliderColor;
ui_theme.popupSliderFocusedColor = themeInfo.uPopupSliderFocusedColor;
// Load any missing atlas metadata (the images are loaded from UIContext).
LoadAtlasMetadata(ui_atlas, "ui_atlas.meta", true);
#if !(PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(ANDROID))
LoadAtlasMetadata(font_atlas, "font_atlas.meta", ui_atlas.num_fonts == 0);
#else
LoadAtlasMetadata(font_atlas, "asciifont_atlas.meta", ui_atlas.num_fonts == 0);
#endif
}
UI::Theme *GetTheme() {
@@ -287,3 +277,40 @@ std::vector<std::string> GetThemeInfoNames() {
return names;
}
AtlasData AtlasProvider(Draw::DrawContext *draw, AtlasChoice atlas) {
switch (atlas) {
case AtlasChoice::General:
{
// Load any missing atlas metadata (the images are loaded from UIContext).
LoadAtlasMetadata(ui_atlas, "ui_atlas.meta");
return {
&ui_atlas,
CreateTextureFromFile(draw, "ui_atlas.zim", ImageFileType::ZIM, false)
};
}
case AtlasChoice::Font:
{
Draw::Texture *fontTexture = nullptr;
#if PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)
// Load the smaller ascii font only, like on Android. For debug ui etc.
// NOTE: We better be sure here that the correct metadata is loaded..
LoadAtlasMetadata(font_atlas, "asciifont_atlas.meta");
fontTexture = CreateTextureFromFile(draw, "asciifont_atlas.zim", ImageFileType::ZIM, false);
if (!fontTexture) {
WARN_LOG(Log::System, "Failed to load font_atlas.zim or asciifont_atlas.zim");
}
#else
// Load the full font texture.
LoadAtlasMetadata(font_atlas, "font_atlas.meta");
fontTexture = CreateTextureFromFile(draw, "font_atlas.zim", ImageFileType::ZIM, false);
#endif
return {
&font_atlas,
fontTexture,
};
}
default:
return {};
};
}
+2
View File
@@ -30,3 +30,5 @@ void UpdateTheme();
Atlas *GetFontAtlas();
Atlas *GetUIAtlas();
UI::Theme *GetTheme();
AtlasData AtlasProvider(Draw::DrawContext *draw, AtlasChoice atlas);