add bleed edge option, get_card_export_settings function

This commit is contained in:
GenevensiS
2025-12-08 16:11:36 +01:00
parent a616dd007b
commit 2888dc4059
26 changed files with 663 additions and 475 deletions
+23 -5
View File
@@ -126,11 +126,29 @@ 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 String getExternalRectString(double scale = 1.0, int offset = 0) { ///< update the style before calling this
return _("---") + wxString::Format(wxT("%i"), (int)std::ceil(scale * left + offset)) +
_("-") + wxString::Format(wxT("%i"), (int)std::ceil(scale * top)) +
_("-") + wxString::Format(wxT("%i"), (int)std::floor(scale * width)) +
_("-") + wxString::Format(wxT("%i"), (int)std::floor(scale * 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
double x = left * scale, y = top * scale;
double w = width * scale, h = height * scale;
RealRect rect(x, y, w, h);
int degrees = 0;
if (is_rad0(angle)) {
} else if (is_rad180(angle)) {
rect = RealRect(img_width - x - w, img_height - y - h, w, h);
degrees = 180;
} else if (is_rad90(angle)) {
rect = RealRect(y, img_height - x - w, h, w);
degrees = 90;
} else if (is_rad270(angle)) {
rect = RealRect(img_width - y - h, x, h, w);
degrees = 270;
} else {
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) +
_("---");
}
+1 -1
View File
@@ -151,7 +151,7 @@ CardsOnClipboard::CardsOnClipboard(const SetP& set, const String id, const vecto
img = export_image(set, cards[0]);
}
else {
img = export_image(set, cards, true, 0, 1.0, 0.0);
img = export_image(set, cards);
}
String temp_path = wxFileName::CreateTempFileName(_("mse")) + _(".png");
img.SaveFile(temp_path, wxBITMAP_TYPE_PNG);
+3 -7
View File
@@ -88,15 +88,11 @@ FileFormatP mtg_editor_file_format();
// ----------------------------------------------------------------------------- : Other ways to export
/// Generate a wxBitmap of one or more cards
Bitmap export_bitmap(const SetP& set, const CardP& card, const double zoom = 1.0, const Radians angle_radians = 0.0);
Bitmap export_bitmap(const SetP& set, const vector<CardP>& cards, bool scale_to_lowest_dpi, int padding, const double zoom, const Radians angle_radians, vector<double>& scales_out, vector<int>& offsets_out);
/// Generate a wxImage of one or more cards
Image export_image(const SetP& set, const CardP& card, const double zoom = 1.0, const Radians angle_radians = 0.0);
Image export_image(const SetP& set, const vector<CardP>& cards, bool scale_to_lowest_dpi = false, int padding = 0, const double zoom = 1.0, const Radians angle_radians = 0.0);
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<CardP>& 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);
/// Export the image of one or more cards to a given filename, using the app's zoom and rotation settings
/// 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<CardP>& cards, const String& path, const String& filename_template, FilenameConflicts conflicts);
+238 -150
View File
@@ -20,157 +20,160 @@
#include <render/card/viewer.hpp>
#include <wx/filename.h>
// ----------------------------------------------------------------------------- : Card export
class UnzoomedDataViewer : public DataViewer {
class ZoomedUnrotatedDataViewer : public DataViewer {
public:
UnzoomedDataViewer();
UnzoomedDataViewer(double zoom, Radians angle);
virtual ~UnzoomedDataViewer() {};
Rotation getRotation() const override;
private:
ZoomedUnrotatedDataViewer(double zoom) : zoom(zoom) {};
virtual ~ZoomedUnrotatedDataViewer() {};
Rotation getRotation() const override;
private:
double zoom;
double angle;
bool declared_values;
};
UnzoomedDataViewer::UnzoomedDataViewer()
: zoom(1.0)
, angle(0.0)
, declared_values(false)
{}
UnzoomedDataViewer::UnzoomedDataViewer(const double zoom, const Radians angle = 0.0)
: zoom(zoom)
, angle(angle)
, declared_values(true)
{}
Rotation UnzoomedDataViewer::getRotation() const {
if (!stylesheet) stylesheet = set->stylesheet;
if (declared_values) {
return Rotation(angle, stylesheet->getCardRect(), zoom, 1.0, ROTATION_ATTACH_TOP_LEFT);
}
double export_zoom = settings.exportZoomSettingsFor(set->stylesheetFor(card));
bool use_viewer_rotation = !settings.stylesheetSettingsFor(set->stylesheetFor(card)).card_normal_export();
if (use_viewer_rotation) {
return Rotation(DataViewer::getRotation().getAngle(), stylesheet->getCardRect(), export_zoom, 1.0, ROTATION_ATTACH_TOP_LEFT);
} else {
return Rotation(angle, stylesheet->getCardRect(), export_zoom, 1.0, ROTATION_ATTACH_TOP_LEFT);
}
}
// ----------------------------------------------------------------------------- : wxBitmap export
Bitmap export_bitmap(const SetP& set, const CardP& card, const double zoom, const Radians angle_radians) {
if (!set) throw Error(_("no set"));
UnzoomedDataViewer viewer = UnzoomedDataViewer(zoom, angle_radians);
viewer.setSet(set);
viewer.setCard(card);
// size of cards
RealSize size = viewer.getRotation().getExternalSize();
// create bitmap & dc
Bitmap bitmap((int)size.width, (int)size.height);
if (!bitmap.Ok()) throw InternalError(_("Unable to create bitmap"));
wxMemoryDC dc;
dc.SelectObject(bitmap);
// draw
viewer.draw(dc);
dc.SelectObject(wxNullBitmap);
return bitmap;
}
Bitmap export_bitmap(const SetP& set, const vector<CardP>& cards, bool scale_to_lowest_dpi, int padding, const double zoom, const Radians angle_radians, vector<double>& scales_out, vector<int>& offsets_out) {
if (!set) throw Error(_("no set"));
vector<Bitmap> bitmaps;
int width = 0;
int height = 0;
double lowest_dpi = 1200.0;
if (scale_to_lowest_dpi) {
FOR_EACH(card, cards) {
lowest_dpi = min(lowest_dpi, set->stylesheetFor(card).card_dpi);
}
lowest_dpi = max(lowest_dpi, 150.0);
}
// Draw card bitmaps
FOR_EACH(card, cards) {
double scale = zoom;
if (scale_to_lowest_dpi) {
double dpi = max(set->stylesheetFor(card).card_dpi, 150.0);
scale *= lowest_dpi / dpi;
}
scales_out.push_back(scale);
UnzoomedDataViewer viewer = UnzoomedDataViewer(scale, angle_radians);
viewer.setSet(set);
viewer.setCard(card);
RealSize size = viewer.getRotation().getExternalSize();
Bitmap bitmap((int)size.width, (int)size.height);
if (!bitmap.Ok()) throw InternalError(_("Unable to create bitmap"));
wxMemoryDC bufferDC;
bufferDC.SelectObject(bitmap);
clearDC(bufferDC, *wxWHITE_BRUSH);
viewer.draw(bufferDC);
bufferDC.SelectObject(wxNullBitmap);
width += (int)size.width;
height = max(height, (int)size.height);
bitmaps.push_back(bitmap);
}
// Draw global bitmap
Bitmap global_bitmap(width + (bitmaps.size()-1) * padding, height);
if (!global_bitmap.Ok()) throw InternalError(_("Unable to create bitmap"));
wxMemoryDC globalDC;
globalDC.SelectObject(global_bitmap);
clearDC(globalDC, *wxWHITE_BRUSH);
int offset = 0;
FOR_EACH(bitmap, bitmaps) {
offsets_out.push_back(offset);
globalDC.SetDeviceOrigin(offset, 0);
globalDC.DrawBitmap(bitmap, 0, 0);
offset += bitmap.GetWidth() + padding;
}
globalDC.SelectObject(wxNullBitmap);
return global_bitmap;
Rotation ZoomedUnrotatedDataViewer::getRotation() const {
return Rotation(0.0, stylesheet->getCardRect(), zoom);
}
// ----------------------------------------------------------------------------- : wxImage export
Image export_image(const SetP& set, const CardP& card, const double zoom, const Radians angle_radians) {
Bitmap bitmap = export_bitmap(set, card, zoom, angle_radians);
Image img = bitmap.ConvertToImage();
String data = _("<mse-data-start>[");
IndexMap<FieldP, ValueP>& 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<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);
if (style) {
style->update(set->getContext(card));
std::string rect = style->getExternalRectString(zoom, 0).ToStdString();
cardv_data[field->name.ToStdString()] = rect;
Image export_image(const SetP& set, const CardP& card, const bool write_metadata, const double zoom, const Radians angle_radians, const double bleed_pixels) {
if (!set) throw Error(_("no set"));
/// create and zoom
ZoomedUnrotatedDataViewer viewer = ZoomedUnrotatedDataViewer(zoom);
viewer.setSet(set);
viewer.setCard(card);
RealSize size = viewer.getRotation().getExternalSize();
int bleed = lround(bleed_pixels);
Bitmap bitmap((int)size.width + 2 * bleed, (int)size.height + 2 * bleed);
if (!bitmap.Ok()) throw InternalError(_("Unable to create bitmap"));
wxMemoryDC dc;
dc.SelectObject(bitmap);
dc.SetDeviceOrigin(bleed, bleed);
viewer.draw(dc);
dc.SelectObject(wxNullBitmap);
Image img = bitmap.ConvertToImage();
/// rotate
img = rotate_image(img, angle_radians);
int width = img.GetWidth(), height = img.GetHeight();
/// add print bleed edge
if (size.width < 1.0 || size.height < 1.0) {
queue_message(MESSAGE_ERROR, _("Cannot add bleed edge to empty image"));
}
else {
if (!img.HasAlpha()) img.InitAlpha();
Byte* pixels = img.GetData();
Byte* alpha = img.GetAlpha();
// fill top left corner
int pixel;
int x_start = 0;
int y_start = 0;
int ref = bleed + bleed * width;
for (int y = 0; y < bleed; ++y) {
for (int x = 0; x < bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
}
data += json_ugly_print(cardv) + _("]<mse-data-end>");
img.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, data);
return img;
}
Image export_image(const SetP& set, const vector<CardP>& cards, bool scale_to_lowest_dpi, int padding, const double zoom, const Radians angle_radians) {
vector<double> scales;
vector<int> offsets;
Bitmap bitmap = export_bitmap(set, cards, scale_to_lowest_dpi, padding, zoom, angle_radians, scales, offsets);
Image img = bitmap.ConvertToImage();
String data = _("<mse-data-start>[");
for (int i = 0; i < cards.size(); ++i) {
if (i > 0) data += _(",");
CardP card = cards[i];
// fill top right corner
x_start = width - bleed;
y_start = 0;
ref = x_start - 1 + bleed * width;
for (int y = 0; y < bleed; ++y) {
for (int x = 0; x < bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill bottom left corner
x_start = 0;
y_start = height - bleed;
ref = bleed + (y_start - 1) * width;
for (int y = 0; y < bleed; ++y) {
for (int x = 0; x < bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill bottom right corner
x_start = width - bleed;
y_start = height - bleed;
ref = (x_start - 1) + (y_start - 1) * width;
for (int y = 0; y < bleed; ++y) {
for (int x = 0; x < bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill left border
x_start = 0;
y_start = bleed;
for (int y = 0; y < height - bleed - bleed; ++y) {
ref = bleed + (y_start + y) * width;
for (int x = 0; x < bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill top border
x_start = bleed;
y_start = 0;
for (int y = 0; y < bleed; ++y) {
for (int x = 0; x < width - bleed - bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
ref = x_start + x + bleed * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill right border
x_start = width - bleed;
y_start = bleed;
for (int y = 0; y < height - bleed - bleed; ++y) {
ref = width - bleed - 1 + (y_start + y) * width;
for (int x = 0; x < bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill bottom border
x_start = bleed;
y_start = height - bleed;
for (int y = 0; y < bleed; ++y) {
for (int x = 0; x < width - bleed - bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
ref = x_start + x + (height - bleed - 1) * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
}
/// add metadata
if (write_metadata) {
String metadata = _("<mse-card-data>[");
IndexMap<FieldP, ValueP>& card_data = card->data;
boost::json::object& cardv = mse_to_json(card, set.get());
boost::json::object& cardv_data = cardv["data"].as_object();
@@ -183,24 +186,109 @@ Image export_image(const SetP& set, const vector<CardP>& cards, bool scale_to_lo
StyleP style = stylesheet->card_style.at(field->index);
if (style) {
style->update(set->getContext(card));
std::string rect = style->getExternalRectString(scales[i], offsets[i]).ToStdString();
std::string rect = style->getExternalRectString(zoom, angle_radians, bleed_pixels, width, height, 0).ToStdString();
cardv_data[field->name.ToStdString()] = rect;
}
}
}
data += json_ugly_print(cardv);
}
data += _("]<mse-data-end>");
img.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, data);
metadata += json_ugly_print(cardv) + _("]</mse-card-data>");
img.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, metadata);
}
return img;
}
Image export_image( const SetP& set, const vector<CardP>& cards,
const int padding,
const double global_zoom,
const bool use_zoom_setting,
const bool use_rotation_setting,
const bool use_bleed_setting) {
if (!set) throw Error(_("no set"));
if (cards.size() == 0) throw Error(_("no cards"));
vector<Image> imgs;
vector<int> offsets;
vector<double> zooms;
vector<double> angles;
vector<double> bleeds;
// Draw card images
FOR_EACH(card, cards) {
Settings::ExportSettings card_settings = settings.exportSettingsFor(set->stylesheetFor(card));
double zoom = use_zoom_setting ? global_zoom * card_settings.zoom : global_zoom;
double angle = use_rotation_setting ? card_settings.angle_radians : 0.0;
double bleed = use_bleed_setting ? card_settings.bleed_pixels : 0.0;
imgs.push_back(export_image(set, card, false, zoom, angle, bleed));
zooms.push_back(zoom);
angles.push_back(angle);
bleeds.push_back(bleed);
}
int global_width = 0;
int global_height = 0;
vector<int> widths;
vector<int> heights;
FOR_EACH(img, imgs) {
int width = img.GetWidth();
int height = img.GetHeight();
widths.push_back(width);
heights.push_back(height);
offsets.push_back(global_width);
global_width += padding + width;
global_height = max(global_height, height);
}
global_width -= padding;
// Draw global image
Image global_img = Image(global_width, global_height);
if (!global_img.Ok()) throw InternalError(_("Unable to create image"));
global_img.InitAlpha();
Byte* pixels = global_img.GetData();
Byte* alpha = global_img.GetAlpha();
// fill with transparent
for (UInt i = 0; i < global_width*global_height; ++i) {
pixels[3 * i + 0] = 0;
pixels[3 * i + 1] = 0;
pixels[3 * i + 2] = 0;
alpha[i] = 0;
}
// Paste card images
FOR_EACH_2(img, imgs, offset, offsets) {
global_img.Paste(img, offset, 0);
}
// Write metadata
String metadata = _("<mse-card-data>[");
for (int i = 0; i < cards.size(); ++i) {
if (i > 0) metadata += _(",");
CardP card = cards[i];
IndexMap<FieldP, ValueP>& 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<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);
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;
}
}
}
metadata += json_ugly_print(cardv);
}
metadata += _("]</mse-card-data>");
global_img.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, metadata);
return global_img;
}
void export_image(const SetP& set, const CardP& card, const String& filename) {
const StyleSheet& stylesheet = set->stylesheetFor(card);
StyleSheetSettings& stylesheet_settings = settings.stylesheetSettingsFor(stylesheet);
double zoom = settings.exportZoomSettingsFor(stylesheet);
Radians angle = stylesheet_settings.card_normal_export() ? 0.0 : deg_to_rad(stylesheet_settings.card_angle());
Image img = export_image(set, card, zoom, angle);
StyleSheetSettings& stylesheet_settings = settings.stylesheetSettingsFor(stylesheet);
Settings::ExportSettings export_settings = settings.exportSettingsFor(stylesheet);
Image img = export_image(set, card, true, export_settings.zoom, export_settings.angle_radians, export_settings.bleed_pixels);
img.SaveFile(filename);
}
+75 -62
View File
@@ -32,8 +32,8 @@ IMPLEMENT_REFLECTION_ENUM(CheckUpdates) {
}
IMPLEMENT_REFLECTION_ENUM(InstallType) {
VALUE_N("default", INSTALL_DEFAULT); //default
VALUE_N("local", INSTALL_LOCAL);
VALUE_N("default", INSTALL_DEFAULT); //default
VALUE_N("local", INSTALL_LOCAL);
VALUE_N("global", INSTALL_GLOBAL);
}
@@ -47,13 +47,13 @@ bool is_install_local(InstallType type) {
}
IMPLEMENT_REFLECTION_ENUM(FilenameConflicts) {
VALUE_N("keep old", CONFLICT_KEEP_OLD);
VALUE_N("overwrite", CONFLICT_OVERWRITE);
VALUE_N("number", CONFLICT_NUMBER);
VALUE_N("number overwrite", CONFLICT_NUMBER_OVERWRITE);
VALUE_N("keep old", CONFLICT_KEEP_OLD);
VALUE_N("overwrite", CONFLICT_OVERWRITE);
VALUE_N("number", CONFLICT_NUMBER);
VALUE_N("number overwrite", CONFLICT_NUMBER_OVERWRITE);
}
const vector<int> Settings::export_zoom_choices = { 50,66,75,80,100,120,125,150,175,200 };
const vector<int> Settings::scale_choices = { 50,66,75,80,100,120,125,150,175,200 };
const int COLUMN_NOT_INITIALIZED = -100000;
@@ -71,13 +71,13 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(ColumnSettings) {
}
GameSettings::GameSettings()
: sort_cards_ascending(true)
, images_export_filename(_("{card.name}.png"))
, images_export_conflicts(CONFLICT_NUMBER_OVERWRITE)
, use_auto_replace(true)
, pack_seed_random(true)
, pack_seed(123456)
, initialized(false)
: sort_cards_ascending (true)
, images_export_filename (_("{card.name}.png"))
, images_export_conflicts (CONFLICT_NUMBER_OVERWRITE)
, use_auto_replace (true)
, pack_seed_random (true)
, pack_seed (123456)
, initialized (false)
{}
void GameSettings::initDefaults(const Game& game) {
@@ -128,37 +128,40 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(GameSettings) {
StyleSheetSettings::StyleSheetSettings()
: card_zoom (1.0, true)
, export_zoom_selection (0, true)
, card_angle (0, true)
, card_anti_alias (true, true)
, card_borders (true, true)
, card_draw_editing (true, true)
, card_normal_export (true, true)
, card_notes_export (false, true)
, card_spellcheck_enabled(true, true)
: card_zoom (1.0, true)
, export_scale_selection (0, true)
, card_angle (0, true)
, card_anti_alias (true, true)
, card_borders (true, true)
, card_draw_editing (true, true)
, card_normal_export (true, true)
, card_bleed_export (false, true)
, card_notes_export (false, true)
, card_spellcheck_enabled (true, true)
{}
void StyleSheetSettings::useDefault(const StyleSheetSettings& ss) {
if (card_zoom .isDefault()) card_zoom .assignDefault(ss.card_zoom);
if (export_zoom_selection .isDefault()) export_zoom_selection .assignDefault(ss.export_zoom_selection);
if (export_scale_selection .isDefault()) export_scale_selection .assignDefault(ss.export_scale_selection);
if (card_angle .isDefault()) card_angle .assignDefault(ss.card_angle);
if (card_anti_alias .isDefault()) card_anti_alias .assignDefault(ss.card_anti_alias);
if (card_borders .isDefault()) card_borders .assignDefault(ss.card_borders);
if (card_draw_editing .isDefault()) card_draw_editing .assignDefault(ss.card_draw_editing);
if (card_normal_export .isDefault()) card_normal_export .assignDefault(ss.card_normal_export);
if (card_bleed_export .isDefault()) card_bleed_export .assignDefault(ss.card_bleed_export);
if (card_notes_export .isDefault()) card_notes_export .assignDefault(ss.card_notes_export);
if (card_spellcheck_enabled.isDefault()) card_spellcheck_enabled.assignDefault(ss.card_spellcheck_enabled);
}
IMPLEMENT_REFLECTION_NO_SCRIPT(StyleSheetSettings) {
REFLECT(card_zoom);
REFLECT(export_zoom_selection);
REFLECT(export_scale_selection);
REFLECT(card_angle);
REFLECT(card_anti_alias);
REFLECT(card_borders);
REFLECT(card_draw_editing);
REFLECT(card_normal_export);
REFLECT(card_bleed_export);
REFLECT(card_notes_export);
REFLECT(card_spellcheck_enabled);
}
@@ -184,29 +187,29 @@ IMPLEMENT_REFLECTION_ENUM(DarkModeType) {
Settings settings;
Settings::Settings()
: locale (_("en"))
, set_window_maximized (false)
, set_window_width (790)
, set_window_height (300)
, card_notes_height (40)
, open_sets_in_new_window (true)
, symbol_grid_size (30)
, symbol_grid (true)
, symbol_grid_snap (false)
, print_spacing (0.33)
, print_cutter_lines (CUTTER_ALL)
, dark_mode_type (DARKMODE_SYSTEM)
, internal_scale_selection(0)
, internal_image_extension(true)
: locale (_("en"))
, set_window_maximized (false)
, set_window_width (790)
, set_window_height (300)
, card_notes_height (40)
, open_sets_in_new_window (true)
, symbol_grid_size (30)
, symbol_grid (true)
, symbol_grid_snap (false)
, print_spacing (0.33)
, print_cutter_lines (CUTTER_ALL)
, dark_mode_type (DARKMODE_SYSTEM)
, import_scale_selection (0)
, internal_image_extension (true)
#if USE_OLD_STYLE_UPDATE_CHECKER
, updates_url (_("https://magicseteditor.boards.net/page/downloads"))
, updates_url (_("https://magicseteditor.boards.net/page/downloads"))
#endif
, package_versions_url (_("https://magicseteditor.boards.net/page/downloads"))
, installer_list_url (_("https://magicseteditor.boards.net/page/downloads"))
, check_updates (CHECK_IF_CONNECTED)
, check_updates_all (true)
, website_url (_("https://magicseteditor.boards.net/"))
, install_type (INSTALL_DEFAULT)
, package_versions_url (_("https://magicseteditor.boards.net/page/downloads"))
, installer_list_url (_("https://magicseteditor.boards.net/page/downloads"))
, check_updates (CHECK_IF_CONNECTED)
, check_updates_all (true)
, website_url (_("https://magicseteditor.boards.net/"))
, install_type (INSTALL_DEFAULT)
{}
void Settings::addRecentFile(const String& filename) {
@@ -230,7 +233,8 @@ GameSettings& Settings::gameSettingsFor(const Game& game) {
if (!gs) gs = make_intrusive<GameSettings>();
gs->initDefaults(game);
return *gs;
}
}
ColumnSettings& Settings::columnSettingsFor(const Game& game, const Field& field) {
// Get game info
GameSettings& gs = gameSettingsFor(game);
@@ -243,7 +247,8 @@ ColumnSettings& Settings::columnSettingsFor(const Game& game, const Field& field
cs.width = field.card_list_width;
}
return cs;
}
}
StyleSheetSettings& Settings::stylesheetSettingsFor(const StyleSheet& stylesheet) {
// Use the canonical form here since the stylesheet name will be used as a stored key.
// This does introduce the possibility of collision if two stylesheets return the same value canonically, but I think that's just a necessary risk.
@@ -253,28 +258,36 @@ StyleSheetSettings& Settings::stylesheetSettingsFor(const StyleSheet& stylesheet
return *ss;
}
double Settings::exportZoomSettingsFor(const StyleSheet& stylesheet) {
double Settings::exportScaleSettingsFor(const StyleSheet& stylesheet) {
StyleSheetSettings& ss = stylesheetSettingsFor(stylesheet);
int export_zoom = ss.export_zoom_selection();
if (export_zoom == 0) return adaptiveZoomSettingsFor(stylesheet, 300.0, 50.0);
if (export_zoom == 1) return adaptiveZoomSettingsFor(stylesheet, 300.0, 1.0);
if (export_zoom == 2) return adaptiveZoomSettingsFor(stylesheet, 150.0, 1.0);
return export_zoom_choices[export_zoom - 3] / 100;
int export_scale = ss.export_scale_selection();
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;
}
double Settings::internalScaleSettingsFor(const StyleSheet& stylesheet) {
if (internal_scale_selection == 0) return exportZoomSettingsFor(stylesheet);
if (internal_scale_selection == 1) return adaptiveZoomSettingsFor(stylesheet, 300.0, 50.0);
if (internal_scale_selection == 2) return adaptiveZoomSettingsFor(stylesheet, 300.0, 1.0);
if (internal_scale_selection == 3) return adaptiveZoomSettingsFor(stylesheet, 150.0, 1.0);
return export_zoom_choices[internal_scale_selection - 4] / 100;
double Settings::importScaleSettingsFor(const StyleSheet& stylesheet) {
if (import_scale_selection == 0) return exportScaleSettingsFor(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;
}
double Settings::adaptiveZoomSettingsFor(const StyleSheet& stylesheet, double dpi_target, double dpi_leeway) {
double Settings::adaptiveScaleSettingsFor(const StyleSheet& stylesheet, double dpi_target, double dpi_leeway) {
if (abs(stylesheet.card_dpi - dpi_target) <= dpi_leeway) return 1.0;
return dpi_target / max(10.0, stylesheet.card_dpi);
}
Settings::ExportSettings Settings::exportSettingsFor(const StyleSheet& stylesheet) {
StyleSheetSettings& ss = stylesheetSettingsFor(stylesheet);
double zoom = settings.exportScaleSettingsFor(stylesheet);
double angle = ss.card_normal_export() ? 0.0 : deg_to_rad(ss.card_angle());
double bleed = ss.card_bleed_export() ? (stylesheet.card_dpi / 300.0) * 36.0 * zoom : 0.0; // 36 pixels of bleed on a 300 DPI print
return ExportSettings{zoom, angle, bleed};
}
IndexMap<FieldP,ValueP>& Settings::exportOptionsFor(const ExportTemplate& export_template) {
return export_options.get(export_template.name(), export_template.option_fields);
}
@@ -324,7 +337,7 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(Settings) {
REFLECT(print_cutter_lines);
REFLECT(dark_mode_type);
REFLECT(apprentice_location);
REFLECT(internal_scale_selection);
REFLECT(import_scale_selection);
REFLECT(internal_image_extension);
#if USE_OLD_STYLE_UPDATE_CHECKER
REFLECT(updates_url);
+19 -13
View File
@@ -98,12 +98,13 @@ public:
// Rendering/display settings
Defaultable<double> card_zoom;
Defaultable<int> export_zoom_selection;
Defaultable<int> export_scale_selection;
Defaultable<Degrees> card_angle;
Defaultable<bool> card_anti_alias;
Defaultable<bool> card_borders;
Defaultable<bool> card_draw_editing;
Defaultable<bool> card_normal_export;
Defaultable<bool> card_bleed_export;
Defaultable<bool> card_notes_export;
Defaultable<bool> card_spellcheck_enabled;
@@ -173,18 +174,23 @@ public:
String default_game;
// --------------------------------------------------- : Game/stylesheet specific
/// Get the settings object for a specific game
GameSettings& gameSettingsFor (const Game& game);
/// Get the settings for a column for a specific field in a game
ColumnSettings& columnSettingsFor (const Game& game, const Field& field);
/// Get the settings object for a specific stylesheet
StyleSheetSettings& stylesheetSettingsFor (const StyleSheet& stylesheet);
double exportZoomSettingsFor (const StyleSheet& stylesheet);
double internalScaleSettingsFor(const StyleSheet& stylesheet);
double adaptiveZoomSettingsFor (const StyleSheet& stylesheet, double target_dpi, double leeway_dpi);
struct ExportSettings {
double zoom, angle_radians, bleed_pixels;
};
static const vector<int> export_zoom_choices;
/// Get the settings object for a specific game
GameSettings& gameSettingsFor (const Game& game);
/// Get the settings for a column for a specific field in a game
ColumnSettings& columnSettingsFor (const Game& game, const Field& field);
/// Get the settings object for a specific stylesheet
StyleSheetSettings& stylesheetSettingsFor (const StyleSheet& stylesheet);
double exportScaleSettingsFor (const StyleSheet& stylesheet);
double importScaleSettingsFor (const StyleSheet& stylesheet);
double adaptiveScaleSettingsFor (const StyleSheet& stylesheet, double target_dpi, double leeway_dpi);
ExportSettings exportSettingsFor (const StyleSheet& stylesheet);
static const vector<int> scale_choices;
private:
map<String,GameSettingsP> game_settings;
@@ -221,7 +227,7 @@ public:
// --------------------------------------------------- : Internal settings
int internal_scale_selection;
int import_scale_selection;
bool internal_image_extension;
// --------------------------------------------------- : Update checking