| #ifdef RIVE_RENDERER_TESS | |
| #include "viewer/tess/bitmap_decoder.hpp" | |
| #include "png.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(rive::Span<const uint8_t> bytes) | |
| { | |
| png_structp png_ptr; | |
| png_infop info_ptr; | |
| png_uint_32 width, height; | |
| int bit_depth, color_type, interlace_type; | |
| 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; | |
| } | |
| EncodedImageBuffer stream = { | |
| .bytes = bytes.data(), | |
| .size = bytes.size(), | |
| .position = 0, | |
| }; | |
| 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); | |
| png_set_strip_16(png_ptr); | |
| int bitDepth = 0; | |
| if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_RGB) | |
| { | |
| png_set_expand(png_ptr); | |
| bitDepth = 24; | |
| if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) | |
| { | |
| png_set_expand(png_ptr); | |
| bitDepth += 8; | |
| } | |
| } | |
| else if (color_type == PNG_COLOR_TYPE_GRAY) | |
| { | |
| png_set_expand(png_ptr); | |
| bitDepth = 8; | |
| } | |
| else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) | |
| { | |
| png_set_expand(png_ptr); | |
| png_set_gray_to_rgb(png_ptr); | |
| bitDepth = 32; | |
| } | |
| else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) | |
| { | |
| png_set_expand(png_ptr); | |
| bitDepth = 32; | |
| } | |
| int pixelBytes = bitDepth / 8; | |
| auto pixelBuffer = new uint8_t[width * height * pixelBytes]; | |
| png_bytep* row_pointers = new png_bytep[height]; | |
| for (unsigned row = 0; row < height; row++) | |
| { | |
| unsigned int rIndex = row; | |
| // if (flipY) { | |
| // rIndex = height - row - 1; | |
| // } | |
| row_pointers[rIndex] = pixelBuffer + (row * (width * pixelBytes)); | |
| } | |
| png_read_image(png_ptr, row_pointers); | |
| png_read_end(png_ptr, info_ptr); | |
| png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) nullptr); | |
| delete[] row_pointers; | |
| Bitmap::PixelFormat pixelFormat; | |
| assert(bitDepth == 32 || bitDepth == 24 || bitDepth == 8); | |
| switch (bitDepth) | |
| { | |
| case 32: | |
| pixelFormat = Bitmap::PixelFormat::RGBA; | |
| break; | |
| case 24: | |
| pixelFormat = Bitmap::PixelFormat::RGB; | |
| break; | |
| case 8: | |
| pixelFormat = Bitmap::PixelFormat::R; | |
| break; | |
| } | |
| return rivestd::make_unique<Bitmap>(width, height, pixelFormat, pixelBuffer); | |
| } | |
| #endif |