mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
add store_in_metadata image style property
This commit is contained in:
+19
-19
@@ -103,15 +103,15 @@ public:
|
||||
Style(const FieldP&);
|
||||
virtual ~Style();
|
||||
|
||||
const FieldP fieldP; ///< Field this style is for, should have the right type!
|
||||
const FieldP fieldP; ///< Field this style is for, should have the right type!
|
||||
|
||||
int z_index; ///< Stacking of values of this field, higher = on top
|
||||
int tab_index; ///< Tab index in editor
|
||||
Scriptable<double> left, top; ///< Position of this field
|
||||
Scriptable<double> width, height; ///< Position of this field
|
||||
Scriptable<double> right, bottom; ///< Position of this field
|
||||
Scriptable<Degrees> angle; ///< Rotation of the box
|
||||
Scriptable<bool> visible; ///< Is this field visible?
|
||||
int z_index; ///< Stacking of values of this field, higher = on top
|
||||
int tab_index; ///< Tab index in editor
|
||||
Scriptable<double> left, top; ///< Position of this field
|
||||
Scriptable<double> right, bottom; ///< Position of this field
|
||||
Scriptable<double> width, height; ///< Size of this field
|
||||
Scriptable<Degrees> angle; ///< Rotation of the box
|
||||
Scriptable<bool> visible; ///< Is this field visible?
|
||||
CachedScriptableMask mask; ///< Mask image
|
||||
|
||||
enum AutomaticSide {
|
||||
@@ -123,10 +123,10 @@ public:
|
||||
} automatic_side : 8; ///< Which of (left, width, right) and (top, height, bottom) is determined automatically?
|
||||
bool content_dependent; ///< Does this style depend on content properties?
|
||||
|
||||
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 String getExternalRectString(double scale, Radians angle, double bleed, int img_width, int img_height, int img_offset) { ///< update the style before calling this
|
||||
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, double bleed, int img_width, int img_height, int img_offset) { ///< update the style before calling this
|
||||
double x = left * scale, y = top * scale;
|
||||
double w = width * scale, h = height * scale;
|
||||
RealRect rect(x, y, w, h);
|
||||
@@ -142,14 +142,14 @@ public:
|
||||
rect = RealRect(img_width - y - h, x, h, w);
|
||||
degrees = 270;
|
||||
} else {
|
||||
return _("");
|
||||
return "";
|
||||
}
|
||||
return _("---") + wxString::Format(wxT("%i"), (int)std::ceil( rect.x + bleed + img_offset)) +
|
||||
_("-") + wxString::Format(wxT("%i"), (int)std::ceil( rect.y + bleed)) +
|
||||
_("-") + wxString::Format(wxT("%i"), (int)std::floor(rect.width)) +
|
||||
_("-") + wxString::Format(wxT("%i"), (int)std::floor(rect.height)) +
|
||||
_("-") + wxString::Format(wxT("%i"), degrees) +
|
||||
_("---");
|
||||
return "<mse-crop-data>" + std::to_string((int)std::ceil (rect.x + bleed + img_offset)) +
|
||||
"-" + std::to_string((int)std::ceil (rect.y + bleed)) +
|
||||
"-" + std::to_string((int)std::floor(rect.width)) +
|
||||
"-" + std::to_string((int)std::floor(rect.height)) +
|
||||
"-" + std::to_string(degrees) +
|
||||
"</mse-crop-data>";
|
||||
}
|
||||
|
||||
/// Does this style have a non-zero size (or is it scripted)?
|
||||
|
||||
@@ -18,17 +18,18 @@ IMPLEMENT_REFLECTION(ImageField) {
|
||||
REFLECT_BASE(Field);
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : ImageStyle
|
||||
|
||||
IMPLEMENT_REFLECTION(ImageStyle) {
|
||||
REFLECT_BASE(Style);
|
||||
REFLECT_N("default", default_image);
|
||||
REFLECT(store_in_metadata);
|
||||
}
|
||||
|
||||
int ImageStyle::update(Context& ctx) {
|
||||
int changes = Style::update(ctx);
|
||||
changes |= default_image.update(ctx) * CHANGE_DEFAULT;
|
||||
changes |= store_in_metadata.update(ctx) * CHANGE_OTHER;
|
||||
return changes;
|
||||
}
|
||||
|
||||
|
||||
+27
-14
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <util/prec.hpp>
|
||||
#include <data/field.hpp>
|
||||
#include <data/set.hpp>
|
||||
#include <script/scriptable.hpp>
|
||||
#include <script/image.hpp>
|
||||
#include <util/io/package.hpp>
|
||||
@@ -30,19 +31,6 @@ public:
|
||||
DECLARE_FIELD_TYPE(Image);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : ImageStyle
|
||||
|
||||
/// The Style for a ImageField
|
||||
class ImageStyle : public Style {
|
||||
public:
|
||||
inline ImageStyle(const ImageFieldP& field) : Style(field) {}
|
||||
DECLARE_STYLE_TYPE(Image);
|
||||
|
||||
ScriptableImage default_image; ///< Placeholder
|
||||
|
||||
int update(Context&) override;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : ImageValue
|
||||
|
||||
/// The Value in a ImageField, i.e. an image
|
||||
@@ -50,8 +38,33 @@ class ImageValue : public Value {
|
||||
public:
|
||||
inline ImageValue(const ImageFieldP& field) : Value(field) {}
|
||||
DECLARE_VALUE_TYPE(Image, LocalFileName);
|
||||
|
||||
|
||||
ValueType filename; ///< Filename of the image (in the current package), or ""
|
||||
Age last_update; ///< When was the image last changed?
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : ImageStyle
|
||||
|
||||
/// The Style for an ImageField
|
||||
class ImageStyle : public Style {
|
||||
public:
|
||||
inline ImageStyle(const ImageFieldP& field) : Style(field), store_in_metadata(false) {}
|
||||
DECLARE_STYLE_TYPE(Image);
|
||||
|
||||
ScriptableImage default_image; ///< Placeholder image when the user hasn't set one.
|
||||
Scriptable<bool> 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));
|
||||
String temppath = wxFileName::CreateTempFileName(_("mse")) + _(".png");
|
||||
img.SaveFile(temppath);
|
||||
std::string s = "<mse-image-data>" + fileToUTF8(temppath.ToStdString()) + "</mse-image-data>";
|
||||
wxRemoveFile(temppath);
|
||||
wxRemoveFile(temppath.substr(0, temppath.size() - 4));
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make card games |
|
||||
//| Copyright: (C) Twan van Laarhoven and the other MSE developers |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
#pragma once
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <util/prec.hpp>
|
||||
#include <fstream>
|
||||
|
||||
// ----------------------------------------------------------------------------- : File to UTF8 Encoding
|
||||
|
||||
inline std::string fileToUTF8(const std::string& filepath) {
|
||||
// File to char
|
||||
std::ifstream file(filepath, std::ios::binary);
|
||||
file.unsetf(std::ios::skipws);
|
||||
std::vector<unsigned char> buffer = std::vector<unsigned char>(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
|
||||
int size = buffer.size();
|
||||
if (size < 2) {
|
||||
queue_message(MESSAGE_WARNING, _("File too small to encode"));
|
||||
return "";
|
||||
}
|
||||
// All bytes that have a highest bit of 0 are valid UTF8 characters, so:
|
||||
// Reset the highest bit of each byte, store these bits in additional bytes at the end
|
||||
const unsigned char highest_bit = 1 << 7;
|
||||
unsigned char added_byte = 0;
|
||||
for (int i = 0, b = 0 ; i < size ; ++i, ++b) {
|
||||
if (b == 7) { // Never set the highest bit of the added byte
|
||||
buffer.push_back(added_byte);
|
||||
b = 0;
|
||||
}
|
||||
unsigned char bit = 1 << b;
|
||||
if ((buffer[i] & highest_bit) != 0) { // The highest bit of the buffer is set
|
||||
buffer[i] &= ~highest_bit; // Reset the highest bit of the buffer
|
||||
added_byte |= bit; // Set the bit of the added byte
|
||||
} else {
|
||||
added_byte &= ~bit; // Reset the bit of the added byte
|
||||
}
|
||||
}
|
||||
buffer.push_back(added_byte);
|
||||
// Char to string
|
||||
return std::string(buffer.begin(), buffer.end());
|
||||
}
|
||||
|
||||
inline bool UTF8ToFile(const std::string& filepath, std::string& string) {
|
||||
// String to char
|
||||
std::vector<unsigned char> buffer(string.begin(), string.end());
|
||||
int size = buffer.size();
|
||||
if (size < 2) {
|
||||
queue_message(MESSAGE_WARNING, _("File too small to decode"));
|
||||
return false;
|
||||
}
|
||||
// Restore the highest bit of each byte
|
||||
size = (size * 7) / 8;
|
||||
const unsigned char highest_bit = 1 << 7;
|
||||
unsigned char added_byte = buffer[size];
|
||||
for (int i = 0, j = size, b = 0 ; i < size ; ++i, ++b) {
|
||||
if (b == 7) {
|
||||
++j;
|
||||
added_byte = buffer[j];
|
||||
b = 0;
|
||||
}
|
||||
unsigned char bit = 1 << b;
|
||||
if ((added_byte & bit) != 0) { // The bit of the added byte is set
|
||||
buffer[i] |= highest_bit; // Set the highest bit of the buffer
|
||||
}
|
||||
}
|
||||
buffer.resize(size);
|
||||
// Char to file
|
||||
std::ofstream file(filepath, std::ios::out|std::ios::binary);
|
||||
std::copy(buffer.cbegin(), buffer.cend(), std::ostream_iterator<unsigned char>(file));
|
||||
return true;
|
||||
}
|
||||
+28
-12
@@ -10,6 +10,7 @@
|
||||
#include <util/tagged_string.hpp>
|
||||
#include <data/format/formats.hpp>
|
||||
#include <data/format/clipboard.hpp>
|
||||
#include <data/format/file_to_text.h>
|
||||
#include <data/game.hpp>
|
||||
#include <data/field/image.hpp>
|
||||
#include <data/set.hpp>
|
||||
@@ -18,8 +19,7 @@
|
||||
#include <data/settings.hpp>
|
||||
#include <script/functions/json.hpp>
|
||||
#include <gui/util.hpp>
|
||||
#include <render/card/viewer.hpp>
|
||||
#include <wx/filename.h>
|
||||
#include <render/card/viewer.hpp>
|
||||
|
||||
class ZoomedUnrotatedDataViewer : public DataViewer {
|
||||
public:
|
||||
@@ -136,16 +136,25 @@ Image export_image(const SetP& set, const CardP& card, const bool write_metadata
|
||||
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"] = "";
|
||||
if (!settings.stylesheetSettingsFor(*stylesheet).card_notes_export()) cardv["notes"] = "";
|
||||
// iterate over all image fields
|
||||
for(IndexMap<FieldP, ValueP>::iterator it = card_data.begin() ; it != card_data.end() ; ++it) {
|
||||
ImageValue* value = dynamic_cast<ImageValue*>(it->get());
|
||||
if (value && !value->filename.empty()) {
|
||||
FieldP field = (*it)->fieldP;
|
||||
StyleP style = stylesheet->card_style.at(field->index);
|
||||
ImageStyle* style = dynamic_cast<ImageStyle*>(stylesheet->card_style.at(field->index).get());
|
||||
if (style) {
|
||||
style->update(set->getContext(card));
|
||||
std::string rect = style->getExternalRectString(zoom, angle_radians, bleed_pixels, width, height, 0).ToStdString();
|
||||
cardv_data[field->name.ToStdString()] = rect;
|
||||
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, width, height, 0);
|
||||
cardv_data[field->name.ToStdString()] = rect;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,13 +233,20 @@ Image export_image( const SetP& set, const vector<CardP>& cards,
|
||||
for(IndexMap<FieldP, ValueP>::iterator it = card_data.begin() ; it != card_data.end() ; ++it) {
|
||||
ImageValue* value = dynamic_cast<ImageValue*>(it->get());
|
||||
if (value && !value->filename.empty()) {
|
||||
FieldP field = (*it)->fieldP;
|
||||
StyleP style = stylesheet->card_style.at(field->index);
|
||||
FieldP field = (*it)->fieldP;
|
||||
ImageStyle* style = dynamic_cast<ImageStyle*>(stylesheet->card_style.at(field->index).get());
|
||||
if (style) {
|
||||
style->update(set->getContext(card));
|
||||
Rotation rotation();
|
||||
std::string rect = style->getExternalRectString(zooms[i], angles[i], bleeds[i], widths[i], heights[i], offsets[i]).ToStdString();
|
||||
cardv_data[field->name.ToStdString()] = rect;
|
||||
// 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], widths[i], heights[i], offsets[i]);
|
||||
cardv_data[field->name.ToStdString()] = rect;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ double Settings::exportScaleSettingsFor(const StyleSheet& stylesheet) {
|
||||
if (export_scale == 0) return adaptiveScaleSettingsFor(stylesheet, 300.0, 50.0);
|
||||
if (export_scale == 1) return adaptiveScaleSettingsFor(stylesheet, 300.0, 1.0);
|
||||
if (export_scale == 2) return adaptiveScaleSettingsFor(stylesheet, 150.0, 1.0);
|
||||
return scale_choices[export_scale - 3] / 100;
|
||||
return (double)scale_choices[export_scale - 3] / 100.0;
|
||||
}
|
||||
|
||||
double Settings::importScaleSettingsFor(const StyleSheet& stylesheet) {
|
||||
@@ -273,7 +273,7 @@ double Settings::importScaleSettingsFor(const StyleSheet& stylesheet) {
|
||||
if (import_scale_selection == 1) return adaptiveScaleSettingsFor(stylesheet, 300.0, 50.0);
|
||||
if (import_scale_selection == 2) return adaptiveScaleSettingsFor(stylesheet, 300.0, 1.0);
|
||||
if (import_scale_selection == 3) return adaptiveScaleSettingsFor(stylesheet, 150.0, 1.0);
|
||||
return scale_choices[import_scale_selection - 4] / 100;
|
||||
return (double)scale_choices[import_scale_selection - 4] / 100.0;
|
||||
}
|
||||
|
||||
double Settings::adaptiveScaleSettingsFor(const StyleSheet& stylesheet, double dpi_target, double dpi_leeway) {
|
||||
|
||||
Reference in New Issue
Block a user