13 uint8_t * scratchBuffer =
nullptr;
14 unsigned int scratchBufferSize = 0;
16 Mutex & getScratchMutex()
18 static Mutex scratchMutex;
22 void ErrorCb(png_structp png_ptr, png_const_charp) { longjmp(png_jmpbuf(png_ptr), 1); }
23 void WarningCb(png_structp, png_const_charp) {}
28 scratchBuffer =
static_cast<uint8_t *
>(buffer);
29 scratchBufferSize = buffer ? size : 0;
34 return getScratchMutex();
79 ownsTexture = takeOwnership && tex;
80 capWidth = ownsTexture ? w : 0;
81 capHeight = ownsTexture ? h : 0;
88 platform->getVideo()->getImageRenderer()->destroyTexture(
texture);
100static void ReadPngDataCb(png_structp png_ptr, png_bytep data, png_size_t length)
106 memcpy(data, memData->
data + memData->
offset, length);
107 memData->
offset += length;
116 return decodeImage(pngData, &w, &h, maxw, maxh);
119bool GuiImageData::decodeImage(
const uint8_t * pngData,
int * outWidth,
int * outHeight,
int maxw,
int maxh)
124 MutexLock scratchGuard(getScratchMutex());
125 uint8_t *
const localScratchBuffer = scratchBuffer;
126 const unsigned int localScratchBufferSize = scratchBufferSize;
128 if(!localScratchBuffer || localScratchBufferSize == 0)
131 if(png_sig_cmp(
static_cast<png_const_bytep
>(pngData), 0, 8))
134 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
nullptr, ErrorCb, WarningCb);
138 png_infop info_ptr = png_create_info_struct(png_ptr);
141 png_destroy_read_struct(&png_ptr,
nullptr,
nullptr);
145 if(setjmp(png_jmpbuf(png_ptr)))
147 png_destroy_read_struct(&png_ptr, &info_ptr,
nullptr);
151 PngMemoryData memData = { pngData, 0 };
152 png_set_read_fn(png_ptr, &memData, ReadPngDataCb);
154 png_read_info(png_ptr, info_ptr);
156 png_uint_32 srcW, srcH;
157 int bit_depth, color_type, interlace_type;
158 png_get_IHDR(png_ptr, info_ptr, &srcW, &srcH, &bit_depth, &color_type, &interlace_type,
nullptr,
nullptr);
160 png_uint_32 w = srcW, h = srcH;
161 bool needsResize = (maxw > 0 &&
static_cast<int>(srcW) > maxw) || (maxh > 0 &&
static_cast<int>(srcH) > maxh);
164 double wScale = maxw > 0 ?
static_cast<double>(maxw) / srcW : 1e30;
165 double hScale = maxh > 0 ?
static_cast<double>(maxh) / srcH : 1e30;
166 double scale = wScale < hScale ? wScale : hScale;
167 w =
static_cast<png_uint_32
>(srcW * scale);
168 h =
static_cast<png_uint_32
>(srcH * scale);
174 png_set_strip_16(png_ptr);
175 if(color_type == PNG_COLOR_TYPE_PALETTE)
176 png_set_palette_to_rgb(png_ptr);
177 if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
178 png_set_expand_gray_1_2_4_to_8(png_ptr);
179 if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
180 png_set_tRNS_to_alpha(png_ptr);
181 if(color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE)
182 png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER);
183 if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
184 png_set_gray_to_rgb(png_ptr);
186 png_read_update_info(png_ptr, info_ptr);
187 unsigned int rowBytes = png_get_rowbytes(png_ptr, info_ptr);
189 unsigned long long rowPtrBytes =
static_cast<unsigned long long>(srcH) *
sizeof(png_bytep);
190 unsigned long long srcRgbaBytes =
static_cast<unsigned long long>(rowBytes) * srcH;
191 unsigned long long resizedRgbaBytes = needsResize ?
static_cast<unsigned long long>(w) * h * 4 : 0;
192 unsigned long long totalScratchBytes = rowPtrBytes + srcRgbaBytes + resizedRgbaBytes;
193 if(totalScratchBytes > localScratchBufferSize)
195 png_destroy_read_struct(&png_ptr, &info_ptr,
nullptr);
199 bool haveUsableTexture =
texture && (!ownsTexture || (
static_cast<int>(w) <= capWidth &&
static_cast<int>(h) <= capHeight));
202 if(!haveUsableTexture)
207 png_destroy_read_struct(&png_ptr, &info_ptr,
nullptr);
212 png_bytep * row_pointers =
reinterpret_cast<png_bytep *
>(localScratchBuffer);
213 uint8_t * srcRgba = localScratchBuffer + rowPtrBytes;
215 for(png_uint_32 i = 0; i < srcH; i++)
216 row_pointers[i] = srcRgba + (
static_cast<size_t>(i) * rowBytes);
218 png_read_image(png_ptr, row_pointers);
220 const uint8_t * finalRgba = srcRgba;
223 uint8_t * resizedRgba = srcRgba + srcRgbaBytes;
224 uint32_t xRatio = ((srcW << 16) / w) + 1;
225 uint32_t yRatio = ((srcH << 16) / h) + 1;
227 for(png_uint_32 y = 0; y < h; y++)
229 png_uint_32 sy = (y * yRatio) >> 16;
230 if(sy >= srcH) sy = srcH - 1;
231 const uint8_t * srcRow = srcRgba +
static_cast<size_t>(sy) * rowBytes;
232 uint8_t * dstRow = resizedRgba +
static_cast<size_t>(y) * w * 4;
234 for(png_uint_32 x = 0; x < w; x++)
236 png_uint_32 sx = (x * xRatio) >> 16;
237 if(sx >= srcW) sx = srcW - 1;
238 memcpy(dstRow + x * 4, srcRow + sx * 4, 4);
242 finalRgba = resizedRgba;
247 if(!haveUsableTexture)
257 int designW =
static_cast<int>(w);
258 int designH =
static_cast<int>(h);
259 if(maxw == 0 && maxh == 0)
261 const PlatformConfig& config = platform->
getConfig();
262 designW =
static_cast<int>(w / config.
assetScaleX + 0.5f);
263 designH =
static_cast<int>(h / config.
assetScaleY + 0.5f);
264 if(designW < 1) designW = 1;
265 if(designH < 1) designH = 1;
270 if(outWidth) *outWidth = designW;
271 if(outHeight) *outHeight = designH;
273 png_destroy_read_struct(&png_ptr, &info_ptr,
nullptr);
Umbrella header for the platform-agnostic libgui core.
~GuiImageData()
Destructor.
GuiImageData()
Constructor.
void * texture
Attached platform-native texture.
bool reload(const uint8_t *pngData, int maxw=0, int maxh=0)
Decodes new PNG data into this object's own texture, reusing the existing allocation whenever it's al...
static void setDecodeScratch(void *buffer, unsigned int size)
Sets the scratch buffer that every decode (the PNG-decoding constructors, and reload()) will stage it...
int height
Height of image.
static Mutex & scratchLock()
virtual void * createTexture(int width, int height)=0
Allocates an empty hardware-aligned texture buffer.
virtual void loadTextureData(void *texture, const uint8_t *rgba, int width, int height)=0
Loads raw RGBA8 pixels into the pre-allocated texture.
virtual void destroyTexture(void *texture)=0
Destroys a texture created by createTexture.
RAII lock guard - locks on construction, unlocks on destruction.
A simple mutual-exclusion lock.
virtual ImageRenderer * getImageRenderer()=0
Read cursor over an in-memory PNG, used as the libpng read callback's I/O pointer.
const uint8_t * data
Start of the PNG file in memory.
size_t offset
Bytes consumed so far.