| /* |
| * Copyright 2023 Rive |
| */ |
| |
| #include "rive/decoders/bitmap_decoder.hpp" |
| #include "png.h" |
| #include <algorithm> |
| #include <cassert> |
| #include <string.h> |
| #include <setjmp.h> |
| |
| struct EncodedImageBuffer |
| { |
| const uint8_t* bytes; |
| size_t position; |
| size_t size; |
| }; |
| |
| static void ReadDataFromMemory(png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead) |
| { |
| png_voidp a = png_get_io_ptr(png_ptr); |
| if (a == nullptr) |
| { |
| return; |
| } |
| EncodedImageBuffer& stream = *(EncodedImageBuffer*)a; |
| |
| size_t bytesRead = std::min(byteCountToRead, (stream.size - stream.position)); |
| memcpy(outBytes, stream.bytes + stream.position, bytesRead); |
| stream.position += bytesRead; |
| |
| if ((png_size_t)bytesRead != byteCountToRead) |
| { |
| // Report image error? |
| } |
| } |
| |
| std::unique_ptr<Bitmap> DecodePng(const uint8_t bytes[], size_t byteCount) |
| { |
| png_structp png_ptr; |
| png_infop info_ptr; |
| png_uint_32 width, height; |
| int bit_depth, color_type, interlace_type; |
| std::unique_ptr<uint8_t[]> pixelBuffer; |
| std::unique_ptr<png_bytep[]> rowsPointer; |
| |
| png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); |
| |
| if (png_ptr == nullptr) |
| { |
| printf("DecodePng - libpng failed (png_create_read_struct)."); |
| return nullptr; |
| } |
| |
| info_ptr = png_create_info_struct(png_ptr); |
| if (info_ptr == NULL) |
| { |
| png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); |
| printf("DecodePng - libpng failed (png_create_info_struct)."); |
| return nullptr; |
| } |
| |
| if (setjmp(png_jmpbuf(png_ptr))) |
| { |
| png_destroy_read_struct(&png_ptr, &info_ptr, NULL); |
| return nullptr; |
| } |
| |
| EncodedImageBuffer stream = {bytes, 0, byteCount}; |
| |
| png_set_read_fn(png_ptr, &stream, ReadDataFromMemory); |
| |
| png_read_info(png_ptr, info_ptr); |
| |
| png_get_IHDR(png_ptr, |
| info_ptr, |
| &width, |
| &height, |
| &bit_depth, |
| &color_type, |
| &interlace_type, |
| NULL, |
| NULL); |
| |
| if (color_type == PNG_COLOR_TYPE_PALETTE || |
| (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)) |
| { |
| // expand to 3 or 4 channels |
| png_set_expand(png_ptr); |
| } |
| |
| png_bytep trns = 0; |
| int trnsCount = 0; |
| if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) |
| { |
| png_get_tRNS(png_ptr, info_ptr, &trns, &trnsCount, 0); |
| png_set_expand(png_ptr); |
| } |
| |
| if (bit_depth == 16) |
| { |
| png_set_strip_16(png_ptr); |
| } |
| |
| if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) |
| { |
| png_set_gray_to_rgb(png_ptr); |
| } |
| |
| png_read_update_info(png_ptr, info_ptr); |
| uint8_t channels = png_get_channels(png_ptr, info_ptr); |
| |
| size_t pixelBufferSize = |
| static_cast<size_t>(width) * static_cast<size_t>(height) * static_cast<size_t>(channels); |
| pixelBuffer = std::make_unique<uint8_t[]>(pixelBufferSize); |
| const uint8_t* pixelBufferEnd = pixelBuffer.get() + pixelBufferSize; |
| |
| rowsPointer = std::make_unique<png_bytep[]>(height); |
| png_bytep* rows = rowsPointer.get(); |
| size_t rowStride = (size_t)width * (size_t)channels; |
| uint8_t* rowWrite = pixelBuffer.get(); |
| for (png_uint_32 row = 0; row < height; row++) |
| { |
| rows[row] = rowWrite; |
| rowWrite += rowStride; |
| if (rowWrite > pixelBufferEnd) |
| { |
| // Read would overflow. |
| return nullptr; |
| } |
| } |
| png_read_image(png_ptr, rows); |
| png_read_end(png_ptr, info_ptr); |
| |
| png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) nullptr); |
| |
| Bitmap::PixelFormat pixelFormat; |
| assert(channels == 3 || channels == 4); |
| switch (channels) |
| { |
| case 4: |
| pixelFormat = Bitmap::PixelFormat::RGBA; |
| break; |
| case 3: |
| pixelFormat = Bitmap::PixelFormat::RGB; |
| break; |
| } |
| return std::make_unique<Bitmap>(width, height, pixelFormat, std::move(pixelBuffer)); |
| } |