Refactoring: lay::PixelBuffer, BitmapBuffer -> tl, added image functions to RdbItem in GSI

This commit is contained in:
Matthias Koefferlein
2022-05-31 00:20:47 +02:00
parent 62bbef53ac
commit b95027a21b
33 changed files with 379 additions and 240 deletions
@@ -327,12 +327,12 @@ static void save_as2 (lay::LayoutViewBase *view, unsigned int index, const std::
view->save_as (index, filename, tl::OutputStream::OM_Auto, options, true, 0);
}
static lay::PixelBuffer get_pixels_with_options (lay::LayoutViewBase *view, unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution, const db::DBox &target_box)
static tl::PixelBuffer get_pixels_with_options (lay::LayoutViewBase *view, unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution, const db::DBox &target_box)
{
return view->get_pixels_with_options (width, height, linewidth, oversampling, resolution, tl::Color (), tl::Color (), tl::Color (), target_box);
}
static lay::BitmapBuffer get_pixels_with_options_mono (lay::LayoutViewBase *view, unsigned int width, unsigned int height, int linewidth, const db::DBox &target_box)
static tl::BitmapBuffer get_pixels_with_options_mono (lay::LayoutViewBase *view, unsigned int width, unsigned int height, int linewidth, const db::DBox &target_box)
{
return view->get_pixels_with_options_mono (width, height, linewidth, tl::Color (), tl::Color (), tl::Color (), target_box);
}
@@ -1129,7 +1129,7 @@ LAYBASIC_PUBLIC Class<lay::LayoutViewBase> decl_LayoutViewBase (QT_EXTERNAL_BASE
"This method has been introduced in 0.23.10.\n"
) +
#endif
gsi::method ("get_screenshot_pixels", static_cast<lay::PixelBuffer (lay::LayoutViewBase::*) ()> (&lay::LayoutViewBase::get_screenshot_pb),
gsi::method ("get_screenshot_pixels", static_cast<tl::PixelBuffer (lay::LayoutViewBase::*) ()> (&lay::LayoutViewBase::get_screenshot_pb),
"@brief Gets a screenshot as a \\PixelBuffer\n"
"\n"
"Getting the image requires the drawing to be complete. Ideally, synchronous mode is switched on "
@@ -1138,7 +1138,7 @@ LAYBASIC_PUBLIC Class<lay::LayoutViewBase> decl_LayoutViewBase (QT_EXTERNAL_BASE
"\n"
"This method has been introduced in 0.28.\n"
) +
gsi::method ("get_pixels", static_cast<lay::PixelBuffer (lay::LayoutViewBase::*) (unsigned int, unsigned int)> (&lay::LayoutViewBase::get_pixels), gsi::arg ("width"), gsi::arg ("height"),
gsi::method ("get_pixels", static_cast<tl::PixelBuffer (lay::LayoutViewBase::*) (unsigned int, unsigned int)> (&lay::LayoutViewBase::get_pixels), gsi::arg ("width"), gsi::arg ("height"),
"@brief Gets the layout image as a \\PixelBuffer\n"
"\n"
"@param width The width of the image to render in pixel.\n"
@@ -1,414 +0,0 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2022 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gsiDecl.h"
#include "layPixelBuffer.h"
#if defined(HAVE_QT)
# include <QBuffer>
# include <QColor>
#endif
namespace gsi
{
// -------------------------------------------------------------------------------------
// lay::BitmapBuffer
static lay::PixelBuffer *create_pixel_buffer (unsigned int w, unsigned int h)
{
return new lay::PixelBuffer (w, h);
}
#if defined(HAVE_QT) && defined(HAVE_QTBINDINGS)
static void fill_with_qcolor (lay::PixelBuffer *pb, QColor c)
{
pb->fill (c.rgb ());
}
#endif
tl::color_t get_pixel_from_pixel_buffer (const lay::PixelBuffer *pb, unsigned int x, unsigned int y)
{
if (x < pb->width () && y < pb->height ()) {
return pb->scan_line (y)[x];
} else {
return tl::color_t (0);
}
}
void set_pixel_in_pixel_buffer (lay::PixelBuffer *pb, unsigned int x, unsigned int y, tl::color_t c)
{
if (! pb->transparent ()) {
c |= 0xff000000; // ensures that alpha is set properly even if not required
}
if (x < pb->width () && y < pb->height ()) {
pb->scan_line (y)[x] = c;
}
}
static lay::PixelBuffer read_pixel_buffer (const std::string &file)
{
#if defined(HAVE_PNG)
tl::InputStream stream (file);
return lay::PixelBuffer::read_png (stream);
#elif defined(HAVE_QT) && defined(HAVE_QTBINDINGS)
// QImage is fallback
QImage img;
img.load (tl::to_qstring (file), "PNG");
return lay::PixelBuffer::from_image (img);
#else
throw tl::Exception (tl::to_string (tr ("No PNG support compiled in for PixelBuffer")));
return lay::PixelBuffer ();
#endif
}
// TODO: there should be some more efficient version of byte strings which avoid copies
static lay::PixelBuffer pixel_buffer_from_png (const std::vector<char> &data)
{
#if defined(HAVE_PNG)
tl::InputMemoryStream data_stream (data.begin ().operator-> (), data.size ());
tl::InputStream stream (data_stream);
return lay::PixelBuffer::read_png (stream);
#elif defined(HAVE_QT) && defined(HAVE_QTBINDINGS)
// QImage is fallback
tl_assert (data.size () < std::numeric_limits<int>::max ());
QImage img = QImage::fromData ((const uchar *) data.begin ().operator-> (), int (data.size ()));
return lay::PixelBuffer::from_image (img);
#else
throw tl::Exception (tl::to_string (tr ("No PNG support compiled in for PixelBuffer")));
return lay::PixelBuffer ();
#endif
}
static void write_pixel_buffer (const lay::PixelBuffer *pb, const std::string &file)
{
#if defined(HAVE_PNG)
tl::OutputStream stream (file);
pb->write_png (stream);
#elif defined(HAVE_QT) && defined(HAVE_QTBINDINGS)
// QImage is fallback
QImage img = pb->to_image ();
img.save (tl::to_qstring (file), "PNG");
#else
throw tl::Exception (tl::to_string (tr ("No PNG support compiled in for PixelBuffer")));
#endif
}
// TODO: there should be some more efficient version of byte strings which avoid copies
static std::vector<char> pixel_buffer_to_png (const lay::PixelBuffer *pb)
{
#if defined(HAVE_PNG)
tl::OutputMemoryStream data_stream;
{
tl::OutputStream stream (data_stream);
pb->write_png (stream);
}
return std::vector<char> (data_stream.data (), data_stream.data () + data_stream.size ());
#elif defined(HAVE_QT) && defined(HAVE_QTBINDINGS)
// QImage is fallback
QImage img = pb->to_image ();
QBuffer data;
img.save (&data, "PNG");
return std::vector<char> (data.data ().constData (), data.data ().constEnd ());
#else
throw tl::Exception (tl::to_string (tr ("No PNG support compiled in for PixelBuffer")));
#endif
}
Class<lay::PixelBuffer> decl_PixelBuffer ("lay", "PixelBuffer",
gsi::constructor ("new", &create_pixel_buffer, gsi::arg ("width"), gsi::arg ("height"),
"@brief Creates a pixel buffer object\n"
"\n"
"@param width The width in pixels\n"
"@param height The height in pixels\n"
"\n"
"The pixels are basically uninitialized. You will need to use \\fill to initialize them to a certain value."
) +
gsi::method ("==", &lay::PixelBuffer::operator==, gsi::arg ("other"),
"@brief Returns a value indicating whether self is identical to the other image\n"
) +
gsi::method ("!=", &lay::PixelBuffer::operator!=, gsi::arg ("other"),
"@brief Returns a value indicating whether self is not identical to the other image\n"
) +
gsi::method ("transparent=", &lay::PixelBuffer::set_transparent, gsi::arg ("t"),
"@brief Sets a flag indicating whether the pixel buffer supports an alpha channel\n"
"\n"
"By default, the pixel buffer does not support an alpha channel.\n"
) +
gsi::method ("transparent", &lay::PixelBuffer::transparent,
"@brief Gets a flag indicating whether the pixel buffer supports an alpha channel\n"
) +
gsi::method ("fill", &lay::PixelBuffer::fill, gsi::arg ("color"),
"@brief Fills the pixel buffer with the given pixel value\n"
) +
#if defined(HAVE_QT) && defined(HAVE_QTBINDINGS)
gsi::method_ext ("fill", &fill_with_qcolor, gsi::arg ("color"),
"@brief Fills the pixel buffer with the given QColor\n"
) +
#endif
gsi::method ("swap", &lay::PixelBuffer::swap, gsi::arg ("other"),
"@brief Swaps data with another PixelBuffer object\n"
) +
gsi::method ("width", &lay::PixelBuffer::width,
"@brief Gets the width of the pixel buffer in pixels\n"
) +
gsi::method ("height", &lay::PixelBuffer::height,
"@brief Gets the height of the pixel buffer in pixels\n"
) +
gsi::method_ext ("set_pixel", &set_pixel_in_pixel_buffer, gsi::arg ("x"), gsi::arg ("y"), gsi::arg ("c"),
"@brief Sets the value of the pixel at position x, y\n"
) +
gsi::method_ext ("pixel", &get_pixel_from_pixel_buffer, gsi::arg ("x"), gsi::arg ("y"),
"@brief Gets the value of the pixel at position x, y\n"
) +
#if defined(HAVE_QT) && defined(HAVE_QTBINDINGS)
gsi::method ("to_qimage", &lay::PixelBuffer::to_image_copy,
"@brief Converts the pixel buffer to a \\QImage object"
) +
gsi::method ("from_qimage", &lay::PixelBuffer::from_image, gsi::arg ("qimage"),
"@brief Creates a pixel buffer object from a QImage object\n"
) +
#endif
gsi::method ("read_png", &read_pixel_buffer, gsi::arg ("file"),
"@brief Reads the pixel buffer from a PNG file"
"\n"
"This method may not be available if PNG support is not compiled into KLayout."
) +
gsi::method ("from_png_data", &pixel_buffer_from_png, gsi::arg ("data"),
"@brief Reads the pixel buffer from a PNG byte stream"
"\n"
"This method may not be available if PNG support is not compiled into KLayout."
) +
gsi::method_ext ("write_png", &write_pixel_buffer, gsi::arg ("file"),
"@brief Writes the pixel buffer to a PNG file"
"\n"
"This method may not be available if PNG support is not compiled into KLayout."
) +
gsi::method_ext ("to_png_data", &pixel_buffer_to_png,
"@brief Converts the pixel buffer to a PNG byte stream"
"\n"
"This method may not be available if PNG support is not compiled into KLayout."
) +
gsi::method ("patch", &lay::PixelBuffer::patch, gsi::arg ("other"),
"@brief Patches another pixel buffer into this one\n"
"\n"
"This method is the inverse of \\diff - it will patch the difference image created by diff into this "
"pixel buffer. Note that this method will not do true alpha blending and requires the other pixel buffer "
"to have the same format than self. Self will be modified by this operation."
) +
gsi::method ("diff", &lay::PixelBuffer::diff, gsi::arg ("other"),
"@brief Creates a difference image\n"
"\n"
"This method is provided to support transfer of image differences - i.e. small updates instead of full images. "
"It works for non-transparent images only and generates an image with transpareny enabled and with the new pixel values for pixels that have changed. "
"The alpha value will be 0 for identical images and 255 for pixels with different values. "
"This way, the difference image can be painted over the original image to generate the new image."
),
"@brief A simplistic pixel buffer representing an image of ARGB32 or RGB32 values\n"
"\n"
"This object is mainly provided for offline rendering of layouts in Qt-less environments.\n"
"It supports a rectangular pixel space with color values encoded in 32bit integers. It supports "
"transparency through an optional alpha channel. The color format for a pixel is "
"\"0xAARRGGBB\" where 'AA' is the alpha value which is ignored in non-transparent mode.\n"
"\n"
"This class supports basic operations such as initialization, single-pixel access and I/O to PNG.\n"
"\n"
"This class has been introduced in version 0.28."
);
// -------------------------------------------------------------------------------------
// lay::BitmapBuffer
static lay::BitmapBuffer *create_bitmap_buffer (unsigned int w, unsigned int h)
{
return new lay::BitmapBuffer (w, h);
}
bool get_pixel_from_bitmap_buffer (const lay::BitmapBuffer *pb, unsigned int x, unsigned int y)
{
if (x < pb->width () && y < pb->height ()) {
return (pb->scan_line (y)[x / 8] & (0x01 << (x % 8))) != 0;
} else {
return false;
}
}
void set_pixel_in_bitmap_buffer (lay::BitmapBuffer *pb, unsigned int x, unsigned int y, bool c)
{
if (x < pb->width () && y < pb->height ()) {
if (c) {
pb->scan_line (y)[x / 8] |= 0x01 << (x % 8);
} else {
pb->scan_line (y)[x / 8] &= ~(0x01 << (x % 8));
}
}
}
static lay::BitmapBuffer read_bitmap_buffer (const std::string &file)
{
#if defined(HAVE_PNG)
tl::InputStream stream (file);
return lay::BitmapBuffer::read_png (stream);
#elif defined(HAVE_QT) && defined(HAVE_QTBINDINGS)
// QImage is fallback
QImage img;
img.load (tl::to_qstring (file), "PNG");
return lay::BitmapBuffer::from_image (img);
#else
throw tl::Exception (tl::to_string (tr ("No PNG support compiled in for BitmapBuffer")));
return lay::BitmapBuffer ();
#endif
}
// TODO: there should be some more efficient version of byte strings which avoid copies
static lay::BitmapBuffer bitmap_buffer_from_png (const std::vector<char> &data)
{
#if defined(HAVE_PNG)
tl::InputMemoryStream data_stream (data.begin ().operator-> (), data.size ());
tl::InputStream stream (data_stream);
return lay::BitmapBuffer::read_png (stream);
#elif defined(HAVE_QT) && defined(HAVE_QTBINDINGS)
// QImage is fallback
tl_assert (data.size () < std::numeric_limits<int>::max ());
QImage img = QImage::fromData ((const uchar *) data.begin ().operator-> (), int (data.size ()));
return lay::BitmapBuffer::from_image (img);
#else
throw tl::Exception (tl::to_string (tr ("No PNG support compiled in for BitmapBuffer")));
return lay::BitmapBuffer ();
#endif
}
static void write_bitmap_buffer (const lay::BitmapBuffer *pb, const std::string &file)
{
#if defined(HAVE_PNG)
tl::OutputStream stream (file);
pb->write_png (stream);
#elif defined(HAVE_QT) && defined(HAVE_QTBINDINGS)
// QImage is fallback
QImage img = pb->to_image ();
img.save (tl::to_qstring (file), "PNG");
#else
throw tl::Exception (tl::to_string (tr ("No PNG support compiled in for BitmapBuffer")));
#endif
}
// TODO: there should be some more efficient version of byte strings which avoid copies
static std::vector<char> bitmap_buffer_to_png (const lay::BitmapBuffer *pb)
{
#if defined(HAVE_PNG)
tl::OutputMemoryStream data_stream;
{
tl::OutputStream stream (data_stream);
pb->write_png (stream);
}
return std::vector<char> (data_stream.data (), data_stream.data () + data_stream.size ());
#elif defined(HAVE_QT) && defined(HAVE_QTBINDINGS)
// QImage is fallback
QImage img = pb->to_image ();
QBuffer data;
img.save (&data, "PNG");
return std::vector<char> (data.data ().constData (), data.data ().constEnd ());
#else
throw tl::Exception (tl::to_string (tr ("No PNG support compiled in for BitmapBuffer")));
#endif
}
Class<lay::BitmapBuffer> decl_BitmapBuffer ("lay", "BitmapBuffer",
gsi::constructor ("new", &create_bitmap_buffer, gsi::arg ("width"), gsi::arg ("height"),
"@brief Creates a pixel buffer object\n"
"\n"
"@param width The width in pixels\n"
"@param height The height in pixels\n"
"\n"
"The pixels are basically uninitialized. You will need to use \\fill to initialize them to a certain value."
) +
gsi::method ("==", &lay::BitmapBuffer::operator==, gsi::arg ("other"),
"@brief Returns a value indicating whether self is identical to the other image\n"
) +
gsi::method ("!=", &lay::BitmapBuffer::operator!=, gsi::arg ("other"),
"@brief Returns a value indicating whether self is not identical to the other image\n"
) +
gsi::method ("fill", &lay::BitmapBuffer::fill, gsi::arg ("color"),
"@brief Fills the pixel buffer with the given pixel value\n"
) +
#if defined(HAVE_QT) && defined(HAVE_QTBINDINGS)
gsi::method_ext ("fill", &fill_with_qcolor, gsi::arg ("color"),
"@brief Fills the pixel buffer with the given QColor\n"
) +
#endif
gsi::method ("swap", &lay::BitmapBuffer::swap, gsi::arg ("other"),
"@brief Swaps data with another BitmapBuffer object\n"
) +
gsi::method ("width", &lay::BitmapBuffer::width,
"@brief Gets the width of the pixel buffer in pixels\n"
) +
gsi::method ("height", &lay::BitmapBuffer::height,
"@brief Gets the height of the pixel buffer in pixels\n"
) +
gsi::method_ext ("set_pixel", &set_pixel_in_bitmap_buffer, gsi::arg ("x"), gsi::arg ("y"), gsi::arg ("c"),
"@brief Sets the value of the pixel at position x, y\n"
) +
gsi::method_ext ("pixel", &get_pixel_from_bitmap_buffer, gsi::arg ("x"), gsi::arg ("y"),
"@brief Gets the value of the pixel at position x, y\n"
) +
#if defined(HAVE_QT) && defined(HAVE_QTBINDINGS)
gsi::method ("to_qimage", &lay::BitmapBuffer::to_image_copy,
"@brief Converts the pixel buffer to a \\QImage object"
) +
gsi::method ("from_qimage", &lay::BitmapBuffer::from_image, gsi::arg ("qimage"),
"@brief Creates a pixel buffer object from a QImage object\n"
) +
#endif
gsi::method ("read_png", &read_bitmap_buffer, gsi::arg ("file"),
"@brief Reads the pixel buffer from a PNG file"
"\n"
"This method may not be available if PNG support is not compiled into KLayout."
) +
gsi::method ("from_png_data", &bitmap_buffer_from_png, gsi::arg ("data"),
"@brief Reads the pixel buffer from a PNG byte stream"
"\n"
"This method may not be available if PNG support is not compiled into KLayout."
) +
gsi::method_ext ("write_png", &write_bitmap_buffer, gsi::arg ("file"),
"@brief Writes the pixel buffer to a PNG file"
"\n"
"This method may not be available if PNG support is not compiled into KLayout."
) +
gsi::method_ext ("to_png_data", &bitmap_buffer_to_png,
"@brief Converts the pixel buffer to a PNG byte stream"
"\n"
"This method may not be available if PNG support is not compiled into KLayout."
),
"@brief A simplistic pixel buffer representing monochrome image\n"
"\n"
"This object is mainly provided for offline rendering of layouts in Qt-less environments.\n"
"It supports a rectangular pixel space with color values encoded in single bits.\n"
"\n"
"This class supports basic operations such as initialization, single-pixel access and I/O to PNG.\n"
"\n"
"This class has been introduced in version 0.28."
);
}
+3 -3
View File
@@ -25,7 +25,7 @@
#include "layBitmap.h"
#include "layDitherPattern.h"
#include "layLineStyles.h"
#include "layPixelBuffer.h"
#include "tlPixelBuffer.h"
#include "tlTimer.h"
#include "tlAssert.h"
#include "tlThreads.h"
@@ -426,7 +426,7 @@ bitmaps_to_image (const std::vector<lay::ViewOp> &view_ops_in,
const std::vector<lay::Bitmap *> &pbitmaps_in,
const lay::DitherPattern &dp,
const lay::LineStyles &ls,
PixelBuffer *pimage, unsigned int width, unsigned int height,
tl::PixelBuffer *pimage, unsigned int width, unsigned int height,
bool use_bitmap_index,
tl::Mutex *mutex)
{
@@ -666,7 +666,7 @@ bitmaps_to_image (const std::vector<lay::ViewOp> &view_ops_in,
const std::vector<lay::Bitmap *> &pbitmaps_in,
const lay::DitherPattern &dp,
const lay::LineStyles &ls,
lay::BitmapBuffer *pimage, unsigned int width, unsigned int height,
tl::BitmapBuffer *pimage, unsigned int width, unsigned int height,
bool use_bitmap_index,
tl::Mutex *mutex)
{
+9 -5
View File
@@ -29,14 +29,18 @@
#include <vector>
namespace tl
{
class PixelBuffer;
class BitmapBuffer;
}
namespace lay
{
class DitherPattern;
class LineStyles;
class Bitmap;
class PixelBuffer;
class BitmapBuffer;
/**
* @brief This function converts the given set of bitmaps to a PixelBuffer
@@ -58,7 +62,7 @@ bitmaps_to_image (const std::vector <lay::ViewOp> &view_ops,
const std::vector <lay::Bitmap *> &pbitmaps,
const lay::DitherPattern &dp,
const lay::LineStyles &ls,
lay::PixelBuffer *pimage, unsigned int width, unsigned int height,
tl::PixelBuffer *pimage, unsigned int width, unsigned int height,
bool use_bitmap_index,
tl::Mutex *mutex);
@@ -72,12 +76,12 @@ bitmaps_to_image (const std::vector <lay::ViewOp> &view_ops,
const std::vector <lay::Bitmap *> &pbitmaps,
const lay::DitherPattern &dp,
const lay::LineStyles &ls,
lay::BitmapBuffer *pimage, unsigned int width, unsigned int height,
tl::BitmapBuffer *pimage, unsigned int width, unsigned int height,
bool use_bitmap_index,
tl::Mutex *mutex);
/**
* @brief Convert a lay::Bitmap to a unsigned char * data field to be passed to lay::BitmapBuffer
* @brief Convert a lay::Bitmap to a unsigned char * data field to be passed to tl::BitmapBuffer
*
* This function converts the bitmap given the view_op into a raw byte data
* field that can be passed to a QBitmap constructor. The data field is not
+22 -22
View File
@@ -132,7 +132,7 @@ std::string ImageCacheEntry::to_string () const
// ----------------------------------------------------------------------------
static void
blowup (const lay::PixelBuffer &src, lay::PixelBuffer &dest, unsigned int os)
blowup (const tl::PixelBuffer &src, tl::PixelBuffer &dest, unsigned int os)
{
unsigned int ymax = src.height ();
unsigned int xmax = src.width ();
@@ -152,7 +152,7 @@ blowup (const lay::PixelBuffer &src, lay::PixelBuffer &dest, unsigned int os)
}
static void
subsample (const lay::PixelBuffer &src, lay::PixelBuffer &dest, unsigned int os, double g)
subsample (const tl::PixelBuffer &src, tl::PixelBuffer &dest, unsigned int os, double g)
{
// TODO: this is probably not compatible with the endianess of SPARC ..
@@ -448,7 +448,7 @@ LayoutCanvas::prepare_drawing ()
if (mp_image) {
delete mp_image;
}
mp_image = new lay::PixelBuffer (m_viewport_l.width (), m_viewport_l.height ());
mp_image = new tl::PixelBuffer (m_viewport_l.width (), m_viewport_l.height ());
if (mp_image_fg) {
delete mp_image_fg;
mp_image_fg = 0;
@@ -589,7 +589,7 @@ LayoutCanvas::paint_event ()
if (mp_image_bg) {
delete mp_image_bg;
}
mp_image_bg = new lay::PixelBuffer (*mp_image);
mp_image_bg = new tl::PixelBuffer (*mp_image);
} else {
// else reuse the saved image
@@ -622,18 +622,18 @@ LayoutCanvas::paint_event ()
clear_fg_bitmaps ();
do_render (m_viewport_l, *this, true);
mp_image_fg = new lay::PixelBuffer ();
mp_image_fg = new tl::PixelBuffer ();
if (fg_bitmaps () > 0) {
lay::PixelBuffer full_image (*mp_image);
tl::PixelBuffer full_image (*mp_image);
bitmaps_to_image (fg_view_op_vector (), fg_bitmap_vector (), dither_pattern (), line_styles (), &full_image, m_viewport_l.width (), m_viewport_l.height (), false, &m_mutex);
// render the foreground parts ..
if (m_oversampling == 1) {
*mp_image_fg = full_image;
} else {
lay::PixelBuffer subsampled_image (m_viewport.width (), m_viewport.height ());
tl::PixelBuffer subsampled_image (m_viewport.width (), m_viewport.height ());
subsampled_image.set_transparent (mp_image->transparent ());
subsample (full_image, subsampled_image, m_oversampling, m_gamma);
*mp_image_fg = subsampled_image;
@@ -645,7 +645,7 @@ LayoutCanvas::paint_event ()
} else {
lay::PixelBuffer subsampled_image (m_viewport.width (), m_viewport.height ());
tl::PixelBuffer subsampled_image (m_viewport.width (), m_viewport.height ());
subsampled_image.set_transparent (mp_image->transparent ());
subsample (*mp_image, subsampled_image, m_oversampling, m_gamma);
*mp_image_fg = subsampled_image;
@@ -670,7 +670,7 @@ LayoutCanvas::paint_event ()
if (fg_bitmaps () > 0) {
lay::PixelBuffer full_image (mp_image->width (), mp_image->height ());
tl::PixelBuffer full_image (mp_image->width (), mp_image->height ());
full_image.set_transparent (true);
full_image.fill (0);
@@ -684,7 +684,7 @@ LayoutCanvas::paint_event ()
#endif
painter.drawImage (QPoint (0, 0), img);
} else {
lay::PixelBuffer subsampled_image (m_viewport.width (), m_viewport.height ());
tl::PixelBuffer subsampled_image (m_viewport.width (), m_viewport.height ());
subsampled_image.set_transparent (true);
subsample (full_image, subsampled_image, m_oversampling, m_gamma);
QImage img = subsampled_image.to_image ();
@@ -712,7 +712,7 @@ class DetachedViewObjectCanvas
: public BitmapViewObjectCanvas
{
public:
DetachedViewObjectCanvas (tl::Color bg, tl::Color fg, tl::Color ac, unsigned int width_l, unsigned int height_l, double resolution, lay::PixelBuffer *img)
DetachedViewObjectCanvas (tl::Color bg, tl::Color fg, tl::Color ac, unsigned int width_l, unsigned int height_l, double resolution, tl::PixelBuffer *img)
: BitmapViewObjectCanvas (width_l, height_l, resolution),
m_bg (bg), m_fg (fg), m_ac (ac), mp_image (img)
{
@@ -720,7 +720,7 @@ public:
m_gamma = 2.0;
if (img->width () != width_l || img->height () != height_l) {
mp_image_l = new lay::PixelBuffer (width_l, height_l);
mp_image_l = new tl::PixelBuffer (width_l, height_l);
mp_image_l->set_transparent (img->transparent ());
mp_image_l->fill (bg.rgb ());
} else {
@@ -753,7 +753,7 @@ public:
return m_ac;
}
virtual lay::PixelBuffer *bg_image ()
virtual tl::PixelBuffer *bg_image ()
{
return mp_image_l ? mp_image_l : mp_image;
}
@@ -781,8 +781,8 @@ public:
private:
tl::Color m_bg, m_fg, m_ac;
lay::PixelBuffer *mp_image;
lay::PixelBuffer *mp_image_l;
tl::PixelBuffer *mp_image;
tl::PixelBuffer *mp_image_l;
double m_gamma;
};
@@ -828,13 +828,13 @@ private:
bool m_bg, m_fg, m_ac;
};
lay::PixelBuffer
tl::PixelBuffer
LayoutCanvas::image (unsigned int width, unsigned int height)
{
return image_with_options (width, height, -1, -1, -1.0, tl::Color (), tl::Color (), tl::Color (), db::DBox ());
}
lay::PixelBuffer
tl::PixelBuffer
LayoutCanvas::image_with_options (unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution, tl::Color background, tl::Color foreground, tl::Color active, const db::DBox &target_box)
{
if (oversampling <= 0) {
@@ -857,7 +857,7 @@ LayoutCanvas::image_with_options (unsigned int width, unsigned int height, int l
}
// TODO: for other architectures MonoLSB may not be the right format
lay::PixelBuffer img (width, height);
tl::PixelBuffer img (width, height);
// this may happen for BIG images:
if (img.width () != width || img.height () != height) {
@@ -910,7 +910,7 @@ LayoutCanvas::image_with_options (unsigned int width, unsigned int height, int l
return img;
}
lay::BitmapBuffer
tl::BitmapBuffer
LayoutCanvas::image_with_options_mono (unsigned int width, unsigned int height, int linewidth, tl::Color background_c, tl::Color foreground_c, tl::Color active_c, const db::DBox &target_box)
{
if (linewidth <= 0) {
@@ -946,7 +946,7 @@ LayoutCanvas::image_with_options_mono (unsigned int width, unsigned int height,
redraw_thread.start (0 /*synchronous*/, m_layers, vp, 1.0, true);
redraw_thread.stop (); // safety
lay::BitmapBuffer img (width, height);
tl::BitmapBuffer img (width, height);
img.fill (background);
rd_canvas.to_image_mono (view_ops, dither_pattern (), line_styles (), background, foreground, active, this, img, vp.width (), vp.height ());
@@ -954,13 +954,13 @@ LayoutCanvas::image_with_options_mono (unsigned int width, unsigned int height,
return img;
}
lay::PixelBuffer
tl::PixelBuffer
LayoutCanvas::screenshot ()
{
// if required, start the redraw thread ..
prepare_drawing ();
lay::PixelBuffer img (m_viewport.width (), m_viewport.height ());
tl::PixelBuffer img (m_viewport.width (), m_viewport.height ());
img.fill (m_background);
DetachedViewObjectCanvas vo_canvas (background_color (), foreground_color (), active_color (), m_viewport_l.width (), m_viewport_l.height (), 1.0 / double (m_oversampling * m_dpr), &img);
+8 -8
View File
@@ -164,10 +164,10 @@ public:
return m_view_ops;
}
lay::PixelBuffer screenshot ();
lay::PixelBuffer image (unsigned int width, unsigned int height);
lay::PixelBuffer image_with_options (unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution, tl::Color background, tl::Color foreground, tl::Color active_color, const db::DBox &target_box);
lay::BitmapBuffer image_with_options_mono (unsigned int width, unsigned int height, int linewidth, tl::Color background, tl::Color foreground, tl::Color active, const db::DBox &target_box);
tl::PixelBuffer screenshot ();
tl::PixelBuffer image (unsigned int width, unsigned int height);
tl::PixelBuffer image_with_options (unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution, tl::Color background, tl::Color foreground, tl::Color active_color, const db::DBox &target_box);
tl::BitmapBuffer image_with_options_mono (unsigned int width, unsigned int height, int linewidth, tl::Color background, tl::Color foreground, tl::Color active, const db::DBox &target_box);
void update_image ();
@@ -312,7 +312,7 @@ public:
/**
* @brief Reimplementation of ViewObjectCanvas: background image
*/
lay::PixelBuffer *bg_image ()
tl::PixelBuffer *bg_image ()
{
return mp_image;
}
@@ -358,9 +358,9 @@ public:
private:
lay::LayoutViewBase *mp_view;
lay::PixelBuffer *mp_image;
lay::PixelBuffer *mp_image_bg;
lay::PixelBuffer *mp_image_fg;
tl::PixelBuffer *mp_image;
tl::PixelBuffer *mp_image_bg;
tl::PixelBuffer *mp_image_fg;
db::DBox m_precious_box;
lay::Viewport m_viewport, m_viewport_l;
tl::color_t m_background;
+8 -8
View File
@@ -2534,7 +2534,7 @@ LayoutViewBase::get_screenshot ()
}
#endif
lay::PixelBuffer
tl::PixelBuffer
LayoutViewBase::get_screenshot_pb ()
{
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Save screenshot")));
@@ -2597,7 +2597,7 @@ LayoutViewBase::save_screenshot (const std::string &fn)
tl::DeferredMethodScheduler::execute ();
tl::OutputStream stream (fn);
lay::PixelBuffer img = mp_canvas->screenshot ();
tl::PixelBuffer img = mp_canvas->screenshot ();
img.set_texts (png_texts (this, box ()));
img.write_png (stream);
@@ -2624,7 +2624,7 @@ LayoutViewBase::get_image (unsigned int width, unsigned int height)
}
#endif
lay::PixelBuffer
tl::PixelBuffer
LayoutViewBase::get_pixels (unsigned int width, unsigned int height)
{
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Get image")));
@@ -2653,7 +2653,7 @@ LayoutViewBase::get_image_with_options (unsigned int width, unsigned int height,
}
#endif
lay::PixelBuffer
tl::PixelBuffer
LayoutViewBase::get_pixels_with_options (unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution,
tl::Color background, tl::Color foreground, tl::Color active, const db::DBox &target_box)
{
@@ -2665,7 +2665,7 @@ LayoutViewBase::get_pixels_with_options (unsigned int width, unsigned int height
return mp_canvas->image_with_options (width, height, linewidth, oversampling, resolution, background, foreground, active, target_box);
}
lay::BitmapBuffer
tl::BitmapBuffer
LayoutViewBase::get_pixels_with_options_mono (unsigned int width, unsigned int height, int linewidth,
tl::Color background, tl::Color foreground, tl::Color active, const db::DBox &target_box)
{
@@ -2712,7 +2712,7 @@ LayoutViewBase::save_image (const std::string &fn, unsigned int width, unsigned
tl::DeferredMethodScheduler::execute ();
tl::OutputStream stream (fn);
lay::PixelBuffer img = mp_canvas->image (width, height);
tl::PixelBuffer img = mp_canvas->image (width, height);
std::vector<std::pair<std::string, std::string> > texts = png_texts (this, vp.box ());
img.set_texts (texts);
img.write_png (stream);
@@ -2775,13 +2775,13 @@ LayoutViewBase::save_image_with_options (const std::string &fn,
tl::OutputStream stream (fn);
if (monochrome) {
lay::BitmapBuffer img = mp_canvas->image_with_options_mono (width, height, linewidth, background, foreground, active, target_box);
tl::BitmapBuffer img = mp_canvas->image_with_options_mono (width, height, linewidth, background, foreground, active, target_box);
img.set_texts (texts);
img.write_png (stream);
} else {
lay::PixelBuffer img = mp_canvas->image_with_options (width, height, linewidth, oversampling, resolution, background, foreground, active, target_box);
tl::PixelBuffer img = mp_canvas->image_with_options (width, height, linewidth, oversampling, resolution, background, foreground, active, target_box);
img.set_texts (texts);
img.write_png (stream);
+8 -8
View File
@@ -837,9 +837,9 @@ public:
#endif
/**
* @brief Gets the screen content as a lay::PixelBuffer object
* @brief Gets the screen content as a tl::PixelBuffer object
*/
lay::PixelBuffer get_screenshot_pb ();
tl::PixelBuffer get_screenshot_pb ();
/**
* @brief Save an image file with the given width and height
@@ -871,9 +871,9 @@ public:
#endif
/**
* @brief Gets the screen content as a lay::PixelBuffer object
* @brief Gets the screen content as a tl::PixelBuffer object
*/
lay::PixelBuffer get_pixels (unsigned int width, unsigned int height);
tl::PixelBuffer get_pixels (unsigned int width, unsigned int height);
#if defined(HAVE_QT)
/**
@@ -894,7 +894,7 @@ public:
#endif
/**
* @brief Get the screen content as a lay::PixelBuffer object with the given width and height
* @brief Get the screen content as a tl::PixelBuffer object with the given width and height
*
* @param width The width of the image in pixels
* @param height The height of the image
@@ -906,10 +906,10 @@ public:
* @param active The active color or tl::Color() for default
* @param target_box The box to draw or db::DBox() for default
*/
lay::PixelBuffer get_pixels_with_options (unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution, tl::Color background, tl::Color foreground, tl::Color active_color, const db::DBox &target_box);
tl::PixelBuffer get_pixels_with_options (unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution, tl::Color background, tl::Color foreground, tl::Color active_color, const db::DBox &target_box);
/**
* @brief Get the screen content as a monochrome lay::BitmapBuffer object with the given options
* @brief Get the screen content as a monochrome tl::BitmapBuffer object with the given options
*
* @param width The width of the image in pixels
* @param height The height of the image
@@ -923,7 +923,7 @@ public:
*
* The colors will are converted to "on" pixels with a green channel value >= 50%.
*/
lay::BitmapBuffer get_pixels_with_options_mono (unsigned int width, unsigned int height, int linewidth, tl::Color background, tl::Color foreground, tl::Color active_color, const db::DBox &target_box);
tl::BitmapBuffer get_pixels_with_options_mono (unsigned int width, unsigned int height, int linewidth, tl::Color background, tl::Color foreground, tl::Color active_color, const db::DBox &target_box);
/**
* @brief Hierarchy level selection setter
-820
View File
@@ -1,820 +0,0 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2022 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "layPixelBuffer.h"
#include "tlAssert.h"
#include "tlLog.h"
#if defined(HAVE_PNG)
# include <png.h>
#endif
#include <memory>
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
PixelBuffer::PixelBuffer (unsigned int w, unsigned int h, tl::color_t *data)
: m_data ()
{
m_width = w;
m_height = h;
m_transparent = false;
m_data.reset (new ImageData (data, w * h));
}
PixelBuffer::PixelBuffer (unsigned int w, unsigned int h, const tl::color_t *data, unsigned int stride)
: m_data ()
{
m_width = w;
m_height = h;
m_transparent = false;
tl_assert ((stride % sizeof (tl::color_t)) == 0);
stride /= sizeof (tl::color_t);
tl::color_t *d = new tl::color_t [w * h];
tl::color_t *new_data = d;
if (data) {
for (unsigned int i = 0; i < h; ++i) {
for (unsigned int j = 0; j < w; ++j) {
*d++ = *data++;
}
if (stride > w) {
data += stride - w;
}
}
}
m_data.reset (new ImageData (new_data, w * h));
}
PixelBuffer::PixelBuffer ()
{
m_width = 0;
m_height = 0;
m_transparent = false;
}
PixelBuffer::PixelBuffer (const PixelBuffer &other)
{
operator= (other);
}
PixelBuffer::PixelBuffer (PixelBuffer &&other)
{
swap (other);
}
PixelBuffer::~PixelBuffer ()
{
// .. nothing yet ..
}
bool
PixelBuffer::operator== (const PixelBuffer &other) const
{
if (width () != other.width () || height () != other.height ()) {
return false;
}
if (transparent () != other.transparent ()) {
return false;
}
tl::color_t m = transparent () ? 0xffffffff : 0xffffff;
for (unsigned int i = 0; i < other.height (); ++i) {
const tl::color_t *d = scan_line (i);
const tl::color_t *de = d + width ();
const tl::color_t *dd = other.scan_line (i);
while (d != de) {
if (((*d++ ^ *dd++) & m) != 0) {
return false;
}
}
}
return true;
}
PixelBuffer &
PixelBuffer::operator= (const PixelBuffer &other)
{
if (this != &other) {
m_width = other.m_width;
m_height = other.m_height;
m_data = other.m_data;
m_transparent = other.m_transparent;
m_texts = other.m_texts;
}
return *this;
}
PixelBuffer &
PixelBuffer::operator= (PixelBuffer &&other)
{
if (this != &other) {
swap (other);
}
return *this;
}
void
PixelBuffer::set_transparent (bool f)
{
m_transparent = f;
}
void
PixelBuffer::swap (PixelBuffer &other)
{
if (this == &other) {
return;
}
std::swap (m_width, other.m_width);
std::swap (m_height, other.m_height);
std::swap (m_transparent, other.m_transparent);
m_data.swap (other.m_data);
m_texts.swap (other.m_texts);
}
void
PixelBuffer::fill (tl::color_t c)
{
if (! transparent ()) {
c |= 0xff000000; // ensures that alpha is properly set
}
tl::color_t *d = data ();
for (unsigned int i = 0; i < m_height; ++i) {
for (unsigned int j = 0; j < m_width; ++j) {
*d++ = c;
}
}
}
tl::color_t *
PixelBuffer::scan_line (unsigned int n)
{
tl_assert (n < m_height);
return m_data->data () + n * m_width;
}
const tl::color_t *
PixelBuffer::scan_line (unsigned int n) const
{
tl_assert (n < m_height);
return m_data->data () + n * m_width;
}
tl::color_t *
PixelBuffer::data ()
{
return m_data->data ();
}
const tl::color_t *
PixelBuffer::data () const
{
return m_data->data ();
}
#if defined(HAVE_QT)
QImage
PixelBuffer::to_image () const
{
return QImage ((const uchar *) data (), m_width, m_height, m_transparent ? QImage::Format_ARGB32 : QImage::Format_RGB32);
}
QImage
PixelBuffer::to_image_copy () const
{
QImage img (m_width, m_height, m_transparent ? QImage::Format_ARGB32 : QImage::Format_RGB32);
#if QT_VERSION < 0x051000
memcpy (img.bits (), data (), img.byteCount ());
#else
memcpy (img.bits (), data (), img.sizeInBytes ());
#endif
return img;
}
PixelBuffer
PixelBuffer::from_image (const QImage &img)
{
if (img.format () != QImage::Format_ARGB32 && img.format () != QImage::Format_RGB32) {
QImage img_argb32 = img.convertToFormat (QImage::Format_ARGB32);
return PixelBuffer (img_argb32.width (), img_argb32.height (), (const tl::color_t *) img_argb32.bits ());
} else {
return PixelBuffer (img.width (), img.height (), (const tl::color_t *) img.bits ());
}
}
#endif
void
PixelBuffer::patch (const PixelBuffer &other)
{
tl_assert (width () == other.width ());
tl_assert (height () == other.height ());
tl_assert (other.transparent ());
const tl::color_t *d = other.data ();
tl::color_t *dd = data ();
for (unsigned int i = 0; i < m_height; ++i) {
for (unsigned int j = 0; j < m_width; ++j) {
tl::color_t c = *d++;
if ((c & 0x80000000) != 0) {
*dd = c;
}
++dd;
}
}
}
PixelBuffer
PixelBuffer::diff (const PixelBuffer &other) const
{
tl_assert (width () == other.width ());
tl_assert (height () == other.height ());
PixelBuffer res (m_width, m_height);
res.set_transparent (true);
const tl::color_t *d2 = other.data ();
const tl::color_t *d1 = data ();
tl::color_t *dd = res.data ();
for (unsigned int i = 0; i < m_height; ++i) {
for (unsigned int j = 0; j < m_width; ++j) {
if (((*d1 ^ *d2) & 0xffffff) != 0) {
*dd++ = *d2 | 0xff000000;
} else {
*dd++ = 0;
}
++d1;
++d2;
}
}
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 tl::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 (tl::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 (tl::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
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) {
tl::color_t *c = res.scan_line (i);
const uint8_t *d = row_pointers [i];
const uint8_t *dd = d + rb;
while (d < dd) {
tl::color_t b = *d++;
tl::color_t g = *d++;
tl::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) {
tl::color_t *c = res.scan_line (i);
const uint8_t *d = row_pointers [i];
const uint8_t *dd = d + rb;
while (d < dd) {
tl::color_t g = *d++;
tl::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) {
tl::color_t *c = res.scan_line (i);
const uint8_t *d = row_pointers [i];
const uint8_t *dd = d + rb;
while (d < dd) {
tl::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 8 bit G, GA, RGB or RGBA 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
PixelBuffer::write_png (tl::OutputStream &output) const
{
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 tl::color_t
unsigned int bd = 8; // bit depth
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);
std::vector<png_text> tptrs;
for (auto i = m_texts.begin (); i != m_texts.end (); ++i) {
tptrs.push_back (png_text ());
tptrs.back ().compression = PNG_TEXT_COMPRESSION_NONE;
tptrs.back ().key = const_cast<char *> (i->first.c_str ());
tptrs.back ().text = const_cast<char *> (i->second.c_str ());
}
png_set_text (png_ptr, info_ptr, tptrs.begin ().operator-> (), m_texts.size ());
png_write_info (png_ptr, info_ptr);
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<uint8_t []> buffer (new uint8_t [width () * 3]);
for (unsigned int i = 0; i < height (); ++i) {
uint8_t *d = buffer.get ();
const tl::color_t *s = scan_line (i);
const tl::color_t *se = s + width ();
while (s != se) {
tl::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);
png_destroy_write_struct(&png_ptr, &info_ptr);
}
#endif
// -----------------------------------------------------------------------------------------------------
// BitmapBuffer implementation
static unsigned int
stride_from_width (unsigned int w)
{
// Qt needs 32bit-aligned data
return 4 * ((w + 31) / 32);
}
BitmapBuffer::BitmapBuffer (unsigned int w, unsigned int h, uint8_t *data)
{
m_width = w;
m_height = h;
m_stride = stride_from_width (w);
m_data.reset (new MonoImageData (data, m_stride * h));
}
BitmapBuffer::BitmapBuffer (unsigned int w, unsigned int h, const uint8_t *data, unsigned int stride)
{
m_width = w;
m_height = h;
m_stride = stride_from_width (w);
uint8_t *d = new uint8_t [m_stride * h];
uint8_t *new_data = d;
if (data) {
for (unsigned int i = 0; i < h; ++i) {
memcpy (d, data, m_stride);
d += m_stride;
data += m_stride;
if (stride > m_stride) {
data += stride - m_stride;
}
}
}
m_data.reset (new MonoImageData (new_data, m_stride * h));
}
BitmapBuffer::BitmapBuffer ()
{
m_width = 0;
m_height = 0;
m_stride = 0;
}
BitmapBuffer::BitmapBuffer (const BitmapBuffer &other)
{
operator= (other);
}
BitmapBuffer::BitmapBuffer (BitmapBuffer &&other)
{
swap (other);
}
BitmapBuffer::~BitmapBuffer ()
{
// .. nothing yet ..
}
bool
BitmapBuffer::operator== (const BitmapBuffer &other) const
{
if (width () != other.width () || height () != other.height ()) {
return false;
}
for (unsigned int i = 0; i < other.height (); ++i) {
const uint8_t *d = scan_line (i);
const uint8_t *dd = other.scan_line (i);
unsigned int bits_left = width ();
while (bits_left >= 8) {
if (*d++ != *dd++) {
return false;
}
bits_left -= 8;
}
if (bits_left > 0) {
unsigned int m = (0x01 << bits_left) - 1;
if (((*d ^ *dd) & m) != 0) {
return false;
}
}
}
return true;
}
BitmapBuffer &
BitmapBuffer::operator= (const BitmapBuffer &other)
{
if (this != &other) {
m_width = other.m_width;
m_height = other.m_height;
m_stride = other.m_stride;
m_data = other.m_data;
m_texts = other.m_texts;
}
return *this;
}
BitmapBuffer &
BitmapBuffer::operator= (BitmapBuffer &&other)
{
if (this != &other) {
swap (other);
}
return *this;
}
void
BitmapBuffer::swap (BitmapBuffer &other)
{
if (this == &other) {
return;
}
std::swap (m_width, other.m_width);
std::swap (m_height, other.m_height);
std::swap (m_stride, other.m_stride);
m_data.swap (other.m_data);
m_texts.swap (other.m_texts);
}
void
BitmapBuffer::fill (bool value)
{
uint8_t c = value ? 0xff : 0;
uint8_t *d = data ();
for (unsigned int i = 0; i < m_height; ++i) {
for (unsigned int j = 0; j < m_stride; ++j) {
*d++ = c;
}
}
}
uint8_t *
BitmapBuffer::scan_line (unsigned int n)
{
tl_assert (n < m_height);
return m_data->data () + n * m_stride;
}
const uint8_t *
BitmapBuffer::scan_line (unsigned int n) const
{
tl_assert (n < m_height);
return m_data->data () + n * m_stride;
}
uint8_t *
BitmapBuffer::data ()
{
return m_data->data ();
}
const uint8_t *
BitmapBuffer::data () const
{
return m_data->data ();
}
#if defined(HAVE_QT)
QImage
BitmapBuffer::to_image () const
{
QImage img = QImage ((const uchar *) data (), m_width, m_height, QImage::Format_MonoLSB);
img.setColor (0, 0xff000000);
img.setColor (1, 0xffffffff);
return img;
}
QImage
BitmapBuffer::to_image_copy () const
{
QImage img (m_width, m_height, QImage::Format_MonoLSB);
#if QT_VERSION < 0x051000
memcpy (img.bits (), data (), img.byteCount ());
#else
memcpy (img.bits (), data (), img.sizeInBytes ());
#endif
return img;
}
BitmapBuffer
BitmapBuffer::from_image (const QImage &img)
{
if (img.format () != QImage::Format_MonoLSB) {
QImage img_monolsb = img.convertToFormat (QImage::Format_MonoLSB);
return BitmapBuffer (img_monolsb.width (), img_monolsb.height (), (const uint8_t *) img_monolsb.bits ());
} else {
return BitmapBuffer (img.width (), img.height (), (const uint8_t *) img.bits ());
}
}
#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_set_packswap (png_ptr); // compatible with BitmapBuffer
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 || fmt == PNG_COLOR_TYPE_PALETTE) && bd == 1) {
// TODO: evaluate palette?
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) const
{
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_packswap (png_ptr); // compatible with BitmapBuffer
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);
std::vector<png_text> tptrs;
for (auto i = m_texts.begin (); i != m_texts.end (); ++i) {
tptrs.push_back (png_text ());
tptrs.back ().compression = PNG_TEXT_COMPRESSION_NONE;
tptrs.back ().key = const_cast<char *> (i->first.c_str ());
tptrs.back ().text = const_cast<char *> (i->second.c_str ());
}
png_set_text (png_ptr, info_ptr, tptrs.begin ().operator-> (), m_texts.size ());
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
}
-557
View File
@@ -1,557 +0,0 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2022 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_layPixelBuffer
#define HDR_layPixelBuffer
#include "laybasicCommon.h"
#include "tlColor.h"
#include "tlCopyOnWrite.h"
#include "tlStream.h"
#include "tlException.h"
#include <string.h>
#include <cstdint>
#if defined(HAVE_QT)
# include <QImage>
#endif
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 tl::Color.
*/
class LAYBASIC_PUBLIC PixelBuffer
{
public:
/**
* @brief Creates an image with the given height and width
*
* If data is given, the image is initialized with the given data and will take ownership over the
* data block.
*
* The size of the data block needs to be w*h elements.
*/
PixelBuffer (unsigned int w, unsigned int h, tl::color_t *data);
/**
* @brief Creates an image with the given height and width
*
* If data is given, the image is initialized with the given data. A copy of the data is created.
*
* "stride" specifies the stride (distance between two rows of data).
* The size of the data block needs to be stride*h elements or w*h if stride is not given.
*/
PixelBuffer (unsigned int w, unsigned int h, const tl::color_t *data = 0, unsigned int stride = 0);
/**
* @brief Default constructor
*/
PixelBuffer ();
/**
* @brief Copy constructor
*/
PixelBuffer (const PixelBuffer &other);
/**
* @brief Move constructor
*/
PixelBuffer (PixelBuffer &&other);
/**
* @brief Destructor
*/
~PixelBuffer ();
/**
* @brief Equality
*/
bool operator== (const PixelBuffer &other) const;
/**
* @brief Inequality
*/
bool operator!= (const PixelBuffer &other) const
{
return !operator== (other);
}
/**
* @brief Assignment
*/
PixelBuffer &operator= (const PixelBuffer &other);
/**
* @brief Move constructor
*/
PixelBuffer &operator= (PixelBuffer &&other);
/**
* @brief Sets a value indicating whether an alpha channel is present
*/
void set_transparent (bool f);
/**
* @brief Gets a value indicating whether an alpha channel is present
*/
bool transparent () const
{
return m_transparent;
}
/**
* @brief Swaps this image with another one
*/
void swap (PixelBuffer &other);
/**
* @brief Gets the images width
*/
unsigned int width () const
{
return m_width;
}
/**
* @brief Gets the images width
*/
unsigned int height () const
{
return m_height;
}
/**
* @brief Gets the image stride (number of bytes per row)
*/
unsigned int stride () const
{
return sizeof (tl::color_t) * m_width;
}
/**
* @brief Fills the image with the given color
*/
void fill (tl::color_t);
/**
* @brief Gets the scanline for row n
*/
tl::color_t *scan_line (unsigned int n);
/**
* @brief Gets the scanline for row n (const version)
*/
const tl::color_t *scan_line (unsigned int n) const;
/**
* @brief Gets the data pointer
*/
tl::color_t *data ();
/**
* @brief Gets the data pointer (const version)
*/
const tl::color_t *data () const;
#if defined(HAVE_QT)
/**
* @brief Produces a QImage object from the image
*
* NOTE: this version creates a reference, i.e. the QImage is valid only
* during the lifetime of the PixelBuffer.
*/
QImage to_image () const;
/**
* @brief Produces a QImage object from the image
*
* NOTE: this version creates a copy and the QImage is independent of the
* PixelBuffer.
*/
QImage to_image_copy () const;
/**
* @brief Creates a pixel buffer from a QImage object
*/
static PixelBuffer from_image (const QImage &img);
#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) const;
#endif
/**
* @brief Overlays the other image with this one
*
* This feature does not implement real alpha blending. Instead all
* pixels with an alpha value >= 128 from the other image are patched into this image.
*/
void patch (const PixelBuffer &other);
/**
* @brief Generates the image difference
*
* This feature produces a binary-alpha image of *this and other. The
* result can be patched into this image to render the same image than
* "other". The difference image will contains the pixels from other which
* are different from *this.
*
* alpha values from this and other are ignored.
*/
PixelBuffer diff (const PixelBuffer &other) const;
/**
* @brief Gets the texts
*
* Texts are annotations which can be stored to PNG and back.
*/
const std::vector<std::pair<std::string, std::string> > &texts () const
{
return m_texts;
}
/**
* @brief Sets the texts
*
* Texts are annotations which can be stored to PNG and back.
*/
void set_texts (const std::vector<std::pair<std::string, std::string> > &texts)
{
m_texts = texts;
}
private:
class ImageData
{
public:
ImageData ()
: mp_data (0), m_length (0)
{
// .. nothing yet ..
}
ImageData (tl::color_t *data, size_t length)
: mp_data (data), m_length (length)
{
// .. nothing yet ..
}
ImageData (const ImageData &other)
{
m_length = other.length ();
mp_data = new tl::color_t [other.length ()];
memcpy (mp_data, other.data (), m_length * sizeof (tl::color_t));
}
~ImageData ()
{
delete[] mp_data;
mp_data = 0;
}
size_t length () const { return m_length; }
tl::color_t *data () { return mp_data; }
const tl::color_t *data () const { return mp_data; }
private:
tl::color_t *mp_data;
size_t m_length;
ImageData &operator= (const ImageData &other);
};
unsigned int m_width, m_height;
bool m_transparent;
tl::copy_on_write_ptr<ImageData> m_data;
std::vector<std::pair<std::string, std::string> > m_texts;
};
/**
* @brief An monochrome image class
*
* This class substitutes QImage for monochrome images in Qt-less applications.
*/
class LAYBASIC_PUBLIC BitmapBuffer
{
public:
/**
* @brief Creates an image with the given height and width
*
* If data is given, the image is initialized with the given data and will take ownership over the
* data block.
*
* Lines are byte-aligned.
*/
BitmapBuffer (unsigned int w, unsigned int h, uint8_t *data);
/**
* @brief Creates an image with the given height and width
*
* If data is given, the image is initialized with the given data. A copy of the data is created.
*
* "stride" specifies the stride (distance in bytes between two rows of data).
* The size of the data block needs to be stride*h elements or bytes(w)*h if stride is not given.
*/
BitmapBuffer (unsigned int w, unsigned int h, const uint8_t *data = 0, unsigned int stride = 0);
/**
* @brief Default constructor
*/
BitmapBuffer ();
/**
* @brief Copy constructor
*/
BitmapBuffer (const BitmapBuffer &other);
/**
* @brief Move constructor
*/
BitmapBuffer (BitmapBuffer &&other);
/**
* @brief Equality
*/
bool operator== (const BitmapBuffer &other) const;
/**
* @brief Inequality
*/
bool operator!= (const BitmapBuffer &other) const
{
return !operator== (other);
}
/**
* @brief Destructor
*/
~BitmapBuffer ();
/**
* @brief Assignment
*/
BitmapBuffer &operator= (const BitmapBuffer &other);
/**
* @brief Move constructor
*/
BitmapBuffer &operator= (BitmapBuffer &&other);
/**
* @brief Swaps this image with another one
*/
void swap (BitmapBuffer &other);
/**
* @brief Gets the images width
*/
unsigned int width () const
{
return m_width;
}
/**
* @brief Gets the images width
*/
unsigned int height () const
{
return m_height;
}
/**
* @brief Gets the image stride (number of bytes per row)
*/
unsigned int stride () const
{
return m_stride;
}
/**
* @brief Fills the image with the given color
*/
void fill (bool value);
/**
* @brief Gets the scanline for row n
*/
uint8_t *scan_line (unsigned int n);
/**
* @brief Gets the scanline for row n (const version)
*/
const uint8_t *scan_line (unsigned int n) const;
/**
* @brief Gets the data pointer
*/
uint8_t *data ();
/**
* @brief Gets the data pointer (const version)
*/
const uint8_t *data () const;
#if defined(HAVE_QT)
/**
* @brief Produces a QImage object from the image
*
* NOTE: this version creates a reference, i.e. the QImage is valid only
* during the lifetime of the BitmapBuffer.
*/
QImage to_image () const;
/**
* @brief Produces a QImage object from the image
*
* NOTE: this version creates a copy and the QImage is independent of the
* BitmapBuffer.
*/
QImage to_image_copy () const;
/**
* @brief Creates a pixel buffer from a QImage object
*/
static BitmapBuffer from_image (const QImage &img);
#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) const;
#endif
/**
* @brief Gets the texts
*
* Texts are annotations which can be stored to PNG and back.
*/
const std::vector<std::pair<std::string, std::string> > &texts () const
{
return m_texts;
}
/**
* @brief Sets the texts
*
* Texts are annotations which can be stored to PNG and back.
*/
void set_texts (const std::vector<std::pair<std::string, std::string> > &texts)
{
m_texts = texts;
}
private:
class MonoImageData
{
public:
MonoImageData ()
: mp_data (0), m_length (0)
{
// .. nothing yet ..
}
MonoImageData (uint8_t *data, size_t length)
: mp_data (data), m_length (length)
{
// .. nothing yet ..
}
MonoImageData (const MonoImageData &other)
{
m_length = other.length ();
mp_data = new uint8_t [other.length ()];
memcpy (mp_data, other.data (), m_length * sizeof (uint8_t));
}
~MonoImageData ()
{
delete[] mp_data;
mp_data = 0;
}
size_t length () const { return m_length; }
uint8_t *data () { return mp_data; }
const uint8_t *data () const { return mp_data; }
private:
uint8_t *mp_data;
size_t m_length;
MonoImageData &operator= (const MonoImageData &other);
};
unsigned int m_width, m_height;
unsigned int m_stride;
tl::copy_on_write_ptr<MonoImageData> m_data;
std::vector<std::pair<std::string, std::string> > m_texts;
};
}
#endif
@@ -23,12 +23,12 @@
#include "layPixelBufferPainter.h"
#include "layFixedFont.h"
#include "layPixelBuffer.h"
#include "tlPixelBuffer.h"
namespace lay
{
PixelBufferPainter::PixelBufferPainter (lay::PixelBuffer &img, unsigned int width, unsigned int height, double resolution)
PixelBufferPainter::PixelBufferPainter (tl::PixelBuffer &img, unsigned int width, unsigned int height, double resolution)
: mp_img (&img),
m_resolution (resolution), m_width (width), m_height (height)
{
@@ -28,12 +28,15 @@
#include "tlColor.h"
#include "dbPoint.h"
namespace tl
{
class PixelBuffer;
}
namespace lay {
class PixelBuffer;
/**
* @brief A very simplistic painter for lay::PixelBuffer
* @brief A very simplistic painter for tl::PixelBuffer
*
* This painter supports very few primitives currently and is used to paint the
* background grid for example.
@@ -41,7 +44,7 @@ class PixelBuffer;
class LAYBASIC_PUBLIC PixelBufferPainter
{
public:
PixelBufferPainter (lay::PixelBuffer &img, unsigned int width, unsigned int height, double resolution);
PixelBufferPainter (tl::PixelBuffer &img, unsigned int width, unsigned int height, double resolution);
void set (const db::Point &p, tl::Color c);
void draw_line (const db::Point &p1, const db::Point &p2, tl::Color c);
@@ -50,7 +53,7 @@ public:
void draw_text (const char *t, const db::Point &p, tl::Color c, int halign, int valign);
private:
lay::PixelBuffer *mp_img;
tl::PixelBuffer *mp_img;
double m_resolution;
int m_width, m_height;
};
@@ -392,7 +392,7 @@ BitmapRedrawThreadCanvas::initialize_plane (lay::CanvasPlane *plane, unsigned in
}
void
BitmapRedrawThreadCanvas::to_image (const std::vector <lay::ViewOp> &view_ops, const lay::DitherPattern &dp, const lay::LineStyles &ls, tl::Color background, tl::Color foreground, tl::Color active, const lay::Drawings *drawings, lay::PixelBuffer &img, unsigned int width, unsigned int height)
BitmapRedrawThreadCanvas::to_image (const std::vector <lay::ViewOp> &view_ops, const lay::DitherPattern &dp, const lay::LineStyles &ls, tl::Color background, tl::Color foreground, tl::Color active, const lay::Drawings *drawings, tl::PixelBuffer &img, unsigned int width, unsigned int height)
{
if (width > m_width) {
width = m_width;
@@ -412,7 +412,7 @@ BitmapRedrawThreadCanvas::to_image (const std::vector <lay::ViewOp> &view_ops, c
}
void
BitmapRedrawThreadCanvas::to_image_mono (const std::vector <lay::ViewOp> &view_ops, const lay::DitherPattern &dp, const lay::LineStyles &ls, bool background, bool foreground, bool active, const lay::Drawings *drawings, lay::BitmapBuffer &img, unsigned int width, unsigned int height)
BitmapRedrawThreadCanvas::to_image_mono (const std::vector <lay::ViewOp> &view_ops, const lay::DitherPattern &dp, const lay::LineStyles &ls, bool background, bool foreground, bool active, const lay::Drawings *drawings, tl::BitmapBuffer &img, unsigned int width, unsigned int height)
{
if (width > m_width) {
width = m_width;
@@ -29,7 +29,7 @@
#include "dbTrans.h"
#include "layViewOp.h"
#include "layBitmapRenderer.h"
#include "layPixelBuffer.h"
#include "tlPixelBuffer.h"
#include "tlThreads.h"
#include <vector>
@@ -319,12 +319,12 @@ public:
/**
* @brief Transfer the content to a PixelBuffer
*/
void to_image (const std::vector <lay::ViewOp> &view_ops, const lay::DitherPattern &dp, const lay::LineStyles &ls, tl::Color background, tl::Color foreground, tl::Color active, const lay::Drawings *drawings, PixelBuffer &img, unsigned int width, unsigned int height);
void to_image (const std::vector <lay::ViewOp> &view_ops, const lay::DitherPattern &dp, const lay::LineStyles &ls, tl::Color background, tl::Color foreground, tl::Color active, const lay::Drawings *drawings, tl::PixelBuffer &img, unsigned int width, unsigned int height);
/**
* @brief Transfer the content to a BitmapBuffer (monochrome)
*/
void to_image_mono (const std::vector <lay::ViewOp> &view_ops, const lay::DitherPattern &dp, const lay::LineStyles &ls, bool background, bool foreground, bool active, const lay::Drawings *drawings, lay::BitmapBuffer &img, unsigned int width, unsigned int height);
void to_image_mono (const std::vector <lay::ViewOp> &view_ops, const lay::DitherPattern &dp, const lay::LineStyles &ls, bool background, bool foreground, bool active, const lay::Drawings *drawings, tl::BitmapBuffer &img, unsigned int width, unsigned int height);
/**
* @brief Gets the current bitmap data as a BitmapCanvasData object
+2 -2
View File
@@ -1357,13 +1357,13 @@ BitmapViewObjectCanvas::set_size (double resolution)
m_resolution = resolution;
}
lay::PixelBuffer *
tl::PixelBuffer *
BitmapViewObjectCanvas::bg_image ()
{
return 0;
}
lay::BitmapBuffer *
tl::BitmapBuffer *
BitmapViewObjectCanvas::bg_bitmap ()
{
return 0;
+8 -4
View File
@@ -63,6 +63,12 @@ namespace db
class Layout;
}
namespace tl
{
class PixelBuffer;
class BitmapBuffer;
}
namespace lay {
class Viewport;
@@ -70,8 +76,6 @@ class ViewObjectUI;
class ViewObjectCanvas;
class CanvasPlane;
class Bitmap;
class PixelBuffer;
class BitmapBuffer;
#if defined(HAVE_QT)
class DragDropDataBase;
@@ -1323,12 +1327,12 @@ public:
/**
* @brief Gets the pixel buffer that background objects render to
*/
virtual lay::PixelBuffer *bg_image ();
virtual tl::PixelBuffer *bg_image ();
/**
* @brief Gets the monochrome pixel buffer that background objects render to
*/
virtual lay::BitmapBuffer *bg_bitmap ();
virtual tl::BitmapBuffer *bg_bitmap ();
private:
std::map <lay::ViewOp, unsigned int> m_fg_bitmap_table;
+1 -3
View File
@@ -36,7 +36,6 @@ SOURCES += \
gsiDeclLayLayoutViewBase.cc \
gsiDeclLayMarker.cc \
gsiDeclLayPlugin.cc \
gsiDeclLayPixelBuffer.cc \
laybasicForceLink.cc \
layAnnotationShapes.cc \
layBitmap.cc \
@@ -64,7 +63,6 @@ SOURCES += \
layNetColorizer.cc \
layObjectInstPath.cc \
layParsedLayerSource.cc \
layPixelBuffer.cc \
layPixelBufferPainter.cc \
layPlugin.cc \
layRedrawLayerInfo.cc \
@@ -116,7 +114,7 @@ HEADERS += \
layNetColorizer.h \
layObjectInstPath.h \
layParsedLayerSource.h \
layPixelBuffer.h \
tlPixelBuffer.h \
layPixelBufferPainter.h \
layPlugin.h \
layRedrawLayerInfo.h \
+3 -3
View File
@@ -24,11 +24,11 @@
#include "layBitmap.h"
#include "layDitherPattern.h"
#include "layLineStyles.h"
#include "layPixelBuffer.h"
#include "tlPixelBuffer.h"
#include "tlUnitTest.h"
std::string
to_string (const lay::PixelBuffer &img, unsigned int mask)
to_string (const tl::PixelBuffer &img, unsigned int mask)
{
std::string s;
for (unsigned int i = 0; i < 32; ++i) {
@@ -85,7 +85,7 @@ TEST(1)
view_ops.push_back (lay::ViewOp (0x000080, lay::ViewOp::Copy, 0, 0, 0, lay::ViewOp::Rect, 1));
view_ops.push_back (lay::ViewOp (0x0000c0, lay::ViewOp::Or, 0, 0, 0, lay::ViewOp::Rect, 3));
lay::PixelBuffer img (32, 32);
tl::PixelBuffer img (32, 32);
img.fill (0);
lay::DitherPattern dp;
@@ -1,656 +0,0 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2022 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "layPixelBuffer.h"
#include "tlUnitTest.h"
#include "tlTimer.h"
#if defined(HAVE_QT)
# include <QImage>
# include <QPainter>
static bool compare_images (const QImage &qimg, const std::string &au)
{
QImage qimg2;
qimg2.load (tl::to_qstring (au));
if (qimg2.width () == (int) qimg.width () && qimg2.height () == (int) qimg.height ()) {
for (int j = 0; j < qimg.height (); ++j) {
for (int i = 0; i < qimg.width (); ++i) {
if (((const tl::color_t *) qimg.scanLine (j))[i] != ((const tl::color_t *) qimg2.scanLine (j))[i]) {
return false;
}
}
}
return true;
} else {
return false;
}
}
static bool compare_images_mono (const QImage &qimg, const std::string &au)
{
QImage qimg2;
qimg2.load (tl::to_qstring (au));
qimg2 = qimg2.convertToFormat (QImage::Format_MonoLSB); // that's the format of BitmapBuffer ..
if (qimg2.width () == (int) qimg.width () && qimg2.height () == (int) qimg.height ()) {
// NOTE: slooooow ...
for (int j = 0; j < qimg.height (); ++j) {
for (int i = 0; i < qimg.width (); ++i) {
if ((qimg.scanLine (j)[i / 8] & (0x01 << (i % 8))) != (qimg2.scanLine (j)[i / 8] & (0x01 << (i % 8)))) {
return false;
}
}
}
return true;
} else {
return false;
}
}
#endif
static bool compare_images (const lay::PixelBuffer &img, const lay::PixelBuffer &img2)
{
return img == img2;
}
static bool compare_images (const lay::BitmapBuffer &img, const lay::BitmapBuffer &img2)
{
return img == img2;
}
TEST(1)
{
lay::PixelBuffer img (15, 25);
EXPECT_EQ (img.width (), 15);
EXPECT_EQ (img.height (), 25);
EXPECT_EQ (img.stride (), 15 * sizeof (tl::color_t));
EXPECT_EQ (img.transparent (), false);
img.set_transparent (true);
EXPECT_EQ (img.transparent (), true);
img.fill (0x112233);
EXPECT_EQ (img.scan_line (5)[10], 0x112233);
lay::PixelBuffer img2;
EXPECT_EQ (img2.transparent (), false);
img2 = img;
EXPECT_EQ (img2.transparent (), true);
EXPECT_EQ (img2.width (), 15);
EXPECT_EQ (img2.height (), 25);
EXPECT_EQ (img.scan_line (5)[10], 0x112233);
EXPECT_EQ (img2.scan_line (5)[10], 0x112233);
img2.fill (0x332211);
EXPECT_EQ (img.scan_line (5)[10], 0x112233);
EXPECT_EQ (img2.scan_line (5)[10], 0x332211);
img.set_transparent (false);
img2.swap (img);
EXPECT_EQ (img2.transparent (), false);
EXPECT_EQ (img2.scan_line (5)[10], 0x112233);
EXPECT_EQ (img.scan_line (5)[10], 0x332211);
img2 = img;
EXPECT_EQ (compare_images (img, img2), true);
EXPECT_EQ (img.scan_line (5)[10], 0x332211);
EXPECT_EQ (img2.scan_line (5)[10], 0x332211);
img2 = lay::PixelBuffer (10, 16);
EXPECT_EQ (img.width (), 15);
EXPECT_EQ (img.height (), 25);
EXPECT_EQ (img2.width (), 10);
EXPECT_EQ (img2.height (), 16);
img2.fill (0x010203);
EXPECT_EQ (compare_images (img, img2), false);
EXPECT_EQ (img.scan_line (5)[10], 0x332211);
EXPECT_EQ (img2.scan_line (5)[8], 0xff010203);
img = std::move (img2);
EXPECT_EQ (compare_images (img, img2), false);
EXPECT_EQ (img.width (), 10);
EXPECT_EQ (img.height (), 16);
EXPECT_EQ (img.scan_line (5)[8], 0xff010203);
lay::PixelBuffer img3 (img);
EXPECT_EQ (compare_images (img, img3), true);
EXPECT_EQ (img3.width (), 10);
EXPECT_EQ (img3.height (), 16);
EXPECT_EQ (img3.scan_line (5)[8], 0xff010203);
img.fill (0x102030);
EXPECT_EQ (compare_images (img, img3), false);
EXPECT_EQ (img3.width (), 10);
EXPECT_EQ (img3.height (), 16);
EXPECT_EQ (img3.scan_line (5)[8], 0xff010203);
EXPECT_EQ (img.width (), 10);
EXPECT_EQ (img.height (), 16);
EXPECT_EQ (img.scan_line (5)[8], 0xff102030);
lay::PixelBuffer img4 (std::move (img));
EXPECT_EQ (img4.width (), 10);
EXPECT_EQ (img4.height (), 16);
EXPECT_EQ (img4.scan_line (5)[8], 0xff102030);
// other constructors
EXPECT_EQ (compare_images (lay::PixelBuffer (img4.width (), img4.height (), (const tl::color_t *) img4.data ()), img4), true);
EXPECT_EQ (compare_images (lay::PixelBuffer (img4.width (), img4.height (), (const tl::color_t *) img4.data (), img4.stride ()), img4), true);
tl::color_t *dnew = new tl::color_t [ img4.width () * img4.height () * sizeof (tl::color_t) ];
memcpy (dnew, (const tl::color_t *) img4.data (), img4.width () * img4.height () * sizeof (tl::color_t));
EXPECT_EQ (compare_images (lay::PixelBuffer (img4.width (), img4.height (), dnew), img4), true);
}
#if defined(HAVE_QT)
TEST(2)
{
lay::PixelBuffer img (227, 231);
for (unsigned int i = 0; i < img.width (); ++i) {
for (unsigned int j = 0; j < img.height (); ++j) {
img.scan_line (j) [i] = 0xff000000 | (i << 16) | j;
}
}
EXPECT_EQ (img.transparent (), false);
EXPECT_EQ (img.to_image ().format () == QImage::Format_RGB32, true);
std::string tmp = tmp_file ("test.png");
QImage qimg = img.to_image ();
qimg.save (tl::to_qstring (tmp));
tl::info << "PNG file written to " << tmp;
std::string au = tl::testsrc () + "/testdata/lay/au.png";
tl::info << "PNG file read from " << au;
EXPECT_EQ (compare_images (qimg, au), true);
lay::PixelBuffer img_returned = lay::PixelBuffer::from_image (qimg);
EXPECT_EQ (compare_images (img, img_returned), true);
lay::PixelBuffer img_saved (img);
img.scan_line (52) [42] = 0xff000000;
lay::PixelBuffer diff = img.diff (img_saved);
EXPECT_EQ (diff.transparent (), true);
EXPECT_EQ (diff.to_image ().format () == QImage::Format_ARGB32, true);
EXPECT_EQ (compare_images (img.to_image (), au), false);
EXPECT_EQ (compare_images (img_saved.to_image (), au), true);
img.patch (diff);
EXPECT_EQ (compare_images (img.to_image (), au), true);
img.fill (0xff000000);
img.patch (diff);
tmp = tmp_file ("diff.png");
qimg = img.to_image ();
qimg.save (tl::to_qstring (tmp));
tl::info << "PNG file written to " << tmp;
au = tl::testsrc () + "/testdata/lay/au_diff.png";
tl::info << "PNG file read from " << au;
EXPECT_EQ (compare_images (qimg, au), true);
qimg = img.to_image_copy ();
img.fill (false);
tmp = tmp_file ("test2.png");
qimg.save (tl::to_qstring (tmp));
tl::info << "PNG file written to " << tmp;
EXPECT_EQ (compare_images (qimg, au), true);
}
#endif
#if defined(HAVE_PNG)
// libpng support
TEST(3)
{
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);
}
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.png";
EXPECT_EQ (compare_images (img2.to_image (), au), true);
#endif
}
TEST(4)
{
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);
}
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.png";
EXPECT_EQ (compare_images (img2.to_image (), au), true);
#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(7)
{
{
tl::SelfTimer timer ("Run time - lay::Image copy, no write (should be very fast)");
lay::PixelBuffer img (1000, 1000);
img.fill (0x112233);
for (unsigned int i = 0; i < 5000; ++i) {
lay::PixelBuffer img2 (img);
}
}
#if defined(HAVE_QT)
{
tl::SelfTimer timer ("Run time - QImage copy, no write (should be very fast)");
lay::PixelBuffer img (1000, 1000);
img.fill (0x112233);
QImage qimg (img.to_image ());
for (unsigned int i = 0; i < 5000; ++i) {
QImage qimg2 (qimg);
}
}
#endif
{
tl::SelfTimer timer ("Run time - lay::Image copy on write");
lay::PixelBuffer img (1000, 1000);
img.fill (0x112233);
for (unsigned int i = 0; i < 5000; ++i) {
lay::PixelBuffer img2 (img);
img2.scan_line (100) [7] = 0;
}
}
#if defined(HAVE_QT)
{
tl::SelfTimer timer ("Run time - QImage copy on write (should not be much less than lay::Image copy on write)");
lay::PixelBuffer img (1000, 1000);
img.fill (0x112233);
QImage qimg (img.to_image ());
for (unsigned int i = 0; i < 5000; ++i) {
QImage qimg2 (qimg);
qimg2.scanLine (100) [7] = 0;
}
}
{
tl::SelfTimer timer ("Run time - direct QImage paint");
lay::PixelBuffer img (1000, 1000);
img.fill (0x112233);
QImage qimg (img.to_image ());
QImage qrec (img.to_image ());
qrec.fill (0);
QPainter painter (&qrec);
for (unsigned int i = 0; i < 1000; ++i) {
painter.drawImage (QPoint (0, 0), qimg);
}
}
{
tl::SelfTimer timer ("Run time - lay::Image paint (should not be much more than direct QImage paint)");
lay::PixelBuffer img (1000, 1000);
img.fill (0x112233);
QImage qrec (img.to_image ());
qrec.fill (0);
QPainter painter (&qrec);
for (unsigned int i = 0; i < 1000; ++i) {
painter.drawImage (QPoint (0, 0), img.to_image ());
}
}
#endif
}
// Monochrome version
TEST(11)
{
lay::BitmapBuffer img (15, 25);
EXPECT_EQ (img.width (), 15);
EXPECT_EQ (img.height (), 25);
EXPECT_EQ (img.stride (), 4);
img.fill (true);
EXPECT_EQ (img.scan_line (5)[1], 0xff);
lay::BitmapBuffer img2;
img2 = img;
EXPECT_EQ (img2.width (), 15);
EXPECT_EQ (img2.height (), 25);
EXPECT_EQ (img.scan_line (5)[1], 0xff);
EXPECT_EQ (img2.scan_line (5)[1], 0xff);
img2.fill (false);
EXPECT_EQ (img.scan_line (5)[1], 0xff);
EXPECT_EQ (img2.scan_line (5)[1], 0);
img2.swap (img);
EXPECT_EQ (img2.scan_line (5)[1], 0xff);
EXPECT_EQ (img.scan_line (5)[1], 0);
img2 = img;
EXPECT_EQ (compare_images (img, img2), true);
EXPECT_EQ (img.scan_line (5)[1], 0);
EXPECT_EQ (img2.scan_line (5)[1], 0);
img2 = lay::BitmapBuffer (10, 16);
EXPECT_EQ (img.width (), 15);
EXPECT_EQ (img.height (), 25);
EXPECT_EQ (img2.width (), 10);
EXPECT_EQ (img2.height (), 16);
img2.fill (true);
EXPECT_EQ (compare_images (img, img2), false);
EXPECT_EQ (img.scan_line (5)[1], 0);
EXPECT_EQ (img2.scan_line (5)[0], 0xff);
img = std::move (img2);
EXPECT_EQ (compare_images (img, img2), false);
EXPECT_EQ (img.width (), 10);
EXPECT_EQ (img.height (), 16);
EXPECT_EQ (img.scan_line (5)[0], 0xff);
lay::BitmapBuffer img3 (img);
EXPECT_EQ (compare_images (img, img3), true);
EXPECT_EQ (img3.width (), 10);
EXPECT_EQ (img3.height (), 16);
EXPECT_EQ (img3.scan_line (5)[1], 0xff);
img.fill (false);
EXPECT_EQ (compare_images (img, img3), false);
EXPECT_EQ (img3.width (), 10);
EXPECT_EQ (img3.height (), 16);
EXPECT_EQ (img3.scan_line (5)[1], 0xff);
EXPECT_EQ (img.width (), 10);
EXPECT_EQ (img.height (), 16);
EXPECT_EQ (img.scan_line (5)[1], 0);
lay::BitmapBuffer img4 (std::move (img));
EXPECT_EQ (img4.width (), 10);
EXPECT_EQ (img4.height (), 16);
EXPECT_EQ (img4.scan_line (5)[1], 0);
// other constructors
EXPECT_EQ (compare_images (lay::BitmapBuffer (img4.width (), img4.height (), (const uint8_t *) img4.data ()), img4), true);
EXPECT_EQ (compare_images (lay::BitmapBuffer (img4.width (), img4.height (), (const uint8_t *) img4.data (), img4.stride ()), img4), true);
uint8_t *dnew = new uint8_t [ img4.width () * img4.height () * sizeof (uint8_t) ];
memcpy (dnew, (const uint8_t *) img4.data (), img4.stride () * img4.height ());
EXPECT_EQ (compare_images (lay::BitmapBuffer (img4.width (), img4.height (), dnew), img4), true);
}
#if defined(HAVE_QT)
TEST(12)
{
lay::BitmapBuffer img (227, 231);
for (unsigned int i = 0; i < img.stride (); ++i) {
for (unsigned int j = 0; j < img.height (); ++j) {
img.scan_line (j) [i] = uint8_t (i * j);
}
}
EXPECT_EQ (img.to_image ().format () == QImage::Format_MonoLSB, true);
std::string tmp = tmp_file ("test.png");
QImage qimg = img.to_image ();
qimg.save (tl::to_qstring (tmp));
tl::info << "PNG file written to " << tmp;
std::string au = tl::testsrc () + "/testdata/lay/au_mono.png";
tl::info << "PNG file read from " << au;
EXPECT_EQ (compare_images_mono (qimg, au), true);
lay::BitmapBuffer img_returned = lay::BitmapBuffer::from_image (qimg);
EXPECT_EQ (compare_images (img, img_returned), true);
qimg = img.to_image_copy ();
img.fill (false);
tmp = tmp_file ("test2.png");
qimg.save (tl::to_qstring (tmp));
tl::info << "PNG file written to " << tmp;
EXPECT_EQ (compare_images_mono (qimg, au), true);
}
#endif
#if defined(HAVE_PNG)
// libpng support
TEST(13)
{
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);
}
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_mono.png";
EXPECT_EQ (compare_images_mono (img2.to_image (), au), true);
#endif
}
#endif
-1
View File
@@ -12,7 +12,6 @@ SOURCES = \
layBitmapsToImage.cc \
layLayerProperties.cc \
layParsedLayerSource.cc \
layPixelBufferTests.cc \
layRenderer.cc \
layAbstractMenuTests.cc \
laySnapTests.cc