139 MutexLock scratchGuard(getScratchMutex());
140 uint8_t *
const localScratchBuffer = scratchBuffer;
141 const unsigned int localScratchBufferSize = scratchBufferSize;
143 if(!localScratchBuffer || localScratchBufferSize == 0)
146 if(png_sig_cmp(
static_cast<png_const_bytep
>(pngData), 0, 8))
149 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
nullptr, ErrorCb, WarningCb);
153 png_infop info_ptr = png_create_info_struct(png_ptr);
156 png_destroy_read_struct(&png_ptr,
nullptr,
nullptr);
160 if(setjmp(png_jmpbuf(png_ptr)))
162 png_destroy_read_struct(&png_ptr, &info_ptr,
nullptr);
167 png_set_read_fn(png_ptr, &memData, ReadPngDataCb);
169 png_read_info(png_ptr, info_ptr);
171 png_uint_32 srcW, srcH;
172 int bit_depth, color_type, interlace_type;
173 png_get_IHDR(png_ptr, info_ptr, &srcW, &srcH, &bit_depth, &color_type, &interlace_type,
nullptr,
nullptr);
175 png_uint_32 w = srcW, h = srcH;
176 bool needsResize = (maxw > 0 &&
static_cast<int>(srcW) > maxw) || (maxh > 0 &&
static_cast<int>(srcH) > maxh);
179 double wScale = maxw > 0 ?
static_cast<double>(maxw) / srcW : 1e30;
180 double hScale = maxh > 0 ?
static_cast<double>(maxh) / srcH : 1e30;
181 double scale = wScale < hScale ? wScale : hScale;
182 w =
static_cast<png_uint_32
>(srcW * scale);
183 h =
static_cast<png_uint_32
>(srcH * scale);
189 png_set_strip_16(png_ptr);
190 if(color_type == PNG_COLOR_TYPE_PALETTE)
191 png_set_palette_to_rgb(png_ptr);
192 if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
193 png_set_expand_gray_1_2_4_to_8(png_ptr);
194 if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
195 png_set_tRNS_to_alpha(png_ptr);
196 if(color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE)
197 png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER);
198 if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
199 png_set_gray_to_rgb(png_ptr);
201 png_read_update_info(png_ptr, info_ptr);
202 unsigned int rowBytes = png_get_rowbytes(png_ptr, info_ptr);
204 unsigned long long rowPtrBytes =
static_cast<unsigned long long>(srcH) *
sizeof(png_bytep);
205 unsigned long long srcRgbaBytes =
static_cast<unsigned long long>(rowBytes) * srcH;
206 unsigned long long resizedRgbaBytes = needsResize ?
static_cast<unsigned long long>(w) * h * 4 : 0;
207 unsigned long long totalScratchBytes = rowPtrBytes + srcRgbaBytes + resizedRgbaBytes;
208 if(totalScratchBytes > localScratchBufferSize)
210 png_destroy_read_struct(&png_ptr, &info_ptr,
nullptr);
216 std::unique_ptr<uint8_t,
decltype(&free)> outRgba(
static_cast<uint8_t *
>(malloc(
static_cast<size_t>(w) * h * 4)), free);
219 png_destroy_read_struct(&png_ptr, &info_ptr,
nullptr);
223 png_bytep * row_pointers =
reinterpret_cast<png_bytep *
>(localScratchBuffer);
224 uint8_t * srcRgba = localScratchBuffer + rowPtrBytes;
226 for(png_uint_32 i = 0; i < srcH; i++)
227 row_pointers[i] = srcRgba + (
static_cast<size_t>(i) * rowBytes);
229 png_read_image(png_ptr, row_pointers);
233 uint32_t xRatio = ((srcW << 16) / w) + 1;
234 uint32_t yRatio = ((srcH << 16) / h) + 1;
236 for(png_uint_32 y = 0; y < h; y++)
238 png_uint_32 sy = (y * yRatio) >> 16;
239 if(sy >= srcH) sy = srcH - 1;
240 const uint8_t * srcRow = srcRgba +
static_cast<size_t>(sy) * rowBytes;
241 uint8_t * dstRow = outRgba.get() +
static_cast<size_t>(y) * w * 4;
243 for(png_uint_32 x = 0; x < w; x++)
245 png_uint_32 sx = (x * xRatio) >> 16;
246 if(sx >= srcW) sx = srcW - 1;
247 memcpy(dstRow + x * 4, srcRow + sx * 4, 4);
253 memcpy(outRgba.get(), srcRgba, srcRgbaBytes);
256 png_destroy_read_struct(&png_ptr, &info_ptr,
nullptr);
258 out.
rgba = std::move(outRgba);
318 if(!pngData || boxW < 1 || boxH < 1)
331 const int dstW =
static_cast<int>(boxW * sx + 0.5f);
332 const int dstH =
static_cast<int>(boxH * sy + 0.5f);
335 out.
rgba.reset(
static_cast<uint8_t *
>(calloc(
static_cast<size_t>(dstW) * dstH, 4)));
343 const int shiftX =
static_cast<int>(offsetX * sx + (offsetX < 0 ? -0.5f : 0.5f));
344 const int shiftY =
static_cast<int>(offsetY * sy + (offsetY < 0 ? -0.5f : 0.5f));
345 const int originX = (src.width - dstW) / 2 - shiftX;
346 const int originY = (src.height - dstH) / 2 - shiftY;
349 const float halfW = boxW * 0.5f - inset;
350 const float halfH = boxH * 0.5f - inset;
351 float r = radius - inset;
352 if(r > halfW) r = halfW;
353 if(r > halfH) r = halfH;
354 if(r < 0.0f) r = 0.0f;
355 const float fringe = sx < sy ? sx : sy;
357 for(
int y = 0; y < dstH; ++y)
359 const int srcY = y + originY;
360 if(srcY < 0 || srcY >= src.height)
363 const float qy = fabsf((y + 0.5f) / sy - boxH * 0.5f) - (halfH - r);
365 for(
int x = 0; x < dstW; ++x)
367 const int srcX = x + originX;
368 if(srcX < 0 || srcX >= src.width)
371 const uint8_t * s = src.
rgba.get() + (
static_cast<size_t>(srcY) * src.width + srcX) * 4;
375 const float qx = fabsf((x + 0.5f) / sx - boxW * 0.5f) - (halfW - r);
376 const float ox = qx > 0.0f ? qx : 0.0f;
377 const float oy = qy > 0.0f ? qy : 0.0f;
378 const float inner = qx > qy ? qx : qy;
379 const float dist = sqrtf(ox * ox + oy * oy) + (inner < 0.0f ? inner : 0.0f) - r;
381 float coverage = 0.5f - dist * fringe;
387 uint8_t * d = out.
rgba.get() + (
static_cast<size_t>(y) * dstW + x) * 4;
391 d[3] =
static_cast<uint8_t
>(s[3] * coverage + 0.5f);