mirror of https://github.com/KLayout/klayout.git
WIP: PNG support for Qt-less apps through libpng in lay::PixelBuffer
This commit is contained in:
parent
7291a3dc47
commit
067f59ab0a
|
|
@ -72,6 +72,15 @@ equals(HAVE_CURL, "1") {
|
|||
DEFINES += HAVE_CURL
|
||||
}
|
||||
|
||||
equals(HAVE_PNG, "1") {
|
||||
!isEmpty(BITS_PATH) {
|
||||
include($$BITS_PATH/png/png.pri)
|
||||
} else {
|
||||
LIBS += -lpng
|
||||
}
|
||||
DEFINES += HAVE_PNG
|
||||
}
|
||||
|
||||
equals(HAVE_EXPAT, "1") {
|
||||
!isEmpty(BITS_PATH) {
|
||||
include($$BITS_PATH/expat/expat.pri)
|
||||
|
|
|
|||
|
|
@ -22,10 +22,92 @@
|
|||
|
||||
#include "layPixelBuffer.h"
|
||||
#include "tlAssert.h"
|
||||
#include "tlLog.h"
|
||||
|
||||
#if defined(HAVE_PNG)
|
||||
# include <png.h>
|
||||
#endif
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// Exceptions
|
||||
|
||||
PixelBufferReadError::PixelBufferReadError (const char *msg)
|
||||
: tl::Exception (tl::to_string (tr ("PNG read error: ")) + std::string (msg))
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
PixelBufferReadError::PixelBufferReadError (const std::string &msg)
|
||||
: tl::Exception (tl::to_string (tr ("PNG read error: ")) + msg)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
PixelBufferWriteError::PixelBufferWriteError (const char *msg)
|
||||
: tl::Exception (tl::to_string (tr ("PNG write error: ")) + std::string (msg))
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
PixelBufferWriteError::PixelBufferWriteError (const std::string &msg)
|
||||
: tl::Exception (tl::to_string (tr ("PNG write error: ")) + msg)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
#if defined(HAVE_PNG)
|
||||
|
||||
static void png_read_warn_f (png_structp /*png_ptr*/, png_const_charp error_message)
|
||||
{
|
||||
tl::warn << tl::to_string (tr ("Warning reading PNG: ")) << error_message;
|
||||
}
|
||||
|
||||
static void png_read_error_f (png_structp /*png_ptr*/, png_const_charp error_message)
|
||||
{
|
||||
throw PixelBufferReadError (error_message);
|
||||
}
|
||||
|
||||
static void png_write_warn_f (png_structp /*png_ptr*/, png_const_charp error_message)
|
||||
{
|
||||
tl::warn << tl::to_string (tr ("Warning writing PNG: ")) << error_message;
|
||||
}
|
||||
|
||||
static void png_write_error_f (png_structp /*png_ptr*/, png_const_charp error_message)
|
||||
{
|
||||
throw PixelBufferReadError (error_message);
|
||||
}
|
||||
|
||||
static void read_from_stream_f (png_structp png_ptr, png_bytep bytes, size_t length)
|
||||
{
|
||||
tl::InputStream *stream = (tl::InputStream *) png_get_io_ptr (png_ptr);
|
||||
try {
|
||||
memcpy (bytes, stream->get (length), length);
|
||||
} catch (tl::Exception &ex) {
|
||||
png_error (png_ptr, ex.msg ().c_str ());
|
||||
}
|
||||
}
|
||||
|
||||
static void write_to_stream_f (png_structp png_ptr, png_bytep bytes, size_t length)
|
||||
{
|
||||
tl::OutputStream *stream = (tl::OutputStream *) png_get_io_ptr (png_ptr);
|
||||
try {
|
||||
stream->put ((const char *) bytes, length);
|
||||
} catch (tl::Exception &ex) {
|
||||
png_error (png_ptr, ex.msg ().c_str ());
|
||||
}
|
||||
}
|
||||
|
||||
static void flush_stream_f (png_structp png_ptr)
|
||||
{
|
||||
tl::OutputStream *stream = (tl::OutputStream *) png_get_io_ptr (png_ptr);
|
||||
stream->flush ();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// PixelBuffer implementation
|
||||
|
||||
|
|
@ -228,6 +310,103 @@ PixelBuffer::diff (const PixelBuffer &other) const
|
|||
return res;
|
||||
}
|
||||
|
||||
#if defined(HAVE_PNG)
|
||||
|
||||
PixelBuffer
|
||||
PixelBuffer::read_png (tl::InputStream &input)
|
||||
{
|
||||
png_structp png_ptr = NULL;
|
||||
png_infop info_ptr = NULL;
|
||||
|
||||
png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, &png_read_error_f, &png_read_warn_f);
|
||||
tl_assert (png_ptr != NULL);
|
||||
|
||||
info_ptr = png_create_info_struct (png_ptr);
|
||||
tl_assert (info_ptr != NULL);
|
||||
|
||||
png_set_read_fn (png_ptr, (void *) &input, &read_from_stream_f);
|
||||
png_set_bgr (png_ptr); // compatible with lay::color_t
|
||||
|
||||
png_read_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
|
||||
|
||||
PixelBuffer res (png_get_image_width (png_ptr, info_ptr), png_get_image_height (png_ptr, info_ptr));
|
||||
|
||||
unsigned int fmt = png_get_color_type (png_ptr, info_ptr);
|
||||
unsigned int bd = png_get_bit_depth (png_ptr, info_ptr);
|
||||
|
||||
if (fmt == PNG_COLOR_TYPE_RGBA && bd == 8) {
|
||||
|
||||
tl_assert (png_get_rowbytes (png_ptr, info_ptr) == res.width () * sizeof (lay::color_t));
|
||||
|
||||
png_bytepp row_pointers = png_get_rows (png_ptr, info_ptr);
|
||||
for (unsigned int i = 0; i < res.height (); ++i) {
|
||||
memcpy ((void *) res.scan_line (i), (void *) row_pointers [i], sizeof (lay::color_t) * res.width ());
|
||||
}
|
||||
|
||||
} else if (fmt == PNG_COLOR_TYPE_RGB && bd == 8) {
|
||||
|
||||
// RGB has 3 bytes per pixel which need to be transformed into RGB32
|
||||
|
||||
unsigned int rb = png_get_rowbytes (png_ptr, info_ptr);
|
||||
tl_assert (rb == res.width () * 3);
|
||||
|
||||
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) {
|
||||
uint8_t b = *d++;
|
||||
uint8_t g = *d++;
|
||||
uint8_t r = *d++;
|
||||
*c++ = 0xff000000 | ((r << 8 | g) << 8) | b;
|
||||
}
|
||||
}
|
||||
|
||||
} 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));
|
||||
|
||||
}
|
||||
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
PixelBuffer::write_png (tl::OutputStream &output)
|
||||
{
|
||||
png_structp png_ptr = NULL;
|
||||
png_infop info_ptr = NULL;
|
||||
|
||||
png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, &png_write_error_f, &png_write_warn_f);
|
||||
tl_assert (png_ptr != NULL);
|
||||
|
||||
info_ptr = png_create_info_struct (png_ptr);
|
||||
tl_assert (info_ptr != NULL);
|
||||
|
||||
png_set_write_fn (png_ptr, (void *) &output, &write_to_stream_f, &flush_stream_f);
|
||||
png_set_bgr (png_ptr); // compatible with lay::color_t
|
||||
|
||||
unsigned int bd = 8; // bit depth
|
||||
unsigned int fmt = PNG_COLOR_TYPE_RGBA;
|
||||
|
||||
png_set_IHDR (png_ptr, info_ptr, width (), height (), bd, fmt, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
png_write_end (png_ptr, info_ptr);
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// BitmapBuffer implementation
|
||||
|
||||
|
|
@ -382,4 +561,81 @@ BitmapBuffer::to_image_copy () const
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PNG)
|
||||
|
||||
BitmapBuffer
|
||||
BitmapBuffer::read_png (tl::InputStream &input)
|
||||
{
|
||||
png_structp png_ptr = NULL;
|
||||
png_infop info_ptr = NULL;
|
||||
|
||||
png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, &png_read_error_f, &png_read_warn_f);
|
||||
tl_assert (png_ptr != NULL);
|
||||
|
||||
info_ptr = png_create_info_struct (png_ptr);
|
||||
tl_assert (info_ptr != NULL);
|
||||
|
||||
png_set_read_fn (png_ptr, (void *) &input, &read_from_stream_f);
|
||||
|
||||
png_read_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
|
||||
|
||||
BitmapBuffer res (png_get_image_width (png_ptr, info_ptr), png_get_image_height (png_ptr, info_ptr));
|
||||
|
||||
unsigned int fmt = png_get_color_type (png_ptr, info_ptr);
|
||||
unsigned int bd = png_get_bit_depth (png_ptr, info_ptr);
|
||||
|
||||
if (fmt == PNG_COLOR_TYPE_GRAY && bd == 1) {
|
||||
|
||||
size_t rb = png_get_rowbytes (png_ptr, info_ptr);
|
||||
tl_assert (rb == (res.width () + 7) / 8);
|
||||
|
||||
png_bytepp row_pointers = png_get_rows (png_ptr, info_ptr);
|
||||
for (unsigned int i = 0; i < res.height (); ++i) {
|
||||
memcpy ((void *) res.scan_line (i), (void *) row_pointers [i], rb);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
throw PixelBufferReadError (tl::sprintf (tl::to_string (tr ("PNG bitmap reader supports monochrome files only (file: %s, format is %d, bit depth is %d)")), input.filename (), fmt, bd));
|
||||
|
||||
}
|
||||
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
BitmapBuffer::write_png (tl::OutputStream &output)
|
||||
{
|
||||
png_structp png_ptr = NULL;
|
||||
png_infop info_ptr = NULL;
|
||||
|
||||
png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, &png_write_error_f, &png_write_warn_f);
|
||||
tl_assert (png_ptr != NULL);
|
||||
|
||||
info_ptr = png_create_info_struct (png_ptr);
|
||||
tl_assert (info_ptr != NULL);
|
||||
|
||||
png_set_write_fn (png_ptr, (void *) &output, &write_to_stream_f, &flush_stream_f);
|
||||
|
||||
unsigned int bd = 1; // bit depth
|
||||
unsigned int fmt = PNG_COLOR_TYPE_GRAY;
|
||||
|
||||
png_set_IHDR (png_ptr, info_ptr, width (), height (), bd, fmt, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
png_write_end (png_ptr, info_ptr);
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
#include "laybasicCommon.h"
|
||||
#include "layColor.h"
|
||||
#include "tlCopyOnWrite.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlException.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <cstdint>
|
||||
|
|
@ -38,13 +40,34 @@
|
|||
namespace lay
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief An exception thrown when a PNG read error occurs
|
||||
*/
|
||||
class LAYBASIC_PUBLIC PixelBufferReadError
|
||||
: public tl::Exception
|
||||
{
|
||||
public:
|
||||
PixelBufferReadError (const char *msg);
|
||||
PixelBufferReadError (const std::string &msg);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An exception thrown when a PNG write error occurs
|
||||
*/
|
||||
class LAYBASIC_PUBLIC PixelBufferWriteError
|
||||
: public tl::Exception
|
||||
{
|
||||
public:
|
||||
PixelBufferWriteError (const char *msg);
|
||||
PixelBufferWriteError (const std::string &msg);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An 32bit RGB/RGBA image class
|
||||
*
|
||||
* This class substitutes QImage in Qt-less applications.
|
||||
* It provides 32bit RGBA pixels with the format used by lay::Color.
|
||||
*/
|
||||
|
||||
class LAYBASIC_PUBLIC PixelBuffer
|
||||
{
|
||||
public:
|
||||
|
|
@ -183,6 +206,20 @@ public:
|
|||
QImage to_image_copy () const;
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PNG)
|
||||
/**
|
||||
* @brief Creates a PixelBuffer object from a PNG file
|
||||
* Throws a PixelBufferReadError if an error occurs.
|
||||
*/
|
||||
static PixelBuffer read_png (tl::InputStream &input);
|
||||
|
||||
/**
|
||||
* @brief Writes the PixelBuffer object to a PNG file
|
||||
* Throws a PixelBufferWriteError if an error occurs.
|
||||
*/
|
||||
void write_png (tl::OutputStream &output);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Overlays the other image with this one
|
||||
*
|
||||
|
|
@ -379,6 +416,20 @@ public:
|
|||
QImage to_image_copy () const;
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PNG)
|
||||
/**
|
||||
* @brief Creates a PixelBuffer object from a PNG file
|
||||
* Throws a PixelBufferReadError if an error occurs.
|
||||
*/
|
||||
static BitmapBuffer read_png (tl::InputStream &input);
|
||||
|
||||
/**
|
||||
* @brief Writes the PixelBuffer object to a PNG file
|
||||
* Throws a PixelBufferWriteError if an error occurs.
|
||||
*/
|
||||
void write_png (tl::OutputStream &output);
|
||||
#endif
|
||||
|
||||
private:
|
||||
class MonoImageData
|
||||
{
|
||||
|
|
|
|||
|
|
@ -250,6 +250,95 @@ TEST(2)
|
|||
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PNG)
|
||||
|
||||
// libpng support
|
||||
TEST(2b)
|
||||
{
|
||||
lay::PixelBuffer img;
|
||||
|
||||
std::string in = tl::testsrc () + "/testdata/lay/png1.png"; // ARGB32
|
||||
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);
|
||||
}
|
||||
|
||||
EXPECT_EQ (compare_images (img, img2), true);
|
||||
|
||||
std::string tmp2 = tmp_file ("test2.png");
|
||||
{
|
||||
tl::OutputStream stream (tmp2);
|
||||
img2.write_png (stream);
|
||||
}
|
||||
tl::info << "PNG file written to " << tmp2;
|
||||
|
||||
#if defined (HAVE_QT)
|
||||
// Qt cross-check
|
||||
std::string au = tl::testsrc () + "/testdata/lay/au.png";
|
||||
EXPECT_EQ (compare_images (img2.to_image (), au), true);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(2c)
|
||||
{
|
||||
lay::PixelBuffer img;
|
||||
|
||||
std::string in = tl::testsrc () + "/testdata/lay/png2.png"; // RGB32
|
||||
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);
|
||||
}
|
||||
|
||||
EXPECT_EQ (compare_images (img, img2), true);
|
||||
|
||||
std::string tmp2 = tmp_file ("test2.png");
|
||||
{
|
||||
tl::OutputStream stream (tmp2);
|
||||
img2.write_png (stream);
|
||||
}
|
||||
tl::info << "PNG file written to " << tmp2;
|
||||
|
||||
#if defined (HAVE_QT)
|
||||
// Qt cross-check
|
||||
std::string au = tl::testsrc () + "/testdata/lay/au.png";
|
||||
EXPECT_EQ (compare_images (img2.to_image (), au), true);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
TEST(3)
|
||||
{
|
||||
{
|
||||
|
|
@ -449,3 +538,50 @@ TEST(12)
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PNG)
|
||||
|
||||
// libpng support
|
||||
TEST(12b)
|
||||
{
|
||||
lay::BitmapBuffer img;
|
||||
|
||||
std::string in = tl::testsrc () + "/testdata/lay/au_mono.png";
|
||||
tl::info << "PNG file read (libpng) from " << in;
|
||||
|
||||
{
|
||||
tl::InputStream stream (in);
|
||||
img = lay::BitmapBuffer::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::BitmapBuffer img2;
|
||||
|
||||
{
|
||||
tl::InputStream stream (tmp);
|
||||
img2 = lay::BitmapBuffer::read_png (stream);
|
||||
}
|
||||
|
||||
EXPECT_EQ (compare_images (img, img2), true);
|
||||
|
||||
std::string tmp2 = tmp_file ("test2.png");
|
||||
{
|
||||
tl::OutputStream stream (tmp2);
|
||||
img2.write_png (stream);
|
||||
}
|
||||
tl::info << "PNG file written to " << tmp2;
|
||||
|
||||
#if defined (HAVE_QT)
|
||||
// Qt cross-check
|
||||
std::string au = tl::testsrc () + "/testdata/lay/au_mono.png";
|
||||
EXPECT_EQ (compare_images (img2.to_image ().convertToFormat (QImage::Format_Mono), au), true);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue