Skip to content

Commit

Permalink
Always decode 3 or 4 channel PNG images.
Browse files Browse the repository at this point in the history
The bug @HayesGordon ran into with Unity was because we were trying to decode grayscale images to a single-channel image and then expanding those to 4 channels as the Rive Renderer expects RGBA. That was padding missing channel data with 255, which is why we were getting Cyan (R would be read, GBA were filled with 255).

Instead, we make the Bitmap library only work with RGB and RGBA and we let libpng do the expansion for us.

Glorious:
<img width="417" alt="CleanShot 2024-03-08 at 21 28 21@2x" src="https://github.com/rive-app/rive/assets/454182/326e537b-15ad-4914-acb7-a85ddc42c88c">

Diffs=
09feaccb0 Always decode 3 or 4 channel PNG images. (#6818)

Co-authored-by: Luigi Rosso <[email protected]>
  • Loading branch information
luigi-rosso and luigi-rosso committed Mar 9, 2024
1 parent ce2c740 commit 033a452
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 36 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7cb7eb8122d3625aad2c6032615006cc8f5383e8
09feaccb0c5bb3a2f88c23aca6c669d79eee4428
1 change: 0 additions & 1 deletion decoders/include/rive/decoders/bitmap_decoder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ class Bitmap
public:
enum class PixelFormat : uint8_t
{
R,
RGB,
RGBA
};
Expand Down
2 changes: 0 additions & 2 deletions decoders/src/bitmap_decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ size_t Bitmap::bytesPerPixel(PixelFormat format) const
{
switch (format)
{
case PixelFormat::R:
return 1;
case PixelFormat::RGB:
return 3;
case PixelFormat::RGBA:
Expand Down
55 changes: 23 additions & 32 deletions decoders/src/decode_png.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,48 +73,42 @@ std::unique_ptr<Bitmap> DecodePng(const uint8_t bytes[], size_t byteCount)
NULL,
NULL);

png_set_strip_16(png_ptr);

int bitDepth = 0;
if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_RGB)
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);
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_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);
bitDepth = 8;
}
else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)

if (bit_depth == 16)
{
png_set_expand(png_ptr);
png_set_gray_to_rgb(png_ptr);
bitDepth = 32;
png_set_strip_16(png_ptr);
}
else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)

if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
{
png_set_expand(png_ptr);
bitDepth = 32;
png_set_gray_to_rgb(png_ptr);
}

int pixelBytes = bitDepth / 8;
auto pixelBuffer = new uint8_t[width * height * pixelBytes];
png_read_update_info(png_ptr, info_ptr);
uint8_t channels = png_get_channels(png_ptr, info_ptr);

auto pixelBuffer = new uint8_t[width * height * channels];

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));
row_pointers[rIndex] = pixelBuffer + (row * (width * channels));
}
png_read_image(png_ptr, row_pointers);
png_read_end(png_ptr, info_ptr);
Expand All @@ -124,18 +118,15 @@ std::unique_ptr<Bitmap> DecodePng(const uint8_t bytes[], size_t byteCount)
delete[] row_pointers;

Bitmap::PixelFormat pixelFormat;
assert(bitDepth == 32 || bitDepth == 24 || bitDepth == 8);
switch (bitDepth)
assert(channels == 3 || channels == 4);
switch (channels)
{
case 32:
case 4:
pixelFormat = Bitmap::PixelFormat::RGBA;
break;
case 24:
case 3:
pixelFormat = Bitmap::PixelFormat::RGB;
break;
case 8:
pixelFormat = Bitmap::PixelFormat::R;
break;
}
return std::make_unique<Bitmap>(width, height, pixelFormat, pixelBuffer);
}

0 comments on commit 033a452

Please sign in to comment.