From 0b48cb90200f5829b3a70ed8f7649d1cc944c62a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 2 May 2022 00:02:47 +0200 Subject: [PATCH] WIP: added new lay::Image class --- src/laybasic/laybasic/layImage.cc | 201 +++++++++++++++++++++ src/laybasic/laybasic/layImage.h | 219 +++++++++++++++++++++++ src/laybasic/laybasic/laybasic.pro | 2 + src/laybasic/unit_tests/layImageTests.cc | 0 src/laybasic/unit_tests/unit_tests.pro | 5 +- src/tl/tl/tlCopyOnWrite.h | 16 ++ src/tl/unit_tests/unit_tests.pro | 10 +- 7 files changed, 446 insertions(+), 7 deletions(-) create mode 100644 src/laybasic/laybasic/layImage.cc create mode 100644 src/laybasic/laybasic/layImage.h create mode 100644 src/laybasic/unit_tests/layImageTests.cc diff --git a/src/laybasic/laybasic/layImage.cc b/src/laybasic/laybasic/layImage.cc new file mode 100644 index 000000000..95010135e --- /dev/null +++ b/src/laybasic/laybasic/layImage.cc @@ -0,0 +1,201 @@ + +/* + + 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 "layImage.h" +#include "tlAssert.h" + +namespace lay +{ + +Image::Image (unsigned int w, unsigned int h, lay::color_t *data) +{ + m_width = w; + m_height = h; + m_data.reset (new ImageData (data, w * h)); +} + +Image::Image (unsigned int w, unsigned int h, const lay::color_t *data, unsigned int stride) +{ + m_width = w; + m_height = h; + + lay::color_t *d = new color_t [w * h]; + + if (data) { + for (unsigned int i = 0; i < h; ++i) { + for (unsigned int i = 0; i < h; ++i) { + *d++ = *data++; + } + if (stride > w) { + data += stride - w; + } + } + } + + m_data.reset (new ImageData (d, w * h)); +} + +Image::Image () +{ + m_width = 0; + m_height = 0; +} + +Image::Image (const Image &other) +{ + operator= (other); +} + +Image::Image (Image &&other) +{ + swap (other); +} + +Image::~Image () +{ + // .. nothing yet .. +} + +Image & +Image::operator= (const Image &other) +{ + if (this != &other) { + m_width = other.m_width; + m_height = other.m_height; + m_data = other.m_data; + } + return *this; +} + +Image & +Image::operator= (Image &&other) +{ + if (this != &other) { + swap (other); + } + return *this; +} + +void +Image::swap (Image &other) +{ + if (this == &other) { + return; + } + + std::swap (m_width, other.m_width); + std::swap (m_height, other.m_height); + m_data.swap (other.m_data); +} + +void +Image::fill (lay::color_t c) +{ + color_t *d = data (); + for (unsigned int i = 0; i < m_height; ++i) { + for (unsigned int j = 0; j < m_width; ++j) { + *d++ = c; + } + } +} + +color_t * +Image::scan_line (unsigned int n) +{ + tl_assert (n < m_height); + return m_data->data () + n * m_width; +} + +const color_t * +Image::scan_line (unsigned int n) const +{ + tl_assert (n < m_height); + return m_data->data () + n * m_width; +} + +color_t * +Image::data () +{ + return m_data->data (); +} + +const color_t * +Image::data () const +{ + return m_data->data (); +} + +#if defined(HAVE_QT) +QImage +Image::to_image () const +{ + return QImage ((const uchar *) data (), m_width, m_height, QImage::Format_ARGB32); +} +#endif + +void +Image::patch (const Image &other) +{ + tl_assert (width () == other.width ()); + tl_assert (height () == other.height ()); + + const color_t *d = other.data (); + color_t *dd = data (); + for (unsigned int i = 0; i < m_height; ++i) { + for (unsigned int j = 0; j < m_width; ++j) { + color_t c = *d++; + if ((c & 0x80000000) != 0) { + *dd = c; + } + ++dd; + } + } +} + +Image +Image::diff (const Image &other) const +{ + tl_assert (width () == other.width ()); + tl_assert (height () == other.height ()); + + Image res (m_width, m_height); + + const color_t *d2 = other.data (); + const color_t *d1 = data (); + 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; +} + +} diff --git a/src/laybasic/laybasic/layImage.h b/src/laybasic/laybasic/layImage.h new file mode 100644 index 000000000..7816a5eee --- /dev/null +++ b/src/laybasic/laybasic/layImage.h @@ -0,0 +1,219 @@ + +/* + + 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_layImage +#define HDR_layImage + +#include "laybasicCommon.h" +#include "layColor.h" +#include "tlCopyOnWrite.h" + +#include + +#if defined(HAVE_QT) +# include +#endif + +namespace lay +{ + +/** + * @brief An 32bit 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 Image +{ +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. + */ + Image (unsigned int w, unsigned int h, lay::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. + */ + Image (unsigned int w, unsigned int h, const lay::color_t *data = 0, unsigned int stride = 0); + + /** + * @brief Default constructor + */ + Image (); + + /** + * @brief Copy constructor + */ + Image (const Image &other); + + /** + * @brief Move constructor + */ + Image (Image &&other); + + /** + * @brief Destructor + */ + ~Image (); + + /** + * @brief Assignment + */ + Image &operator= (const Image &other); + + /** + * @brief Move constructor + */ + Image &operator= (Image &&other); + + /** + * @brief Swaps this image with another one + */ + void swap (Image &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 Fills the image with the given color + */ + void fill (lay::color_t); + + /** + * @brief Gets the scanline for row n + */ + color_t *scan_line (unsigned int n); + + /** + * @brief Gets the scanline for row n (const version) + */ + const color_t *scan_line (unsigned int n) const; + + /** + * @brief Gets the data pointer + */ + color_t *data (); + + /** + * @brief Gets the data pointer (const version) + */ + const color_t *data () const; + +#if defined(HAVE_QT) + /** + * @brief Produces a QImage object from the image + */ + QImage to_image () 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 Image &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". + * + * alpha values from this and other are ignored. + */ + Image diff (const Image &other) const; + +private: + class ImageData + { + public: + ImageData () + : mp_data (0), m_length (0) + { + // .. nothing yet .. + } + + ImageData (lay::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 lay::color_t [other.length ()]; + memcpy (mp_data, other.data (), m_length * sizeof (lay::color_t)); + } + + ~ImageData () + { + delete[] mp_data; + mp_data = 0; + } + + size_t length () const { return m_length; } + lay::color_t *data () { return mp_data; } + const lay::color_t *data () const { return mp_data; } + + private: + lay::color_t *mp_data; + size_t m_length; + size_t m_stride; + + ImageData &operator= (const ImageData &other); + }; + + unsigned int m_width, m_height; + tl::copy_on_write_ptr m_data; +}; + +} + +#endif diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index 53e543b71..e760d0d00 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -118,6 +118,7 @@ DEFINES += MAKE_LAYBASIC_LIBRARY layGenericSyntaxHighlighter.cc \ layGridNetConfigPage.cc \ layHierarchyControlPanel.cc \ + layImage.cc \ layIndexedNetlistModel.cc \ layItemDelegates.cc \ layLayerControlPanel.cc \ @@ -191,6 +192,7 @@ DEFINES += MAKE_LAYBASIC_LIBRARY layGenericSyntaxHighlighter.h \ layGridNetConfigPage.h \ layHierarchyControlPanel.h \ + layImage.h \ layIndexedNetlistModel.h \ layItemDelegates.h \ layLayerControlPanel.h \ diff --git a/src/laybasic/unit_tests/layImageTests.cc b/src/laybasic/unit_tests/layImageTests.cc new file mode 100644 index 000000000..e69de29bb diff --git a/src/laybasic/unit_tests/unit_tests.pro b/src/laybasic/unit_tests/unit_tests.pro index 0da4329f9..40db96675 100644 --- a/src/laybasic/unit_tests/unit_tests.pro +++ b/src/laybasic/unit_tests/unit_tests.pro @@ -10,13 +10,14 @@ SOURCES = \ layAnnotationShapes.cc \ layBitmap.cc \ layBitmapsToImage.cc \ + layImageTests.cc \ layColorTests.cc \ layLayerProperties.cc \ layParsedLayerSource.cc \ layRenderer.cc \ layNetlistBrowserModelTests.cc \ - layNetlistBrowserTreeModelTests.cc \ - layAbstractMenuTests.cc \ + layNetlistBrowserTreeModelTests.cc \ + layAbstractMenuTests.cc \ laySnapTests.cc INCLUDEPATH += $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC $$OUT_PWD/../laybasic diff --git a/src/tl/tl/tlCopyOnWrite.h b/src/tl/tl/tlCopyOnWrite.h index 717688592..2dbbae8d1 100644 --- a/src/tl/tl/tlCopyOnWrite.h +++ b/src/tl/tl/tlCopyOnWrite.h @@ -143,6 +143,22 @@ public: release (); } + /** + * @brief Swaps two pointers + */ + void swap (copy_on_write_ptr &other) + { + if (this == &other) { + return; + } + + tl::MutexLocker locker1 (&ms_lock); + tl::MutexLocker locker2 (&other.ms_lock); + + std::swap (mp_x, other.mp_x); + std::swap (mp_holder, other.mp_holder); + } + /** * @brief Gets a writable object * This is when we will create a new copy if the object is shared. diff --git a/src/tl/unit_tests/unit_tests.pro b/src/tl/unit_tests/unit_tests.pro index 667e447bd..a0acbddb2 100644 --- a/src/tl/unit_tests/unit_tests.pro +++ b/src/tl/unit_tests/unit_tests.pro @@ -19,6 +19,7 @@ SOURCES = \ tlFileSystemWatcherTests.cc \ tlFileUtilsTests.cc \ tlHttpStreamTests.cc \ + tlImageTests.cc \ tlIncludeTests.cc \ tlInt128SupportTests.cc \ tlIntervalMapTests.cc \ @@ -36,11 +37,10 @@ SOURCES = \ tlUniqueIdTests.cc \ tlListTests.cc \ tlEquivalenceClustersTests.cc \ - tlUniqueNameTests.cc \ - tlGlobPatternTests.cc \ - tlRecipeTests.cc \ - tlUriTests.cc \ - tlUtilsTests.cc \ + tlUniqueNameTests.cc \ + tlGlobPatternTests.cc \ + tlRecipeTests.cc \ + tlUriTests.cc \ tlVariantTests.cc \ tlWebDAVTests.cc \ tlXMLParserTests.cc