Added mono image class

This commit is contained in:
Matthias Koefferlein 2022-05-04 02:18:01 +02:00
parent 2088881110
commit 17cbcc2877
6 changed files with 514 additions and 16 deletions

View File

@ -201,6 +201,8 @@ public:
/** /**
* @brief Returns the first logical layer for a given layer specification * @brief Returns the first logical layer for a given layer specification
* The first value of the pair indicates whether there is a valid mapping.
* The second value will give the layer to map to.
*/ */
template <class L> template <class L>
std::pair<bool, unsigned int> first_logical (const L &p) const std::pair<bool, unsigned int> first_logical (const L &p) const
@ -215,6 +217,8 @@ public:
/** /**
* @brief Returns the first logical layer for a given layer specification * @brief Returns the first logical layer for a given layer specification
* The first value of the pair indicates whether there is a valid mapping.
* The second value will give the layer to map to.
*/ */
template <class L> template <class L>
std::pair<bool, unsigned int> first_logical (const L &p, db::Layout &layout) const std::pair<bool, unsigned int> first_logical (const L &p, db::Layout &layout) const
@ -230,24 +234,21 @@ public:
/** /**
* @brief Query a layer mapping * @brief Query a layer mapping
* *
* @return A pair telling if the layer is mapped (first=true) and * @return A set of layers which are designated targets.
* the logical layer mapped (second) if this is the case.
*/ */
std::set<unsigned int> logical (const LDPair &p) const; std::set<unsigned int> logical (const LDPair &p) const;
/** /**
* @brief Query a layer mapping from a name * @brief Query a layer mapping from a name
* *
* @return A pair telling if the layer is mapped (first=true) and * @return A set of layers which are designated targets.
* the logical layer mapped (second) if this is the case.
*/ */
std::set<unsigned int> logical (const std::string &name) const; std::set<unsigned int> logical (const std::string &name) const;
/** /**
* @brief Query a layer mapping from a name or LDPair * @brief Query a layer mapping from a name or LDPair
* *
* @return A pair telling if the layer is mapped (first=true) and * @return A set of layers which are designated targets.
* the logical layer mapped (second) if this is the case.
* *
* @param p The layer that is looked for * @param p The layer that is looked for
*/ */
@ -256,8 +257,7 @@ public:
/** /**
* @brief Query or install a layer mapping from a name or LDPair * @brief Query or install a layer mapping from a name or LDPair
* *
* @return A pair telling if the layer is mapped (first=true) and * @return A set of layers which are designated targets.
* the logical layer mapped (second) if this is the case.
* *
* @param p The layer that is looked for * @param p The layer that is looked for
* *

View File

@ -26,7 +26,11 @@
namespace lay namespace lay
{ {
// -----------------------------------------------------------------------------------------------------
// Image implementation
Image::Image (unsigned int w, unsigned int h, lay::color_t *data) Image::Image (unsigned int w, unsigned int h, lay::color_t *data)
: m_data ()
{ {
m_width = w; m_width = w;
m_height = h; m_height = h;
@ -35,16 +39,21 @@ Image::Image (unsigned int w, unsigned int h, lay::color_t *data)
} }
Image::Image (unsigned int w, unsigned int h, const lay::color_t *data, unsigned int stride) Image::Image (unsigned int w, unsigned int h, const lay::color_t *data, unsigned int stride)
: m_data ()
{ {
m_width = w; m_width = w;
m_height = h; m_height = h;
m_transparent = false; m_transparent = false;
lay::color_t *d = new color_t [w * h]; tl_assert ((stride % sizeof (lay::color_t)) == 0);
stride /= sizeof (lay::color_t);
lay::color_t *d = new lay::color_t [w * h];
lay::color_t *new_data = d;
if (data) { if (data) {
for (unsigned int i = 0; i < h; ++i) { for (unsigned int i = 0; i < h; ++i) {
for (unsigned int i = 0; i < h; ++i) { for (unsigned int j = 0; j < w; ++j) {
*d++ = *data++; *d++ = *data++;
} }
if (stride > w) { if (stride > w) {
@ -53,7 +62,7 @@ Image::Image (unsigned int w, unsigned int h, const lay::color_t *data, unsigned
} }
} }
m_data.reset (new ImageData (d, w * h)); m_data.reset (new ImageData (new_data, w * h));
} }
Image::Image () Image::Image ()
@ -211,4 +220,150 @@ Image::diff (const Image &other) const
return res; return res;
} }
// -----------------------------------------------------------------------------------------------------
// MonoImage implementation
static unsigned int
stride_from_width (unsigned int w)
{
// Qt needs 32bit-aligned data
return 4 * ((w + 31) / 32);
}
MonoImage::MonoImage (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));
}
MonoImage::MonoImage (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));
}
MonoImage::MonoImage ()
{
m_width = 0;
m_height = 0;
m_stride = 0;
}
MonoImage::MonoImage (const MonoImage &other)
{
operator= (other);
}
MonoImage::MonoImage (MonoImage &&other)
{
swap (other);
}
MonoImage::~MonoImage ()
{
// .. nothing yet ..
}
MonoImage &
MonoImage::operator= (const MonoImage &other)
{
if (this != &other) {
m_width = other.m_width;
m_height = other.m_height;
m_stride = other.m_stride;
m_data = other.m_data;
}
return *this;
}
MonoImage &
MonoImage::operator= (MonoImage &&other)
{
if (this != &other) {
swap (other);
}
return *this;
}
void
MonoImage::swap (MonoImage &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);
}
void
MonoImage::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 *
MonoImage::scan_line (unsigned int n)
{
tl_assert (n < m_height);
return m_data->data () + n * m_stride;
}
const uint8_t *
MonoImage::scan_line (unsigned int n) const
{
tl_assert (n < m_height);
return m_data->data () + n * m_stride;
}
uint8_t *
MonoImage::data ()
{
return m_data->data ();
}
const uint8_t *
MonoImage::data () const
{
return m_data->data ();
}
#if defined(HAVE_QT)
QImage
MonoImage::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;
}
#endif
} }

View File

@ -29,6 +29,7 @@
#include "tlCopyOnWrite.h" #include "tlCopyOnWrite.h"
#include <string.h> #include <string.h>
#include <cstdint>
#if defined(HAVE_QT) #if defined(HAVE_QT)
# include <QImage> # include <QImage>
@ -38,7 +39,7 @@ namespace lay
{ {
/** /**
* @brief An 32bit RGBA image class * @brief An 32bit RGB/RGBA image class
* *
* This class substitutes QImage in Qt-less applications. * This class substitutes QImage in Qt-less applications.
* It provides 32bit RGBA pixels with the format used by lay::Color. * It provides 32bit RGBA pixels with the format used by lay::Color.
@ -131,6 +132,14 @@ public:
return m_height; return m_height;
} }
/**
* @brief Gets the image stride (number of bytes per row)
*/
unsigned int stride () const
{
return sizeof (lay::color_t) * m_width;
}
/** /**
* @brief Fills the image with the given color * @brief Fills the image with the given color
*/ */
@ -228,6 +237,171 @@ private:
tl::copy_on_write_ptr<ImageData> m_data; tl::copy_on_write_ptr<ImageData> m_data;
}; };
/**
* @brief An monochrome image class
*
* This class substitutes QImage for monochrome images in Qt-less applications.
*/
class LAYBASIC_PUBLIC MonoImage
{
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.
*/
MonoImage (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.
*/
MonoImage (unsigned int w, unsigned int h, const uint8_t *data = 0, unsigned int stride = 0);
/**
* @brief Default constructor
*/
MonoImage ();
/**
* @brief Copy constructor
*/
MonoImage (const MonoImage &other);
/**
* @brief Move constructor
*/
MonoImage (MonoImage &&other);
/**
* @brief Destructor
*/
~MonoImage ();
/**
* @brief Assignment
*/
MonoImage &operator= (const MonoImage &other);
/**
* @brief Move constructor
*/
MonoImage &operator= (MonoImage &&other);
/**
* @brief Swaps this image with another one
*/
void swap (MonoImage &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 QMonoImage object from the image
*/
QImage to_image () const;
#endif
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;
};
} }
#endif #endif

