From 6b1c7488bf798b19e7dd2bb2a68525a1f934b9e7 Mon Sep 17 00:00:00 2001 From: GenevensiS <66968533+G-e-n-e-v-e-n-s-i-S@users.noreply.github.com> Date: Wed, 21 Jan 2026 18:33:27 +0100 Subject: [PATCH] ensure image script functions preserve metadata --- src/data/field.cpp | 4 +- src/data/field.hpp | 40 ++++++++-- src/data/field/image.hpp | 14 ++-- src/data/format/formats.hpp | 10 ++- src/data/format/image.cpp | 122 ++++++++++++++--------------- src/data/format/image_encoding.hpp | 99 ++++++++++++++--------- src/gfx/blend_image.cpp | 11 ++- src/gfx/combine_image.cpp | 6 +- src/gfx/generated_image.cpp | 52 +++++++++++- src/gfx/generated_image.hpp | 14 ++++ src/gfx/image_effects.cpp | 6 ++ src/gfx/resample_image.cpp | 8 ++ src/gfx/rotate_image.cpp | 21 +++-- src/gui/control/card_list.cpp | 79 +++++++++++-------- src/gui/control/card_list.hpp | 4 + src/script/functions/image.cpp | 4 +- src/util/io/package.hpp | 9 --- src/util/real_point.hpp | 43 +++++++++- 18 files changed, 365 insertions(+), 181 deletions(-) diff --git a/src/data/field.cpp b/src/data/field.cpp index 2c8819cf..077db053 100644 --- a/src/data/field.cpp +++ b/src/data/field.cpp @@ -180,13 +180,13 @@ int Style::update(Context& ctx) { else if (automatic_side & AUTO_BOTTOM) bottom = top + height; else {int tb = int(top + bottom); top = (tb - height) / 2; bottom = (tb + height) / 2; } // adjust rotation point - if (angle != 0 && (automatic_side & (AUTO_LEFT | AUTO_TOP))) { + if (!almost_equal(angle, 0.0) && (automatic_side & (AUTO_LEFT | AUTO_TOP))) { double s = sin(deg_to_rad(angle)), c = cos(deg_to_rad(angle)); if (automatic_side & AUTO_LEFT) { // attach right corner instead of left left = left + width * (1 - c); top = top + width * s; } - if (automatic_side & AUTO_TOP) { // attach botom corner instead of top + if (automatic_side & AUTO_TOP) { // attach bottom corner instead of top left = left - height * s; top = top + height * (1 - c); } diff --git a/src/data/field.hpp b/src/data/field.hpp index a89c275f..364a0b43 100644 --- a/src/data/field.hpp +++ b/src/data/field.hpp @@ -126,12 +126,40 @@ public: inline RealPoint getPos() const { return RealPoint(left, top); } inline RealSize getSize() const { return RealSize(width, height); } inline RealRect getExternalRect() const { return RealRect(left, top, width, height); } - inline std::string getExternalRectString(double scale, Radians angle, int offset_x, int offset_y, int img_width, int img_height) { ///< update the style before calling this - RealRect rect(left, top, width, height); - int degrees = lround(rad_to_deg(this->angle)); - return transformAndEncodeRectInString(rect, degrees, scale, angle, offset_x, offset_y, img_width, img_height); - } - + inline RealRect getCanonicalExternalRect() const { + if (almost_equal(angle, 90.0)) { + if (automatic_side & Style::AutomaticSide::AUTO_LEFT) { + if (automatic_side & Style::AutomaticSide::AUTO_TOP) return RealRect(left + width - height, top + height, height, width); //bottom right + else return RealRect(left + width, top, height, width); //top right + } + else { + if (automatic_side & Style::AutomaticSide::AUTO_TOP) return RealRect(left - height, top + height - width, height, width); //bottom left + else return RealRect(left, top - width, height, width); //top left + } + } + else if (almost_equal(angle, 270.0)) { + if (automatic_side & Style::AutomaticSide::AUTO_LEFT) { + if (automatic_side & Style::AutomaticSide::AUTO_TOP) return RealRect(left + width, top + height - width, height, width); //bottom right + else return RealRect(left + width - height, top - width, height, width); //top right + } + else { + if (automatic_side & Style::AutomaticSide::AUTO_TOP) return RealRect(left, top + height, height, width); //bottom left + else return RealRect(left - height, top, height, width); //top left + } + } + else if (almost_equal(angle, 180.0)) { + if (automatic_side & Style::AutomaticSide::AUTO_LEFT) { + if (automatic_side & Style::AutomaticSide::AUTO_TOP) return RealRect(left + width, top + height, width, height); //bottom right + else return RealRect(left + width, top - height, width, height); //top right + } + else { + if (automatic_side & Style::AutomaticSide::AUTO_TOP) return RealRect(left - width, top + height, width, height); //bottom left + else return RealRect(left - width, top - height, width, height); //top left + } + } + return getExternalRect(); + } + /// Does this style have a non-zero size (or is it scripted)? bool hasSize() const; diff --git a/src/data/field/image.hpp b/src/data/field/image.hpp index c72b6e84..e126000b 100644 --- a/src/data/field/image.hpp +++ b/src/data/field/image.hpp @@ -39,6 +39,13 @@ public: inline ImageValue(const ImageFieldP& field) : Value(field) {} DECLARE_VALUE_TYPE(Image, LocalFileName); + inline Image getImage(const SetP& set) { + auto imageInputStream = set->openIn(filename); + Image img(*imageInputStream, wxBITMAP_TYPE_PNG); + if (!img.IsOk()) throw ScriptError(_ERROR_2_("file not found", filename.toStringForKey(), set)); + return img; + } + ValueType filename; ///< Filename of the image (in the current package), or "" Age last_update; ///< When was the image last changed? }; @@ -55,11 +62,4 @@ public: Scriptable store_in_metadata; ///< Is the image stored in full in the metadata when exporting? int update(Context&) override; - - inline std::string getExternalImageString(const SetP& set, ImageValue* value) { ///< update the style before calling this - auto imageInputStream = set->openIn(value->filename); - Image img(*imageInputStream, wxBITMAP_TYPE_PNG); - if (!img.IsOk()) throw ScriptError(_ERROR_2_("file not found", value->filename.toStringForKey(), set)); - return encodeImageInString(img); - } }; diff --git a/src/data/format/formats.hpp b/src/data/format/formats.hpp index bb2a9b0b..d25b23fc 100644 --- a/src/data/format/formats.hpp +++ b/src/data/format/formats.hpp @@ -89,12 +89,18 @@ FileFormatP mtg_editor_file_format(); // ----------------------------------------------------------------------------- : Other ways to export /// Generate a wxImage of one or more cards -Image export_image(const SetP& set, const CardP& card, const bool write_metadata = true, const double zoom = 1.0, const Radians angle_radians = 0.0, const double bleed_pixels = 0.0); -Image export_image(const SetP& set, const vector& cards, const int padding = 2, const double global_zoom = 1.0, const bool use_zoom_setting = true, const bool use_rotation_setting = true, const bool use_bleed_setting = false); +Image export_image(const SetP& set, const CardP& card, bool write_metadata = true, double zoom = 1.0, Radians angle_radians = 0.0, double bleed_pixels = 0.0); +Image export_image(const SetP& set, const vector& cards, int padding = 2, double global_zoom = 1.0, bool use_zoom_setting = true, bool use_rotation_setting = true, bool use_bleed_setting = false); /// Export the image of one or more cards to a given filename, using the app's zoom, rotation and bleed settings, and including metadata void export_image(const SetP& set, const CardP& card, const String& filename); void export_image(const SetP& set, const vector& cards, const String& path, const String& filename_template, FilenameConflicts conflicts); + +/// Write the metadata for a card +// Assuming first the zoom is applied, then the rotation, then the offset. +// This means that width and height need to be already scaled by a factor of zoom, but not already rotated +// while offset_x and offset_y need to be already scaled and already rotated. +String export_metadata(const SetP& set, const CardP& card, double zoom, Radians angle_radians, int width, int height, double offset_x, double offset_y); /// Export a set to Magic Workstation format void export_mws(Window* parent, const SetP& set); diff --git a/src/data/format/image.cpp b/src/data/format/image.cpp index a151ff8c..6622cfba 100644 --- a/src/data/format/image.cpp +++ b/src/data/format/image.cpp @@ -35,7 +35,7 @@ Rotation ZoomedUnrotatedDataViewer::getRotation() const { // ----------------------------------------------------------------------------- : wxImage export -Image export_image(const SetP& set, const CardP& card, const bool write_metadata, const double zoom, const Radians angle_radians, const double bleed_pixels) { +Image export_image(const SetP& set, const CardP& card, bool write_metadata, double zoom, Radians angle_radians, double bleed_pixels) { if (!set) throw Error(_("no set")); /// create and zoom ZoomedUnrotatedDataViewer viewer = ZoomedUnrotatedDataViewer(zoom); @@ -129,47 +129,24 @@ Image export_image(const SetP& set, const CardP& card, const bool write_metadata } /// add metadata - if (write_metadata) { - String metadata = _("["); - IndexMap& card_data = card->data; - boost::json::object cardv = mse_to_json(card, set.get()); - boost::json::object& cardv_data = cardv["data"].as_object(); - StyleSheetP stylesheet = set->stylesheetForP(card); - if (!settings.stylesheetSettingsFor(*stylesheet).card_notes_export()) cardv["notes"] = ""; - // iterate over all image fields - for(IndexMap::iterator it = card_data.begin() ; it != card_data.end() ; ++it) { - ImageValue* value = dynamic_cast(it->get()); - if (value && !value->filename.empty()) { - FieldP field = (*it)->fieldP; - ImageStyle* style = dynamic_cast(stylesheet->card_style.at(field->index).get()); - if (style) { - style->update(set->getContext(card)); - // store the entire image in the metadata - if (style->store_in_metadata()) { - std::string bytes = style->getExternalImageString(set, value); - cardv_data[field->name.ToStdString()] = bytes; - } - // store only crop coordinates - else { - std::string rect = style->getExternalRectString(zoom, angle_radians, bleed_pixels, bleed_pixels, width, height); - cardv_data[field->name.ToStdString()] = rect; - } - } - } - } - metadata += json_ugly_print(cardv) + _("]"); + if (write_metadata) { + bool rotated = is_rad90(angle_radians) || is_rad270(angle_radians); // we stored width and height after rotation, but export_metadata expects them before rotation + String metadata = _("[") + + export_metadata(set, card, zoom, angle_radians, rotated ? height : width, rotated ? width : height, bleed_pixels, bleed_pixels) + + _("]"); img.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, metadata); } return img; } -Image export_image( const SetP& set, const vector& cards, - const int padding, - const double global_zoom, - const bool use_zoom_setting, - const bool use_rotation_setting, - const bool use_bleed_setting) { +Image export_image(const SetP& set, + const vector& cards, + int padding, + double global_zoom, + bool use_zoom_setting, + bool use_rotation_setting, + bool use_bleed_setting) { if (!set) throw Error(_("no set")); if (cards.size() == 0) throw Error(_("no cards")); vector imgs; @@ -224,32 +201,8 @@ Image export_image( const SetP& set, const vector& cards, for (int i = 0; i < cards.size(); ++i) { if (i > 0) metadata += _(","); CardP card = cards[i]; - IndexMap& card_data = card->data; - boost::json::object cardv = mse_to_json(card, set.get()); - boost::json::object& cardv_data = cardv["data"].as_object(); - StyleSheetP stylesheet = set->stylesheetForP(card); - if (!settings.stylesheetSettingsFor(*stylesheet).card_notes_export()) cardv["notes"] = ""; - for(IndexMap::iterator it = card_data.begin() ; it != card_data.end() ; ++it) { - ImageValue* value = dynamic_cast(it->get()); - if (value && !value->filename.empty()) { - FieldP field = (*it)->fieldP; - ImageStyle* style = dynamic_cast(stylesheet->card_style.at(field->index).get()); - if (style) { - style->update(set->getContext(card)); - // store the entire image in the metadata - if (style->store_in_metadata()) { - std::string bytes = style->getExternalImageString(set, value); - cardv_data[field->name.ToStdString()] = bytes; - } - // store only crop coordinates - else { - std::string rect = style->getExternalRectString(zooms[i], angles[i], bleeds[i] + offsets[i], bleeds[i], widths[i], heights[i]); - cardv_data[field->name.ToStdString()] = rect; - } - } - } - } - metadata += json_ugly_print(cardv); + bool rotated = is_rad90(angles[i]) || is_rad270(angles[i]); // we stored width and height after rotation, but export_metadata expects them before rotation + metadata += export_metadata(set, card, zooms[i], angles[i], rotated ? heights[i] : widths[i], rotated ? widths[i] : heights[i], bleeds[i] + offsets[i], bleeds[i]); } metadata += _("]"); global_img.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, metadata); @@ -265,8 +218,7 @@ void export_image(const SetP& set, const CardP& card, const String& filename) { img.SaveFile(filename); } -void export_image(const SetP& set, const vector& cards, - const String& path, const String& filename_template, FilenameConflicts conflicts) +void export_image(const SetP& set, const vector& cards, const String& path, const String& filename_template, FilenameConflicts conflicts) { wxBusyCursor busy; // Script @@ -289,4 +241,44 @@ void export_image(const SetP& set, const vector& cards, used.insert(filename); export_image(set, card, filename); } -} +} + +String export_metadata(const SetP& set, const CardP& card, double zoom, Radians angle_radians, int width, int height, double offset_x, double offset_y) +{ + IndexMap& card_data = card->data; + boost::json::object cardv = mse_to_json(card, set.get()); + boost::json::object& cardv_data = cardv["data"].as_object(); + StyleSheetP stylesheet = set->stylesheetForP(card); + if (!settings.stylesheetSettingsFor(*stylesheet).card_notes_export()) cardv["notes"] = ""; + RealRect bounds_rect = RealRect(0, 0, width, height); + int bounds_degrees = 0; + RealRect::rotate(bounds_rect, bounds_degrees, width, height, lround(rad_to_deg(angle_radians))); + RealRect::translate(bounds_rect, bounds_degrees, offset_x, offset_y); + cardv.emplace("bounds", encodeRectInStdString(bounds_rect, bounds_degrees)); + // iterate over all image fields + for (IndexMap::iterator it = card_data.begin(); it != card_data.end(); ++it) { + ImageValue* value = dynamic_cast(it->get()); + if (value && !value->filename.empty()) { + FieldP field = (*it)->fieldP; + ImageStyle* style = dynamic_cast(stylesheet->card_style.at(field->index).get()); + if (style) { + style->update(set->getContext(card)); + // store the entire image in the metadata + if (style->store_in_metadata()) { + Image img = value->getImage(set); + cardv_data[field->name.ToStdString()] = encodeImageInString(img); + } + // store only crop coordinates + else { + RealRect rect = style->getCanonicalExternalRect(); + int degrees = lround(style->angle()); + RealRect::scale(rect, degrees, zoom, zoom); + RealRect::rotate(rect, degrees, width, height, lround(rad_to_deg(angle_radians))); // width and height are already scaled + RealRect::translate(rect, degrees, offset_x, offset_y); // offset_x and offset_y are already scaled and rotated + cardv_data[field->name.ToStdString()] = encodeRectInStdString(rect, degrees); + } + } + } + } + return json_ugly_print(cardv); +} diff --git a/src/data/format/image_encoding.hpp b/src/data/format/image_encoding.hpp index eaed453a..b8c45fa9 100644 --- a/src/data/format/image_encoding.hpp +++ b/src/data/format/image_encoding.hpp @@ -9,13 +9,15 @@ // ----------------------------------------------------------------------------- : Includes #include +#include +#include #include #include // ----------------------------------------------------------------------------- : Crop Rect Encoding /// Encode a rect in a std::string -inline static std::string encodeRectInStdString(wxRect rect, int degrees) { +inline static std::string encodeRectInStdString(RealRect rect, int degrees) { return "" + std::to_string((int)std::ceil (rect.x)) + ";" + std::to_string((int)std::ceil (rect.y)) + ";" + std::to_string((int)std::floor(rect.width)) + @@ -25,7 +27,7 @@ inline static std::string encodeRectInStdString(wxRect rect, int degrees) { } /// Encode a rect in a wxString -inline static String encodeRectInWxString(wxRect rect, int degrees) { +inline static String encodeRectInWxString(RealRect rect, int degrees) { return _("") + wxString::Format(wxT("%i"), (int)std::ceil (rect.x)) + _(";") + wxString::Format(wxT("%i"), (int)std::ceil (rect.y)) + _(";") + wxString::Format(wxT("%i"), (int)std::floor(rect.width)) + @@ -35,7 +37,7 @@ inline static String encodeRectInWxString(wxRect rect, int degrees) { } /// Retreive a rect encoded in a string, return true if successful -inline static bool decodeRectFromString(const String& rectString, wxRect& rect_out, int& degrees_out) { +inline static bool decodeRectFromString(const String& rectString, RealRect& rect_out, int& degrees_out) { size_t start = rectString.find(_("")); if (start == String::npos) return false; size_t end = rectString.find(_(""), start + 15); @@ -73,44 +75,22 @@ inline static bool decodeRectFromString(const String& rectString, wxRect& rect_o if(!string.ToInt(°rees_out)) return false; - rect_out = wxRect(x, y, width, height); - return true; -} - -/// Apply a transformation to a rect, return true if successful -inline static bool transformEncodedRect(wxRect& rect, int& degrees, double scale, Radians angle, int offset_x, int offset_y, int img_width, int img_height) { - if (degrees != 0 && degrees != 90 && degrees != 180 && degrees != 270) return false; - rect = wxRect(rect.x * scale, rect.y * scale, rect.width * scale, rect.height * scale); - if (is_rad0(angle)) { - } else if (is_rad180(angle)) { - rect = wxRect(img_width - rect.x - rect.width, img_height - rect.y - rect.height, rect.width, rect.height); - degrees += 180; - } else if (is_rad90(angle)) { - rect = wxRect(rect.y, img_height - rect.x - rect.width, rect.height, rect.width); - degrees += 90; - } else if (is_rad270(angle)) { - rect = wxRect(img_width - rect.y - rect.height, rect.x, rect.height, rect.width); - degrees += 270; - } else { - return false; - } - rect = wxRect(rect.x + offset_x, rect.y + offset_y, rect.width, rect.height); - if (degrees >= 360) degrees -= 360; + rect_out = RealRect(x, y, width, height); return true; } /// Retreive a rect encoded in a string, apply a transformation, then encode it back -inline static String transformEncodedRect(const String& rectString, double scale, Radians angle, int offset_x, int offset_y, int img_width, int img_height) { - wxRect rect; +inline static String transformEncodedRect(const String& rectString, RectTransform transform, double param_x, double param_y, int mode) { + RealRect rect(0,0,0,0); int degrees; if (!decodeRectFromString(rectString, rect, degrees)) return _(""); - if (!transformEncodedRect(rect, degrees, scale, angle, offset_x, offset_y, img_width, img_height)) return _(""); + transform(rect, degrees, param_x, param_y, mode); return encodeRectInWxString(rect, degrees); } /// Retreive all rects encoded in a string, apply a transformation, then encode them back -inline static String transformAllEncodedRects(const String& rectString, double scale, Radians angle, int offset_x, int offset_y, int img_width, int img_height) { - wxRect rect; +inline static String transformAllEncodedRects(const String& rectString, RectTransform transform, double param_x, double param_y, int mode = 0) { + RealRect rect(0,0,0,0); int degrees; size_t start = rectString.find(_("")); if (start == String::npos) return rectString; @@ -121,19 +101,13 @@ inline static String transformAllEncodedRects(const String& rectString, double s end = rectString.find(_(""), start + 15); if (end == String::npos) return rectString; end += 16; - result = result + transformEncodedRect(rectString.substr(start, end - start), scale, angle, offset_x, offset_y, img_width, img_height); + result = result + transformEncodedRect(rectString.substr(start, end - start), transform, param_x, param_y, mode); start = rectString.find(_(""), end); } result = result + rectString.substr(end); return result; } -/// Apply a transformation to a rect, then encode it in a string -inline static std::string transformAndEncodeRectInString(wxRect rect, int degrees, double scale, Radians angle, int offset_x, int offset_y, int img_width, int img_height) { - if (!transformEncodedRect(rect, degrees, scale, angle, offset_x, offset_y, img_width, img_height)) return ""; - return encodeRectInStdString(rect, degrees); -} - // ----------------------------------------------------------------------------- : File to UTF8 Encoding /// Encode a file in a string @@ -227,3 +201,52 @@ inline static Image decodeImageFromString(const String& string) { wxRemoveFile(temppath.substr(0, temppath.size() - 4)); return img; } + +// ----------------------------------------------------------------------------- : Metadata manipulation + +inline static String metadata_merge(const Image& img1, const Image& img2, int offset_x1 = 0, int offset_y1 = 0, int offset_x2 = 0, int offset_y2 = 0) +{ + if (img1.HasOption(wxIMAGE_OPTION_PNG_DESCRIPTION)) { + String metadata1 = img1.GetOption(wxIMAGE_OPTION_PNG_DESCRIPTION); + if (offset_x1 != 0 || offset_y1 != 0) metadata1 = transformAllEncodedRects(metadata1, RealRect::translate, offset_x1, offset_y1); + if (img2.HasOption(wxIMAGE_OPTION_PNG_DESCRIPTION)) { + String metadata2 = img2.GetOption(wxIMAGE_OPTION_PNG_DESCRIPTION); + if (offset_x2 != 0 || offset_y2 != 0) metadata2 = transformAllEncodedRects(metadata2, RealRect::translate, offset_x2, offset_y2); + size_t end1 = metadata1.find(_("")); + size_t start2 = metadata2.find(_("")); + if (end1 != String::npos && start2 != String::npos && end1 > 0 && start2 + 16 < metadata2.size()) { + metadata1 = metadata1.substr(0, end1 - 1) + "," + metadata2.substr(start2 + 16); + } + } + return metadata1; + } + else if (img2.HasOption(wxIMAGE_OPTION_PNG_DESCRIPTION)) { + String metadata2 = img2.GetOption(wxIMAGE_OPTION_PNG_DESCRIPTION); + if (offset_x2 != 0 || offset_y2 != 0) metadata2 = transformAllEncodedRects(metadata2, RealRect::translate, offset_x2, offset_y2); + return metadata2; + } + return _(""); +} + +inline static boost::json::array metadata_to_json(const String& metadata) { + size_t start = metadata.find(_("")); + if (start == String::npos) return boost::json::array(); + size_t end = metadata.find(_(""), start + 15); + if (end == String::npos) return boost::json::array(); + String string = metadata.substr(start + 15, end - (start + 15)); + try { + boost::system::error_code ec; + boost::json::parse_options options; + options.allow_invalid_utf8 = true; + boost::json::value jv = boost::json::parse(string.ToStdString(), ec, {}, options); + if(ec || !jv.is_array()) { + queue_message(MESSAGE_ERROR, _ERROR_("json cant parse")); + return boost::json::array(); + } + return jv.as_array(); + } + catch (...) { + queue_message(MESSAGE_ERROR, _ERROR_("json cant parse")); + return boost::json::array(); + } +} diff --git a/src/gfx/blend_image.cpp b/src/gfx/blend_image.cpp index 73391076..0468cd72 100644 --- a/src/gfx/blend_image.cpp +++ b/src/gfx/blend_image.cpp @@ -7,6 +7,7 @@ // ----------------------------------------------------------------------------- : Includes #include +#include #include #include @@ -70,7 +71,10 @@ void linear_blend(Image& img1, const Image& img2, double x1,double y1, double x2 alpha2 += 1; } } - } + } + + //transfer metadata + img1.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, metadata_merge(img1, img2)); } // ----------------------------------------------------------------------------- : Mask Blend @@ -101,7 +105,10 @@ void mask_blend(Image& img1, const Image& img2, const Image& mask) { // use mask's red channel to blend alpha (all mask channels should be identical since it's grey scale) alpha1[i] = (alpha1[i] * dataM[i * 3] + alpha2[i] * (255 - dataM[i * 3])) / 255; } - } + } + + //transfer metadata + img1.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, metadata_merge(img1, img2)); } // ----------------------------------------------------------------------------- : Alpha diff --git a/src/gfx/combine_image.cpp b/src/gfx/combine_image.cpp index e0b38a09..de75e14c 100644 --- a/src/gfx/combine_image.cpp +++ b/src/gfx/combine_image.cpp @@ -7,6 +7,7 @@ // ----------------------------------------------------------------------------- : Includes #include +#include #include #include #include @@ -575,7 +576,10 @@ void combine_image(Image& a, const Image& b, ImageCombine combine) { DISPATCH(COMBINE_SMALLER_THAN_240); DISPATCH(COMBINE_SMALLER_THAN_245); DISPATCH(COMBINE_SMALLER_THAN_250); - } + } + + //transfer metadata + a.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, metadata_merge(a, b)); } void draw_combine_image(DC& dc, UInt x, UInt y, const Image& img, ImageCombine combine) { diff --git a/src/gfx/generated_image.cpp b/src/gfx/generated_image.cpp index 1ea0e535..504fffaa 100644 --- a/src/gfx/generated_image.cpp +++ b/src/gfx/generated_image.cpp @@ -13,6 +13,7 @@ #include #include #include +#include