Texture replacer: Faster way to get width/height from a png

This commit is contained in:
Henrik Rydgård 2023-03-07 22:20:56 +01:00
parent 81817faf1d
commit 76c6058abe
3 changed files with 40 additions and 21 deletions

View File

@ -59,3 +59,14 @@ int pngLoadPtr(const unsigned char *input_ptr, size_t input_len, int *pwidth, in
png_image_finish_read(&png, NULL, *image_data_ptr, stride, NULL);
return 1;
}
bool PNGHeaderPeek::IsValidPNGHeader() const {
if (magic != 0x474e5089 || ihdrTag != 0x52444849) {
return false;
}
// Reject crazy sized images, too.
if (Width() > 32768 && Height() > 32768) {
return false;
}
return true;
}

View File

@ -1,5 +1,8 @@
#ifndef _PNG_LOAD_H
#define _PNG_LOAD_H
#pragma once
#include <cstdint>
#include "Common/BitSet.h"
// *image_data_ptr should be deleted with free()
// return value of 1 == success.
@ -9,4 +12,20 @@ int pngLoad(const char *file, int *pwidth,
int pngLoadPtr(const unsigned char *input_ptr, size_t input_len, int *pwidth,
int *pheight, unsigned char **image_data_ptr);
#endif // _PNG_LOAD_H
// PNG peeker - just read the start of a PNG straight into this struct, in order to
// look at basic parameters like width and height. Note that while PNG is a chunk-based
// format, the IHDR chunk is REQUIRED to be the first one, so this will work.
struct PNGHeaderPeek {
uint32_t magic;
uint32_t ignore0;
uint32_t ignore1;
uint32_t ihdrTag;
uint32_t be_width; // big endian
uint32_t be_height;
uint8_t bitDepth; // bits per channel, can be 1, 2, 4, 8, 16
uint8_t colorType; // really, pixel format. 0 = grayscale, 2 = rgb, 3 = palette index, 4 = gray+alpha, 6 = rgba
bool IsValidPNGHeader() const;
int Width() const { return swap32(be_width); }
int Height() const { return swap32(be_height); }
};

View File

@ -32,6 +32,7 @@
#include "Common/Data/Convert/ColorConv.h"
#include "Common/Data/Format/IniFile.h"
#include "Common/Data/Format/ZIMLoad.h"
#include "Common/Data/Format/PNGLoad.h"
#include "Common/Data/Text/I18n.h"
#include "Common/Data/Text/Parsers.h"
#include "Common/File/FileUtil.h"
@ -645,27 +646,15 @@ bool TextureReplacer::PopulateLevelFromZip(ReplacedTextureLevel &level, bool ign
good = (flags & ZIM_FORMAT_MASK) == ZIM_RGBA8888;
}
} else if (imageType == ReplacedImageType::PNG) {
png_image png = {};
png.version = PNG_IMAGE_VERSION;
// TODO: Use some way to stream data into libpng. Better than the IO lookups on Android...
zip_uint64_t zsize = ZipFileSize(level.zinfo->z, level.zi);
std::string pngdata;
if (zsize != INVALID_ZIP_SIZE)
pngdata.resize(zsize);
if (!pngdata.empty()) {
pngdata.resize(zip_fread(zf, &pngdata[0], pngdata.size()));
}
if (png_image_begin_read_from_memory(&png, &pngdata[0], pngdata.size())) {
// We pad files that have been hashrange'd so they are the same texture size.
level.w = png.width;
level.h = png.height;
PNGHeaderPeek headerPeek;
good = zip_fread(zf, &headerPeek, sizeof(headerPeek)) == sizeof(headerPeek);
if (headerPeek.IsValidPNGHeader()) {
level.w = headerPeek.Width();
level.h = headerPeek.Height();
good = true;
} else {
ERROR_LOG(G3D, "Could not load texture replacement info: %s - %s (zip)", level.file.ToVisualString().c_str(), png.message);
ERROR_LOG(G3D, "Could not get PNG dimensions: %s (zip)", level.file.ToVisualString().c_str());
}
png_image_free(&png);
} else {
ERROR_LOG(G3D, "Could not load texture replacement info: %s - unsupported format (zip)", level.file.ToVisualString().c_str());
}