View File

@ -36,8 +36,8 @@ static bool compare_images (const QImage &qimg, const std::string &au)
qimg2.load (tl::to_qstring (au)); qimg2.load (tl::to_qstring (au));
if (qimg2.width () == (int) qimg.width () && qimg2.height () == (int) qimg.height ()) { if (qimg2.width () == (int) qimg.width () && qimg2.height () == (int) qimg.height ()) {
for (int i = 0; i < qimg.width (); ++i) { for (int j = 0; j < qimg.height (); ++j) {
for (int j = 0; j < qimg.height (); ++j) { for (int i = 0; i < qimg.width (); ++i) {
if (((const lay::color_t *) qimg.scanLine (j))[i] != ((const lay::color_t *) qimg2.scanLine (j))[i]) { if (((const lay::color_t *) qimg.scanLine (j))[i] != ((const lay::color_t *) qimg2.scanLine (j))[i]) {
return false; return false;
} }
@ -49,6 +49,58 @@ static bool compare_images (const QImage &qimg, const std::string &au)
} }
} }
static bool compare_images_mono (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 ()) {
// NOTE: slooooow ...
for (int j = 0; j < qimg.height (); ++j) {
for (int i = 0; i < qimg.width (); ++i) {
if ((qimg.scanLine (j)[i / 8] & (0x80 >> (i % 8))) != (qimg2.scanLine (j)[i / 8] & (0x80 >> (i % 8)))) {
return false;
}
}
}
return true;
} else {
return false;
}
}
static bool compare_images (const lay::Image &img, const lay::Image &img2)
{
if (img2.width () == img.width () && img2.height () == img.height ()) {
for (unsigned int j = 0; j < img.height (); ++j) {
for (unsigned int i = 0; i < img.width (); ++i) {
if (((const lay::color_t *) img.scan_line (j))[i] != ((const lay::color_t *) img2.scan_line (j))[i]) {
return false;
}
}
}
return true;
} else {
return false;
}
}
static bool compare_images (const lay::MonoImage &img, const lay::MonoImage &img2)
{
if (img2.width () == img.width () && img2.height () == img.height ()) {
for (unsigned int j = 0; j < img.height (); ++j) {
for (unsigned int i = 0; i < img.stride (); ++i) {
if (((const uint8_t *) img.scan_line (j))[i] != ((const uint8_t *) img2.scan_line (j))[i]) {
return false;
}
}
}
return true;
} else {
return false;
}
}
#endif #endif
TEST(1) TEST(1)
@ -56,6 +108,7 @@ TEST(1)
lay::Image img (15, 25); lay::Image img (15, 25);
EXPECT_EQ (img.width (), 15); EXPECT_EQ (img.width (), 15);
EXPECT_EQ (img.height (), 25); EXPECT_EQ (img.height (), 25);
EXPECT_EQ (img.stride (), 15 * sizeof (lay::color_t));
EXPECT_EQ (img.transparent (), false); EXPECT_EQ (img.transparent (), false);
img.set_transparent (true); img.set_transparent (true);
@ -85,6 +138,7 @@ TEST(1)
EXPECT_EQ (img.scan_line (5)[10], 0x332211); EXPECT_EQ (img.scan_line (5)[10], 0x332211);
img2 = img; img2 = img;
EXPECT_EQ (compare_images (img, img2), true);
EXPECT_EQ (img.scan_line (5)[10], 0x332211); EXPECT_EQ (img.scan_line (5)[10], 0x332211);
EXPECT_EQ (img2.scan_line (5)[10], 0x332211); EXPECT_EQ (img2.scan_line (5)[10], 0x332211);
@ -94,21 +148,25 @@ TEST(1)
EXPECT_EQ (img2.width (), 10); EXPECT_EQ (img2.width (), 10);
EXPECT_EQ (img2.height (), 16); EXPECT_EQ (img2.height (), 16);
img2.fill (0x010203); img2.fill (0x010203);
EXPECT_EQ (compare_images (img, img2), false);
EXPECT_EQ (img.scan_line (5)[10], 0x332211); EXPECT_EQ (img.scan_line (5)[10], 0x332211);
EXPECT_EQ (img2.scan_line (5)[8], 0x010203); EXPECT_EQ (img2.scan_line (5)[8], 0x010203);
img = std::move (img2); img = std::move (img2);
EXPECT_EQ (compare_images (img, img2), false);
EXPECT_EQ (img.width (), 10); EXPECT_EQ (img.width (), 10);
EXPECT_EQ (img.height (), 16); EXPECT_EQ (img.height (), 16);
EXPECT_EQ (img.scan_line (5)[8], 0x010203); EXPECT_EQ (img.scan_line (5)[8], 0x010203);
lay::Image img3 (img); lay::Image img3 (img);
EXPECT_EQ (compare_images (img, img3), true);
EXPECT_EQ (img3.width (), 10); EXPECT_EQ (img3.width (), 10);
EXPECT_EQ (img3.height (), 16); EXPECT_EQ (img3.height (), 16);
EXPECT_EQ (img3.scan_line (5)[8], 0x010203); EXPECT_EQ (img3.scan_line (5)[8], 0x010203);
img.fill (0x102030); img.fill (0x102030);
EXPECT_EQ (compare_images (img, img3), false);
EXPECT_EQ (img3.width (), 10); EXPECT_EQ (img3.width (), 10);
EXPECT_EQ (img3.height (), 16); EXPECT_EQ (img3.height (), 16);
EXPECT_EQ (img3.scan_line (5)[8], 0x010203); EXPECT_EQ (img3.scan_line (5)[8], 0x010203);
@ -120,6 +178,14 @@ TEST(1)
EXPECT_EQ (img4.width (), 10); EXPECT_EQ (img4.width (), 10);
EXPECT_EQ (img4.height (), 16); EXPECT_EQ (img4.height (), 16);
EXPECT_EQ (img4.scan_line (5)[8], 0x102030); EXPECT_EQ (img4.scan_line (5)[8], 0x102030);
// other constructors
EXPECT_EQ (compare_images (lay::Image (img4.width (), img4.height (), (const lay::color_t *) img4.data ()), img4), true);
EXPECT_EQ (compare_images (lay::Image (img4.width (), img4.height (), (const lay::color_t *) img4.data (), img4.stride ()), img4), true);
lay::color_t *dnew = new lay::color_t [ img4.width () * img4.height () * sizeof (lay::color_t) ];
memcpy (dnew, (const lay::color_t *) img4.data (), img4.width () * img4.height () * sizeof (lay::color_t));
EXPECT_EQ (compare_images (lay::Image (img4.width (), img4.height (), dnew), img4), true);
} }
#if defined(HAVE_QT) #if defined(HAVE_QT)
@ -260,3 +326,108 @@ TEST(3)
#endif #endif
} }
// Monochrome version
TEST(11)
{
lay::MonoImage 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::MonoImage 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::MonoImage (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::MonoImage 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::MonoImage 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::MonoImage (img4.width (), img4.height (), (const uint8_t *) img4.data ()), img4), true);
EXPECT_EQ (compare_images (lay::MonoImage (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::MonoImage (img4.width (), img4.height (), dnew), img4), true);
}
#if defined(HAVE_QT)
TEST(12)
{
lay::MonoImage 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.convertToFormat (QImage::Format_Mono), au), true);
}
#endif

View File

@ -153,7 +153,6 @@ public:
} }
tl::MutexLocker locker (&ms_lock); tl::MutexLocker locker (&ms_lock);
std::swap (mp_x, other.mp_x);
std::swap (mp_holder, other.mp_holder); std::swap (mp_holder, other.mp_holder);
} }
@ -250,7 +249,6 @@ public:
} }
private: private:
X *mp_x;
copy_on_write_holder<X> *mp_holder; copy_on_write_holder<X> *mp_holder;
void release () void release ()

BIN
testdata/lay/au_mono.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB