diff --git a/src/laybasic/laybasic/layPixelBuffer.cc b/src/laybasic/laybasic/layPixelBuffer.cc index 9a1320353..90d7eafdd 100644 --- a/src/laybasic/laybasic/layPixelBuffer.cc +++ b/src/laybasic/laybasic/layPixelBuffer.cc @@ -28,6 +28,8 @@ # include #endif +#include + namespace lay { @@ -385,6 +387,8 @@ PixelBuffer::read_png (tl::InputStream &input) memcpy ((void *) res.scan_line (i), (void *) row_pointers [i], sizeof (lay::color_t) * res.width ()); } + res.set_transparent (true); + } else if (fmt == PNG_COLOR_TYPE_RGB && bd == 8) { // RGB has 3 bytes per pixel which need to be transformed into RGB32 @@ -398,17 +402,56 @@ PixelBuffer::read_png (tl::InputStream &input) const uint8_t *d = row_pointers [i]; const uint8_t *dd = d + rb; while (d < dd) { - uint8_t b = *d++; - uint8_t g = *d++; - uint8_t r = *d++; + lay::color_t b = *d++; + lay::color_t g = *d++; + lay::color_t r = *d++; *c++ = 0xff000000 | ((r << 8 | g) << 8) | b; } } + } else if (fmt == PNG_COLOR_TYPE_GRAY_ALPHA && bd == 8) { + + // GA format has 2 bytes per pixel (alpha, gray) which need to be transformed into ARGB + + unsigned int rb = png_get_rowbytes (png_ptr, info_ptr); + tl_assert (rb == res.width () * 2); + + png_bytepp row_pointers = png_get_rows (png_ptr, info_ptr); + for (unsigned int i = 0; i < res.height (); ++i) { + lay::color_t *c = res.scan_line (i); + const uint8_t *d = row_pointers [i]; + const uint8_t *dd = d + rb; + while (d < dd) { + lay::color_t g = *d++; + lay::color_t a = *d++; + *c++ = (a << 24) | ((g << 8 | g) << 8) | g; + } + } + + res.set_transparent (true); + + } else if (fmt == PNG_COLOR_TYPE_GRAY && bd == 8) { + + // G format has 1 byte per pixel (gray) which need to be transformed into ARGB + + unsigned int rb = png_get_rowbytes (png_ptr, info_ptr); + tl_assert (rb == res.width ()); + + png_bytepp row_pointers = png_get_rows (png_ptr, info_ptr); + for (unsigned int i = 0; i < res.height (); ++i) { + lay::color_t *c = res.scan_line (i); + const uint8_t *d = row_pointers [i]; + const uint8_t *dd = d + rb; + while (d < dd) { + lay::color_t g = *d++; + *c++ = 0xff000000 | ((g << 8 | g) << 8) | g; + } + } + } else { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - throw PixelBufferReadError (tl::sprintf (tl::to_string (tr ("PNG reader supports 32 bit RGB or RGBA only (file: %s, format is %d, bit depth is %d)")), input.filename (), fmt, bd)); + throw PixelBufferReadError (tl::sprintf (tl::to_string (tr ("PNG reader supports 8 bit G, GA, RGB or RGBA files only (file: %s, format is %d, bit depth is %d)")), input.filename (), fmt, bd)); } @@ -433,7 +476,7 @@ PixelBuffer::write_png (tl::OutputStream &output) const png_set_bgr (png_ptr); // compatible with lay::color_t unsigned int bd = 8; // bit depth - unsigned int fmt = PNG_COLOR_TYPE_RGBA; + unsigned int fmt = transparent () ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB; png_set_IHDR (png_ptr, info_ptr, width (), height (), bd, fmt, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); @@ -448,8 +491,31 @@ PixelBuffer::write_png (tl::OutputStream &output) const png_write_info (png_ptr, info_ptr); - for (unsigned int i = 0; i < height (); ++i) { - png_write_row (png_ptr, png_const_bytep (scan_line (i))); + if (transparent ()) { + + for (unsigned int i = 0; i < height (); ++i) { + png_write_row (png_ptr, png_const_bytep (scan_line (i))); + } + + } else { + + std::unique_ptr buffer (new uint8_t [width () * 3]); + + for (unsigned int i = 0; i < height (); ++i) { + uint8_t *d = buffer.get (); + const lay::color_t *s = scan_line (i); + const lay::color_t *se = s + width (); + while (s != se) { + lay::color_t c = *s++; + *d++ = c & 0xff; + c >>= 8; + *d++ = c & 0xff; + c >>= 8; + *d++ = c & 0xff; + } + png_write_row (png_ptr, png_const_bytep (buffer.get ())); + } + } png_write_end (png_ptr, info_ptr); diff --git a/src/laybasic/unit_tests/layPixelBufferTests.cc b/src/laybasic/unit_tests/layPixelBufferTests.cc index b02ecc53d..fe57b7545 100644 --- a/src/laybasic/unit_tests/layPixelBufferTests.cc +++ b/src/laybasic/unit_tests/layPixelBufferTests.cc @@ -319,9 +319,93 @@ TEST(4) #endif } +TEST(5) +{ + lay::PixelBuffer img; + + std::string in = tl::testsrc () + "/testdata/lay/png3.png"; // GA + tl::info << "PNG file read (libpng) from " << in; + + { + tl::InputStream stream (in); + img = lay::PixelBuffer::read_png (stream); + } + + std::string tmp = tmp_file ("test.png"); + { + tl::OutputStream stream (tmp); + img.write_png (stream); + } + tl::info << "PNG file written to " << tmp; + + lay::PixelBuffer img2; + + { + tl::InputStream stream (tmp); + img2 = lay::PixelBuffer::read_png (stream); + } + + std::string tmp2 = tmp_file ("test2.png"); + { + tl::OutputStream stream (tmp2); + img2.write_png (stream); + } + tl::info << "PNG file written to " << tmp2; + + EXPECT_EQ (compare_images (img, img2), true); + +#if defined (HAVE_QT) + // Qt cross-check + std::string au = tl::testsrc () + "/testdata/lay/au_gs.png"; + EXPECT_EQ (compare_images (img2.to_image (), au), true); +#endif +} + +TEST(6) +{ + lay::PixelBuffer img; + + std::string in = tl::testsrc () + "/testdata/lay/png4.png"; // G + tl::info << "PNG file read (libpng) from " << in; + + { + tl::InputStream stream (in); + img = lay::PixelBuffer::read_png (stream); + } + + std::string tmp = tmp_file ("test.png"); + { + tl::OutputStream stream (tmp); + img.write_png (stream); + } + tl::info << "PNG file written to " << tmp; + + lay::PixelBuffer img2; + + { + tl::InputStream stream (tmp); + img2 = lay::PixelBuffer::read_png (stream); + } + + std::string tmp2 = tmp_file ("test2.png"); + { + tl::OutputStream stream (tmp2); + img2.write_png (stream); + } + tl::info << "PNG file written to " << tmp2; + + EXPECT_EQ (compare_images (img, img2), true); + +#if defined (HAVE_QT) + // Qt cross-check + std::string au = tl::testsrc () + "/testdata/lay/au_gs.png"; + EXPECT_EQ (compare_images (img2.to_image (), au), true); +#endif +} + #endif -TEST(5) +TEST(7) { { tl::SelfTimer timer ("Run time - lay::Image copy, no write (should be very fast)"); diff --git a/testdata/lay/au_gs.png b/testdata/lay/au_gs.png new file mode 100644 index 000000000..427b121bd Binary files /dev/null and b/testdata/lay/au_gs.png differ diff --git a/testdata/lay/png3.png b/testdata/lay/png3.png new file mode 100644 index 000000000..23ea8d767 Binary files /dev/null and b/testdata/lay/png3.png differ diff --git a/testdata/lay/png4.png b/testdata/lay/png4.png new file mode 100644 index 000000000..c22aa09cc Binary files /dev/null and b/testdata/lay/png4.png differ