mirror of https://github.com/KLayout/klayout.git
WIP
This commit is contained in:
parent
41cbef1e5a
commit
0198c7cd69
|
|
@ -566,7 +566,7 @@ LayoutCanvas::update_image ()
|
||||||
void
|
void
|
||||||
LayoutCanvas::free_resources ()
|
LayoutCanvas::free_resources ()
|
||||||
{
|
{
|
||||||
#if defined(HAVE_QT) // @@@
|
#if defined(HAVE_QT)
|
||||||
if (mp_pixmap) {
|
if (mp_pixmap) {
|
||||||
delete mp_pixmap;
|
delete mp_pixmap;
|
||||||
mp_pixmap = 0;
|
mp_pixmap = 0;
|
||||||
|
|
@ -574,7 +574,7 @@ LayoutCanvas::free_resources ()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HAVE_QT) // @@@
|
#if defined(HAVE_QT)
|
||||||
void
|
void
|
||||||
LayoutCanvas::paintEvent (QPaintEvent *)
|
LayoutCanvas::paintEvent (QPaintEvent *)
|
||||||
{
|
{
|
||||||
|
|
@ -1006,7 +1006,7 @@ LayoutCanvas::screenshot ()
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HAVE_QT)
|
#if defined(HAVE_QT) // @@@
|
||||||
void
|
void
|
||||||
LayoutCanvas::resizeEvent (QResizeEvent *)
|
LayoutCanvas::resizeEvent (QResizeEvent *)
|
||||||
{
|
{
|
||||||
|
|
@ -1090,7 +1090,7 @@ LayoutCanvas::do_update_image ()
|
||||||
update_image ();
|
update_image ();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HAVE_QT) // @@@
|
#if defined(HAVE_QT)
|
||||||
bool
|
bool
|
||||||
LayoutCanvas::event (QEvent *e)
|
LayoutCanvas::event (QEvent *e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,9 @@
|
||||||
# include <QImageWriter>
|
# include <QImageWriter>
|
||||||
#endif
|
#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 <limits>
|
#include <limits>
|
||||||
|
|
||||||
namespace lay
|
namespace lay
|
||||||
|
|
@ -2512,7 +2515,7 @@ png_texts (const lay::LayoutViewBase *view, const db::DBox &box)
|
||||||
return texts;
|
return texts;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HAVE_QT)
|
#if defined(HAVE_QT) && !defined(PREFER_LIBPNG_FOR_SAVE)
|
||||||
void
|
void
|
||||||
LayoutViewBase::save_screenshot (const std::string &fn)
|
LayoutViewBase::save_screenshot (const std::string &fn)
|
||||||
{
|
{
|
||||||
|
|
@ -2544,9 +2547,9 @@ LayoutViewBase::save_screenshot (const std::string &fn)
|
||||||
tl::DeferredMethodScheduler::execute ();
|
tl::DeferredMethodScheduler::execute ();
|
||||||
|
|
||||||
tl::OutputStream stream (fn);
|
tl::OutputStream stream (fn);
|
||||||
// @@@ TODO: add texts
|
lay::PixelBuffer img = mp_canvas->screenshot ();
|
||||||
// @@@ mp_canvas->screenshot ().write_png (stream, png_texts (this, box ()));
|
img.set_texts (png_texts (this, box ()));
|
||||||
mp_canvas->screenshot ().write_png (stream);
|
img.write_png (stream);
|
||||||
|
|
||||||
tl::log << "Saved screen shot to " << fn;
|
tl::log << "Saved screen shot to " << fn;
|
||||||
}
|
}
|
||||||
|
|
@ -2596,7 +2599,7 @@ LayoutViewBase::get_image_with_options (unsigned int width, unsigned int height,
|
||||||
|
|
||||||
lay::PixelBuffer
|
lay::PixelBuffer
|
||||||
LayoutViewBase::get_pixels_with_options (unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution,
|
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")));
|
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
|
lay::BitmapBuffer
|
||||||
LayoutViewBase::get_pixels_with_options_mono (unsigned int width, unsigned int height, int linewidth,
|
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")));
|
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);
|
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
|
void
|
||||||
LayoutViewBase::save_image (const std::string &fn, unsigned int width, unsigned int height)
|
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")));
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Save image")));
|
||||||
|
|
||||||
lay::Viewport vp (width, height, mp_canvas->viewport ().target_box ());
|
lay::Viewport vp (width, height, mp_canvas->viewport ().target_box ());
|
||||||
std::vector<std::pair<std::string, std::string> > texts = png_texts (this, vp.box ());
|
|
||||||
|
|
||||||
// Execute all deferred methods - ensure there are no pending tasks
|
// Execute all deferred methods - ensure there are no pending tasks
|
||||||
tl::DeferredMethodScheduler::execute ();
|
tl::DeferredMethodScheduler::execute ();
|
||||||
|
|
||||||
tl::OutputStream stream (fn);
|
tl::OutputStream stream (fn);
|
||||||
mp_canvas->image (width, height).write_png (stream);
|
lay::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);
|
||||||
|
|
||||||
tl::log << "Saved image to " << fn;
|
tl::log << "Saved image to " << fn;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAVE_QT)
|
#if defined(HAVE_QT) && !defined(PREFER_LIBPNG_FOR_SAVE)
|
||||||
void
|
void
|
||||||
LayoutViewBase::save_image_with_options (const std::string &fn,
|
LayoutViewBase::save_image_with_options (const std::string &fn,
|
||||||
unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution,
|
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 ();
|
tl::DeferredMethodScheduler::execute ();
|
||||||
|
|
||||||
if (monochrome) {
|
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 ()));
|
throw tl::Exception (tl::to_string (QObject::tr ("Unable to write screenshot to file: %s (%s)")), fn, tl::to_string (writer.errorString ()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2707,9 +2712,17 @@ LayoutViewBase::save_image_with_options (const std::string &fn,
|
||||||
|
|
||||||
tl::OutputStream stream (fn);
|
tl::OutputStream stream (fn);
|
||||||
if (monochrome) {
|
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 {
|
} 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;
|
tl::log << "Saved image to " << fn;
|
||||||
|
|
|
||||||
|
|
@ -202,6 +202,7 @@ PixelBuffer::operator= (const PixelBuffer &other)
|
||||||
m_height = other.m_height;
|
m_height = other.m_height;
|
||||||
m_data = other.m_data;
|
m_data = other.m_data;
|
||||||
m_transparent = other.m_transparent;
|
m_transparent = other.m_transparent;
|
||||||
|
m_texts = other.m_texts;
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -232,6 +233,7 @@ PixelBuffer::swap (PixelBuffer &other)
|
||||||
std::swap (m_height, other.m_height);
|
std::swap (m_height, other.m_height);
|
||||||
std::swap (m_transparent, other.m_transparent);
|
std::swap (m_transparent, other.m_transparent);
|
||||||
m_data.swap (other.m_data);
|
m_data.swap (other.m_data);
|
||||||
|
m_texts.swap (other.m_texts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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);
|
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);
|
png_write_info (png_ptr, info_ptr);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < height (); ++i) {
|
for (unsigned int i = 0; i < height (); ++i) {
|
||||||
|
|
@ -546,6 +557,7 @@ BitmapBuffer::operator= (const BitmapBuffer &other)
|
||||||
m_height = other.m_height;
|
m_height = other.m_height;
|
||||||
m_stride = other.m_stride;
|
m_stride = other.m_stride;
|
||||||
m_data = other.m_data;
|
m_data = other.m_data;
|
||||||
|
m_texts = other.m_texts;
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -570,6 +582,7 @@ BitmapBuffer::swap (BitmapBuffer &other)
|
||||||
std::swap (m_height, other.m_height);
|
std::swap (m_height, other.m_height);
|
||||||
std::swap (m_stride, other.m_stride);
|
std::swap (m_stride, other.m_stride);
|
||||||
m_data.swap (other.m_data);
|
m_data.swap (other.m_data);
|
||||||
|
m_texts.swap (other.m_texts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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);
|
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);
|
png_write_info (png_ptr, info_ptr);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < height (); ++i) {
|
for (unsigned int i = 0; i < height (); ++i) {
|
||||||
|
|
|
||||||
|
|
@ -258,6 +258,26 @@ public:
|
||||||
*/
|
*/
|
||||||
PixelBuffer diff (const PixelBuffer &other) const;
|
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:
|
private:
|
||||||
class ImageData
|
class ImageData
|
||||||
{
|
{
|
||||||
|
|
@ -301,6 +321,7 @@ private:
|
||||||
unsigned int m_width, m_height;
|
unsigned int m_width, m_height;
|
||||||
bool m_transparent;
|
bool m_transparent;
|
||||||
tl::copy_on_write_ptr<ImageData> m_data;
|
tl::copy_on_write_ptr<ImageData> m_data;
|
||||||
|
std::vector<std::pair<std::string, std::string> > m_texts;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -466,6 +487,26 @@ public:
|
||||||
void write_png (tl::OutputStream &output) const;
|
void write_png (tl::OutputStream &output) const;
|
||||||
#endif
|
#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:
|
private:
|
||||||
class MonoImageData
|
class MonoImageData
|
||||||
{
|
{
|
||||||
|
|
@ -509,6 +550,7 @@ private:
|
||||||
unsigned int m_width, m_height;
|
unsigned int m_width, m_height;
|
||||||
unsigned int m_stride;
|
unsigned int m_stride;
|
||||||
tl::copy_on_write_ptr<MonoImageData> m_data;
|
tl::copy_on_write_ptr<MonoImageData> m_data;
|
||||||
|
std::vector<std::pair<std::string, std::string> > m_texts;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -236,3 +236,90 @@ TEST(13)
|
||||||
EXPECT_EQ (compare_images (img, au_img), true);
|
EXPECT_EQ (compare_images (img, au_img), true);
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue