diff --git a/src/laybasic/laybasic/layLayoutCanvas.cc b/src/laybasic/laybasic/layLayoutCanvas.cc index 49ec4eb0d..ea9eb5e1c 100644 --- a/src/laybasic/laybasic/layLayoutCanvas.cc +++ b/src/laybasic/laybasic/layLayoutCanvas.cc @@ -566,7 +566,7 @@ LayoutCanvas::update_image () void LayoutCanvas::free_resources () { -#if defined(HAVE_QT) // @@@ +#if defined(HAVE_QT) if (mp_pixmap) { delete mp_pixmap; mp_pixmap = 0; @@ -574,7 +574,7 @@ LayoutCanvas::free_resources () #endif } -#if defined(HAVE_QT) // @@@ +#if defined(HAVE_QT) void LayoutCanvas::paintEvent (QPaintEvent *) { @@ -1006,7 +1006,7 @@ LayoutCanvas::screenshot () return img; } -#if defined(HAVE_QT) +#if defined(HAVE_QT) // @@@ void LayoutCanvas::resizeEvent (QResizeEvent *) { @@ -1090,7 +1090,7 @@ LayoutCanvas::do_update_image () update_image (); } -#if defined(HAVE_QT) // @@@ +#if defined(HAVE_QT) bool LayoutCanvas::event (QEvent *e) { diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index a8d8eed03..e427b4ef7 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -64,6 +64,9 @@ # include #endif +// Enable this if you have both Qt and libpng and want to use libpng for saving images: +// #define PREFER_LIBPNG_FOR_SAVE 1 + #include namespace lay @@ -2512,7 +2515,7 @@ png_texts (const lay::LayoutViewBase *view, const db::DBox &box) return texts; } -#if defined(HAVE_QT) +#if defined(HAVE_QT) && !defined(PREFER_LIBPNG_FOR_SAVE) void LayoutViewBase::save_screenshot (const std::string &fn) { @@ -2544,9 +2547,9 @@ LayoutViewBase::save_screenshot (const std::string &fn) tl::DeferredMethodScheduler::execute (); tl::OutputStream stream (fn); - // @@@ TODO: add texts - // @@@ mp_canvas->screenshot ().write_png (stream, png_texts (this, box ())); - mp_canvas->screenshot ().write_png (stream); + lay::PixelBuffer img = mp_canvas->screenshot (); + img.set_texts (png_texts (this, box ())); + img.write_png (stream); tl::log << "Saved screen shot to " << fn; } @@ -2596,7 +2599,7 @@ LayoutViewBase::get_image_with_options (unsigned int width, unsigned int height, lay::PixelBuffer LayoutViewBase::get_pixels_with_options (unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution, - lay::Color background, lay::Color foreground, lay::Color active, const db::DBox &target_box) + lay::Color background, lay::Color foreground, lay::Color active, const db::DBox &target_box) { tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Get image"))); @@ -2608,7 +2611,7 @@ LayoutViewBase::get_pixels_with_options (unsigned int width, unsigned int height lay::BitmapBuffer LayoutViewBase::get_pixels_with_options_mono (unsigned int width, unsigned int height, int linewidth, - lay::Color background, lay::Color foreground, lay::Color active, const db::DBox &target_box) + lay::Color background, lay::Color foreground, lay::Color active, const db::DBox &target_box) { tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Get image"))); @@ -2618,7 +2621,7 @@ LayoutViewBase::get_pixels_with_options_mono (unsigned int width, unsigned int h return mp_canvas->image_with_options_mono (width, height, linewidth, background, foreground, active, target_box); } -#if defined(HAVE_QT) +#if defined(HAVE_QT) && !defined(PREFER_LIBPNG_FOR_SAVE) void LayoutViewBase::save_image (const std::string &fn, unsigned int width, unsigned int height) { @@ -2648,19 +2651,21 @@ LayoutViewBase::save_image (const std::string &fn, unsigned int width, unsigned tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Save image"))); lay::Viewport vp (width, height, mp_canvas->viewport ().target_box ()); - std::vector > texts = png_texts (this, vp.box ()); // Execute all deferred methods - ensure there are no pending tasks tl::DeferredMethodScheduler::execute (); tl::OutputStream stream (fn); - mp_canvas->image (width, height).write_png (stream); + lay::PixelBuffer img = mp_canvas->image (width, height); + std::vector > texts = png_texts (this, vp.box ()); + img.set_texts (texts); + img.write_png (stream); tl::log << "Saved image to " << fn; } #endif -#if defined(HAVE_QT) +#if defined(HAVE_QT) && !defined(PREFER_LIBPNG_FOR_SAVE) void LayoutViewBase::save_image_with_options (const std::string &fn, unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution, @@ -2680,7 +2685,7 @@ LayoutViewBase::save_image_with_options (const std::string &fn, tl::DeferredMethodScheduler::execute (); if (monochrome) { - if (! writer.write (mp_canvas->image_with_options_mono (width, height, linewidth, background.to_mono (), foreground.to_mono (), active.to_mono (), target_box).to_image ())) { + if (! writer.write (mp_canvas->image_with_options_mono (width, height, linewidth, background, foreground, active, target_box).to_image ())) { throw tl::Exception (tl::to_string (QObject::tr ("Unable to write screenshot to file: %s (%s)")), fn, tl::to_string (writer.errorString ())); } } else { @@ -2707,9 +2712,17 @@ LayoutViewBase::save_image_with_options (const std::string &fn, tl::OutputStream stream (fn); if (monochrome) { - mp_canvas->image_with_options_mono (width, height, linewidth, background.to_mono (), foreground.to_mono (), active.to_mono (), target_box).write_png (stream); + + lay::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 { - mp_canvas->image_with_options (width, height, linewidth, oversampling, resolution, background, foreground, active, target_box).write_png (stream); + + lay::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); + } tl::log << "Saved image to " << fn; diff --git a/src/laybasic/laybasic/layPixelBuffer.cc b/src/laybasic/laybasic/layPixelBuffer.cc index 05aa56111..9a1320353 100644 --- a/src/laybasic/laybasic/layPixelBuffer.cc +++ b/src/laybasic/laybasic/layPixelBuffer.cc @@ -202,6 +202,7 @@ PixelBuffer::operator= (const PixelBuffer &other) m_height = other.m_height; m_data = other.m_data; m_transparent = other.m_transparent; + m_texts = other.m_texts; } return *this; } @@ -232,6 +233,7 @@ PixelBuffer::swap (PixelBuffer &other) 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 @@ -435,6 +437,15 @@ PixelBuffer::write_png (tl::OutputStream &output) const png_set_IHDR (png_ptr, info_ptr, width (), height (), bd, fmt, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + std::vector 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 (i->first.c_str ()); + tptrs.back ().text = const_cast (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) { @@ -546,6 +557,7 @@ BitmapBuffer::operator= (const BitmapBuffer &other) m_height = other.m_height; m_stride = other.m_stride; m_data = other.m_data; + m_texts = other.m_texts; } return *this; } @@ -570,6 +582,7 @@ BitmapBuffer::swap (BitmapBuffer &other) 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 @@ -708,6 +721,15 @@ BitmapBuffer::write_png (tl::OutputStream &output) const png_set_IHDR (png_ptr, info_ptr, width (), height (), bd, fmt, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + std::vector 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 (i->first.c_str ()); + tptrs.back ().text = const_cast (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) { diff --git a/src/laybasic/laybasic/layPixelBuffer.h b/src/laybasic/laybasic/layPixelBuffer.h index 1a095f7c0..943d445c0 100644 --- a/src/laybasic/laybasic/layPixelBuffer.h +++ b/src/laybasic/laybasic/layPixelBuffer.h @@ -258,6 +258,26 @@ public: */ PixelBuffer diff (const PixelBuffer &other) const; + /** + * @brief Gets the texts + * + * Texts are annotations which can be stored to PNG and back. + */ + const std::vector > &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 > &texts) + { + m_texts = texts; + } + private: class ImageData { @@ -301,6 +321,7 @@ private: unsigned int m_width, m_height; bool m_transparent; tl::copy_on_write_ptr m_data; + std::vector > m_texts; }; /** @@ -466,6 +487,26 @@ public: 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 > &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 > &texts) + { + m_texts = texts; + } + private: class MonoImageData { @@ -509,6 +550,7 @@ private: unsigned int m_width, m_height; unsigned int m_stride; tl::copy_on_write_ptr m_data; + std::vector > m_texts; }; } diff --git a/src/laybasic/unit_tests/layLayoutViewTests.cc b/src/laybasic/unit_tests/layLayoutViewTests.cc index 464954af7..806026609 100644 --- a/src/laybasic/unit_tests/layLayoutViewTests.cc +++ b/src/laybasic/unit_tests/layLayoutViewTests.cc @@ -236,3 +236,90 @@ TEST(13) EXPECT_EQ (compare_images (img, au_img), true); } #endif + +#if defined(HAVE_PNG) || defined(HAVE_QT) +TEST(21) +{ + lay::LayoutView lv (0, false, 0); + lv.cell_box_color (lay::Color (0, 0, 0)); + + lv.load_layout (tl::testsrc () + "/testdata/gds/t10.gds", true); + + std::string tmp = tmp_file ("test.png"); + lv.save_image_with_options (tmp, 500, 500, 1, 1, 1.0, lay::Color (255, 255, 255), lay::Color (0, 0, 0), lay::Color (128, 128, 128), db::DBox (), false); + + lay::PixelBuffer img; + { + tl::InputStream stream (tmp); + img = lay::PixelBuffer::read_png (stream); + } + tl::info << "PNG file read from " << tmp; + + std::string au = tl::testsrc () + "/testdata/lay/au_lv1.png"; + lay::PixelBuffer au_img; + { + tl::InputStream stream (au); + au_img = lay::PixelBuffer::read_png (stream); + } + tl::info << "PNG file read from " << au; + + EXPECT_EQ (compare_images (img, au_img), true); +} + +TEST(22) +{ + lay::LayoutView lv (0, false, 0); + lv.full_hier_new_cell (true); + + lv.load_layout (tl::testsrc () + "/testdata/gds/t10.gds", true); + + std::string tmp = tmp_file ("test.png"); + lv.save_image_with_options (tmp, 500, 500, 1, 1, 1.0, lay::Color (255, 255, 255), lay::Color (0, 0, 0), lay::Color (128, 128, 128), db::DBox (), false); + + lay::PixelBuffer img; + { + tl::InputStream stream (tmp); + img = lay::PixelBuffer::read_png (stream); + } + tl::info << "PNG file read from " << tmp; + + std::string au = tl::testsrc () + "/testdata/lay/au_lv2.png"; + lay::PixelBuffer au_img; + { + tl::InputStream stream (au); + au_img = lay::PixelBuffer::read_png (stream); + } + tl::info << "PNG file read from " << au; + + EXPECT_EQ (compare_images (img, au_img), true); +} + +// monochrome +TEST(23) +{ + lay::LayoutView lv (0, false, 0); + lv.full_hier_new_cell (true); + + lv.load_layout (tl::testsrc () + "/testdata/gds/t10.gds", true); + + std::string tmp = tmp_file ("test.png"); + lv.save_image_with_options (tmp, 500, 500, 1, 1, 1.0, lay::Color (255, 255, 255), lay::Color (0, 0, 0), lay::Color (128, 128, 128), db::DBox (), true); + + lay::BitmapBuffer img; + { + tl::InputStream stream (tmp); + img = lay::BitmapBuffer::read_png (stream); + } + tl::info << "PNG file read from " << tmp; + + std::string au = tl::testsrc () + "/testdata/lay/au_lv3.png"; + lay::BitmapBuffer au_img; + { + tl::InputStream stream (au); + au_img = lay::BitmapBuffer::read_png (stream); + } + tl::info << "PNG file read from " << au; + + EXPECT_EQ (compare_images (img, au_img), true); +} +#endif