Skip to content

Commit

Permalink
png: handle invalid buffer size returned by libspng
Browse files Browse the repository at this point in the history
sometimes (no clue why) spng_decoded_image_size is just plain wrong. In those cases, just guess what the size should be with 32bpp.

fixes #9
  • Loading branch information
vaxerski committed Jan 27, 2025
1 parent 23783b9 commit 12cd703
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 16 deletions.
16 changes: 8 additions & 8 deletions src/image/Image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,23 @@ Hyprgraphics::CImage::CImage(const std::string& path) : filepath(path) {
const auto len = path.length();
if (path.find(".png") == len - 4 || path.find(".PNG") == len - 4) {
CAIROSURFACE = PNG::createSurfaceFromPNG(path);
mime = "image/png";
mime = "image/png";
} else if (path.find(".jpg") == len - 4 || path.find(".JPG") == len - 4 || path.find(".jpeg") == len - 5 || path.find(".JPEG") == len - 5) {
CAIROSURFACE = JPEG::createSurfaceFromJPEG(path);
imageHasAlpha = false;
mime = "image/jpeg";
mime = "image/jpeg";
} else if (path.find(".bmp") == len - 4 || path.find(".BMP") == len - 4) {
CAIROSURFACE = BMP::createSurfaceFromBMP(path);
imageHasAlpha = false;
mime = "image/bmp";
mime = "image/bmp";
} else if (path.find(".webp") == len - 5 || path.find(".WEBP") == len - 5) {
CAIROSURFACE = WEBP::createSurfaceFromWEBP(path);
mime = "image/webp";
mime = "image/webp";
} else if (path.find(".jxl") == len - 4 || path.find(".JXL") == len - 4) {

#ifdef JXL_FOUND
CAIROSURFACE = JXL::createSurfaceFromJXL(path);
mime = "image/jxl";
mime = "image/jxl";
#else
lastError = "hyprgraphics compiled without JXL support";
return;
Expand All @@ -49,15 +49,15 @@ Hyprgraphics::CImage::CImage(const std::string& path) : filepath(path) {

if (first_word == "PNG") {
CAIROSURFACE = PNG::createSurfaceFromPNG(path);
mime = "image/png";
mime = "image/png";
} else if (first_word == "JPEG") {
CAIROSURFACE = JPEG::createSurfaceFromJPEG(path);
imageHasAlpha = false;
mime = "image/jpeg";
mime = "image/jpeg";
} else if (first_word == "BMP") {
CAIROSURFACE = BMP::createSurfaceFromBMP(path);
imageHasAlpha = false;
mime = "image/bmp";
mime = "image/bmp";
} else {
lastError = "unrecognized image";
return;
Expand Down
4 changes: 2 additions & 2 deletions src/image/formats/Bmp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ std::expected<cairo_surface_t*, std::string> BMP::createSurfaceFromBMP(const std
if (!std::filesystem::exists(path))
return std::unexpected("loading bmp: file doesn't exist");

std::ifstream bitmapImageStream(path);
BmpHeader bitmapHeader;
std::ifstream bitmapImageStream(path);
BmpHeader bitmapHeader;
if (const auto RET = bitmapHeader.load(bitmapImageStream); RET.has_value())
return std::unexpected("loading bmp: " + *RET);

Expand Down
28 changes: 22 additions & 6 deletions src/image/formats/Png.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,42 @@ std::expected<cairo_surface_t*, std::string> PNG::createSurfaceFromPNG(const std
spng_set_png_buffer(ctx, PNGCONTENT.data(), PNGCONTENT.size());

spng_ihdr ihdr{0};
if (spng_get_ihdr(ctx, &ihdr))
return std::unexpected("loading png: file content was empty (bad file?)");
if (int ret = spng_get_ihdr(ctx, &ihdr); ret)
return std::unexpected(std::string{"loading png: spng_get_ihdr failed: "} + spng_strerror(ret));

int fmt = SPNG_FMT_PNG;
if (ihdr.color_type == SPNG_COLOR_TYPE_INDEXED)
fmt = SPNG_FMT_RGB8;

size_t imageLength = 0;
if (spng_decoded_image_size(ctx, fmt, &imageLength))
return std::unexpected("loading png: spng_decoded_image_size failed");
if (int ret = spng_decoded_image_size(ctx, fmt, &imageLength); ret)
return std::unexpected(std::string{"loading png: spng_decoded_image_size failed: "} + spng_strerror(ret));

uint8_t* imageData = (uint8_t*)malloc(imageLength);

if (!imageData)
return std::unexpected("loading png: mallocing failed, out of memory?");

// TODO: allow proper decode of high bitrate images
if (spng_decode_image(ctx, imageData, imageLength, SPNG_FMT_RGBA8, 0)) {
bool succeededDecode = false;
int ret = spng_decode_image(ctx, imageData, imageLength, SPNG_FMT_RGBA8, 0);
if (!ret)
succeededDecode = true;

if (!succeededDecode && ret == SPNG_EBUFSIZ) {
// hack, but I don't know why decoded_image_size is sometimes wrong
imageLength = ihdr.height * ihdr.width * 4 /* FIXME: this is wrong if we doing >32bpp!!!! */;
imageData = (uint8_t*)realloc(imageData, imageLength);

ret = spng_decode_image(ctx, imageData, imageLength, SPNG_FMT_RGBA8, 0);
}

if (!ret)
succeededDecode = true;

if (!succeededDecode) {
free(imageData);
return std::unexpected("loading png: spng_decode_image failed (invalid image?)");
return std::unexpected(std::string{"loading png: spng_decode_image failed: "} + spng_strerror(ret) + " (bad image?)");
}

// convert RGBA8888 -> ARGB8888 premult for cairo
Expand Down

0 comments on commit 12cd703

Please sign in to comment.