mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-12 13:37:00 -04:00
Images are now cached as wxBitmap, not wxImage. This should improve performance.
Fixed some more corner cases of rotation+zoom. git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@630 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
+3
-3
@@ -120,7 +120,7 @@ template <> StyleP read_new<Style>(Reader&) {
|
|||||||
throw InternalError(_("IndexMap contains nullptr StyleP the application should have crashed already"));
|
throw InternalError(_("IndexMap contains nullptr StyleP the application should have crashed already"));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Style::update(Context& ctx) {
|
int Style::update(Context& ctx) {
|
||||||
bool changed =
|
bool changed =
|
||||||
left .update(ctx)
|
left .update(ctx)
|
||||||
| width .update(ctx)
|
| width .update(ctx)
|
||||||
@@ -196,8 +196,8 @@ void Style::removeListener(StyleListener* listener) {
|
|||||||
listeners.end()
|
listeners.end()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
void Style::tellListeners(bool already_prepared) {
|
void Style::tellListeners(int changes) {
|
||||||
FOR_EACH(l, listeners) l->onStyleChange(already_prepared);
|
FOR_EACH(l, listeners) l->onStyleChange(changes);
|
||||||
}
|
}
|
||||||
|
|
||||||
StyleListener::StyleListener(const StyleP& style)
|
StyleListener::StyleListener(const StyleP& style)
|
||||||
|
|||||||
+18
-6
@@ -119,9 +119,11 @@ class Style : public IntrusivePtrVirtualBase {
|
|||||||
/** thisP is a smart pointer to this */
|
/** thisP is a smart pointer to this */
|
||||||
virtual ValueViewerP makeEditor(DataEditor& parent, const StyleP& thisP) = 0;
|
virtual ValueViewerP makeEditor(DataEditor& parent, const StyleP& thisP) = 0;
|
||||||
|
|
||||||
/// Update scripted values of this style, return true if anything has changed.
|
/// Update scripted values of this style, return nonzero if anything has changed.
|
||||||
/** The caller should tellListeners() */
|
/** The caller should tellListeners()
|
||||||
virtual bool update(Context&);
|
* The result is a combination of StyleChange flags
|
||||||
|
*/
|
||||||
|
virtual int update(Context&);
|
||||||
/// Add the given dependency to the dependent_scripts list for the variables this style depends on
|
/// Add the given dependency to the dependent_scripts list for the variables this style depends on
|
||||||
/** Only use for things that need invalidate() */
|
/** Only use for things that need invalidate() */
|
||||||
virtual void initDependencies(Context&, const Dependency&) const;
|
virtual void initDependencies(Context&, const Dependency&) const;
|
||||||
@@ -140,7 +142,8 @@ class Style : public IntrusivePtrVirtualBase {
|
|||||||
/// Remove a StyleListener
|
/// Remove a StyleListener
|
||||||
void removeListener(StyleListener*);
|
void removeListener(StyleListener*);
|
||||||
/// Tell the StyleListeners that this style has changed
|
/// Tell the StyleListeners that this style has changed
|
||||||
void tellListeners(bool already_prepared);
|
/** change_info is a subset of StyleChange flags */
|
||||||
|
void tellListeners(int changes);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DECLARE_REFLECTION_VIRTUAL();
|
DECLARE_REFLECTION_VIRTUAL();
|
||||||
@@ -148,6 +151,15 @@ class Style : public IntrusivePtrVirtualBase {
|
|||||||
vector<StyleListener*> listeners;
|
vector<StyleListener*> listeners;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// What changed in a style update?
|
||||||
|
enum StyleChange
|
||||||
|
{ CHANGE_NONE = 0x00 // nothing changed
|
||||||
|
, CHANGE_OTHER = 0x01 // some other change (note: result of casting from bool)
|
||||||
|
, CHANGE_DEFAULT = 0x02 // only the 'default' state is affected
|
||||||
|
, CHANGE_MASK = 0x04 // a mask image changed, must be reloaded
|
||||||
|
, CHANGE_ALREADY_PREPARED = 0x80 // hint that the change was the result of a content property change, viewers are already prepared
|
||||||
|
};
|
||||||
|
|
||||||
void init_object(const FieldP&, StyleP&);
|
void init_object(const FieldP&, StyleP&);
|
||||||
inline const FieldP& get_key (const StyleP& s) { return s->fieldP; }
|
inline const FieldP& get_key (const StyleP& s) { return s->fieldP; }
|
||||||
inline const String& get_key_name(const StyleP& s) { return s->fieldP->name; }
|
inline const String& get_key_name(const StyleP& s) { return s->fieldP->name; }
|
||||||
@@ -168,8 +180,8 @@ class StyleListener : public IntrusivePtrVirtualBase {
|
|||||||
virtual ~StyleListener();
|
virtual ~StyleListener();
|
||||||
|
|
||||||
/// Called when a (scripted) property of the viewed style has changed
|
/// Called when a (scripted) property of the viewed style has changed
|
||||||
/** already_prepared indicates that this change happend after preparing text for content properties */
|
/** changes is a combination of StyleChange flags */
|
||||||
virtual void onStyleChange(bool already_prepared) {}
|
virtual void onStyleChange(int changes) {}
|
||||||
protected:
|
protected:
|
||||||
const StyleP styleP; ///< The style we are listening to
|
const StyleP styleP; ///< The style we are listening to
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -216,17 +216,17 @@ void ChoiceStyle::initImage() {
|
|||||||
script.addInstruction(I_RET);
|
script.addInstruction(I_RET);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChoiceStyle::update(Context& ctx) {
|
int ChoiceStyle::update(Context& ctx) {
|
||||||
// Don't update the choice images, leave that to invalidate()
|
// Don't update the choice images, leave that to invalidate()
|
||||||
bool change = Style ::update(ctx)
|
int change = Style ::update(ctx)
|
||||||
| font .update(ctx)
|
| font .update(ctx) * CHANGE_OTHER
|
||||||
| mask_filename.update(ctx);
|
| mask_filename.update(ctx) * CHANGE_MASK;
|
||||||
if (!choice_images_initialized) {
|
if (!choice_images_initialized) {
|
||||||
// we only want to do this once because it is rather slow, other updates are handled by dependencies
|
// we only want to do this once because it is rather slow, other updates are handled by dependencies
|
||||||
choice_images_initialized = true;
|
choice_images_initialized = true;
|
||||||
FOR_EACH(ci, choice_images) {
|
FOR_EACH(ci, choice_images) {
|
||||||
if (ci.second.update(ctx)) {
|
if (ci.second.update(ctx)) {
|
||||||
change = true;
|
change |= CHANGE_OTHER;
|
||||||
// TODO : remove this thumbnail
|
// TODO : remove this thumbnail
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,7 +253,7 @@ void ChoiceStyle::invalidate(Context& ctx) {
|
|||||||
thumbnails_status[i] = THUMB_CHANGED;
|
thumbnails_status[i] = THUMB_CHANGED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (change) tellListeners(false);
|
if (change) tellListeners(CHANGE_OTHER);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChoiceStyle::loadMask(Package& pkg) {
|
void ChoiceStyle::loadMask(Package& pkg) {
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ class ChoiceStyle : public Style {
|
|||||||
ChoicePopupStyle popup_style; ///< Style of popups/menus
|
ChoicePopupStyle popup_style; ///< Style of popups/menus
|
||||||
ChoiceRenderStyle render_style; ///< Style of rendering
|
ChoiceRenderStyle render_style; ///< Style of rendering
|
||||||
Font font; ///< Font for drawing text (when RENDER_TEXT)
|
Font font; ///< Font for drawing text (when RENDER_TEXT)
|
||||||
ScriptableImage image; ///< Image to draw (when RENDER_IMAGE)
|
CachedScriptableImage image; ///< Image to draw (when RENDER_IMAGE)
|
||||||
map<String,ScriptableImage> choice_images; ///< Images for the various choices (when RENDER_IMAGE)
|
map<String,ScriptableImage> choice_images; ///< Images for the various choices (when RENDER_IMAGE)
|
||||||
bool choice_images_initialized;
|
bool choice_images_initialized;
|
||||||
Scriptable<String> mask_filename; ///< Filename of an additional mask over the images
|
Scriptable<String> mask_filename; ///< Filename of an additional mask over the images
|
||||||
@@ -163,7 +163,7 @@ class ChoiceStyle : public Style {
|
|||||||
/// Initialize image from choice_images
|
/// Initialize image from choice_images
|
||||||
void initImage();
|
void initImage();
|
||||||
|
|
||||||
virtual bool update(Context&);
|
virtual int update(Context&);
|
||||||
virtual void initDependencies(Context&, const Dependency&) const;
|
virtual void initDependencies(Context&, const Dependency&) const;
|
||||||
virtual void invalidate(Context&);
|
virtual void invalidate(Context&);
|
||||||
|
|
||||||
|
|||||||
@@ -67,9 +67,9 @@ IMPLEMENT_REFLECTION(ColorStyle) {
|
|||||||
REFLECT_N("mask", mask_filename);
|
REFLECT_N("mask", mask_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ColorStyle::update(Context& ctx) {
|
int ColorStyle::update(Context& ctx) {
|
||||||
return Style ::update(ctx)
|
return Style ::update(ctx)
|
||||||
| mask_filename.update(ctx);
|
| mask_filename.update(ctx) * CHANGE_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : ColorValue
|
// ----------------------------------------------------------------------------- : ColorValue
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class ColorStyle : public Style {
|
|||||||
double bottom_width; ///< Width of the colored region on the bottom side
|
double bottom_width; ///< Width of the colored region on the bottom side
|
||||||
Scriptable<String> mask_filename; ///< Filename of an additional mask over the images
|
Scriptable<String> mask_filename; ///< Filename of an additional mask over the images
|
||||||
|
|
||||||
virtual bool update(Context&);
|
virtual int update(Context&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DECLARE_REFLECTION();
|
DECLARE_REFLECTION();
|
||||||
|
|||||||
@@ -25,14 +25,16 @@ IMPLEMENT_REFLECTION(ImageField) {
|
|||||||
|
|
||||||
IMPLEMENT_REFLECTION(ImageStyle) {
|
IMPLEMENT_REFLECTION(ImageStyle) {
|
||||||
REFLECT_BASE(Style);
|
REFLECT_BASE(Style);
|
||||||
|
REFLECT(angle);
|
||||||
REFLECT_N("mask", mask_filename);
|
REFLECT_N("mask", mask_filename);
|
||||||
REFLECT_N("default", default_image);
|
REFLECT_N("default", default_image);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImageStyle::update(Context& ctx) {
|
int ImageStyle::update(Context& ctx) {
|
||||||
return Style ::update(ctx)
|
return Style ::update(ctx)
|
||||||
| mask_filename.update(ctx)
|
| angle .update(ctx) * CHANGE_OTHER
|
||||||
| default_image.update(ctx);
|
| mask_filename.update(ctx) * CHANGE_MASK
|
||||||
|
| default_image.update(ctx) * CHANGE_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : ImageValue
|
// ----------------------------------------------------------------------------- : ImageValue
|
||||||
|
|||||||
@@ -38,10 +38,11 @@ class ImageStyle : public Style {
|
|||||||
inline ImageStyle(const ImageFieldP& field) : Style(field) {}
|
inline ImageStyle(const ImageFieldP& field) : Style(field) {}
|
||||||
DECLARE_STYLE_TYPE(Image);
|
DECLARE_STYLE_TYPE(Image);
|
||||||
|
|
||||||
|
Scriptable<int> angle; ///< Rotation of images
|
||||||
Scriptable<String> mask_filename; ///< Filename for a mask image
|
Scriptable<String> mask_filename; ///< Filename for a mask image
|
||||||
ScriptableImage default_image; ///< Placeholder
|
ScriptableImage default_image; ///< Placeholder
|
||||||
|
|
||||||
virtual bool update(Context&);
|
virtual int update(Context&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DECLARE_REFLECTION();
|
DECLARE_REFLECTION();
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ InfoStyle::InfoStyle(const InfoFieldP& field)
|
|||||||
, background_color(255,255,255)
|
, background_color(255,255,255)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool InfoStyle::update(Context& ctx) {
|
int InfoStyle::update(Context& ctx) {
|
||||||
return Style ::update(ctx)
|
return Style ::update(ctx)
|
||||||
| font .update(ctx);
|
| font .update(ctx) * CHANGE_OTHER;
|
||||||
}
|
}
|
||||||
void InfoStyle::initDependencies(Context& ctx, const Dependency& dep) const {
|
void InfoStyle::initDependencies(Context& ctx, const Dependency& dep) const {
|
||||||
Style ::initDependencies(ctx, dep);
|
Style ::initDependencies(ctx, dep);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class InfoStyle : public Style {
|
|||||||
double padding_top, padding_bottom;
|
double padding_top, padding_bottom;
|
||||||
Color background_color;
|
Color background_color;
|
||||||
|
|
||||||
virtual bool update(Context&);
|
virtual int update(Context&);
|
||||||
virtual void initDependencies(Context&, const Dependency&) const;
|
virtual void initDependencies(Context&, const Dependency&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -68,12 +68,12 @@ double TextStyle::getStretch() const {
|
|||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextStyle::update(Context& ctx) {
|
int TextStyle::update(Context& ctx) {
|
||||||
return Style ::update(ctx)
|
return Style ::update(ctx)
|
||||||
| font .update(ctx)
|
| font .update(ctx) * CHANGE_OTHER
|
||||||
| symbol_font.update(ctx)
|
| symbol_font.update(ctx) * CHANGE_OTHER
|
||||||
| alignment .update(ctx)
|
| alignment .update(ctx) * CHANGE_OTHER
|
||||||
| angle .update(ctx);
|
| angle .update(ctx) * CHANGE_OTHER;
|
||||||
}
|
}
|
||||||
void TextStyle::initDependencies(Context& ctx, const Dependency& dep) const {
|
void TextStyle::initDependencies(Context& ctx, const Dependency& dep) const {
|
||||||
Style ::initDependencies(ctx, dep);
|
Style ::initDependencies(ctx, dep);
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class TextStyle : public Style {
|
|||||||
double content_width, content_height; ///< Size of the rendered text
|
double content_width, content_height; ///< Size of the rendered text
|
||||||
int content_lines; ///< Number of rendered lines
|
int content_lines; ///< Number of rendered lines
|
||||||
|
|
||||||
virtual bool update(Context&);
|
virtual int update(Context&);
|
||||||
virtual void initDependencies(Context&, const Dependency&) const;
|
virtual void initDependencies(Context&, const Dependency&) const;
|
||||||
virtual void checkContentDependencies(Context&, const Dependency&) const;
|
virtual void checkContentDependencies(Context&, const Dependency&) const;
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ void combine_image(Image& a, const Image& b, ImageCombine combine) {
|
|||||||
// Combine image data, by dispatching to combineImageDo
|
// Combine image data, by dispatching to combineImageDo
|
||||||
switch(combine) {
|
switch(combine) {
|
||||||
#define DISPATCH(comb) case comb: combine_image_do<comb>(a,b); return
|
#define DISPATCH(comb) case comb: combine_image_do<comb>(a,b); return
|
||||||
|
case COMBINE_DEFAULT:
|
||||||
case COMBINE_NORMAL: a = b; return; // no need to do a per pixel operation
|
case COMBINE_NORMAL: a = b; return; // no need to do a per pixel operation
|
||||||
DISPATCH(COMBINE_ADD);
|
DISPATCH(COMBINE_ADD);
|
||||||
DISPATCH(COMBINE_SUBTRACT);
|
DISPATCH(COMBINE_SUBTRACT);
|
||||||
@@ -138,7 +139,7 @@ void combine_image(Image& a, const Image& b, ImageCombine combine) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void draw_combine_image(DC& dc, UInt x, UInt y, const Image& img, ImageCombine combine) {
|
void draw_combine_image(DC& dc, UInt x, UInt y, const Image& img, ImageCombine combine) {
|
||||||
if (combine == COMBINE_NORMAL) {
|
if (combine <= COMBINE_NORMAL) {
|
||||||
dc.DrawBitmap(img, x, y);
|
dc.DrawBitmap(img, x, y);
|
||||||
} else {
|
} else {
|
||||||
// Capture the current image in the target rectangle
|
// Capture the current image in the target rectangle
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ Image conform_image(const Image& img, const GeneratedImage::Options& options) {
|
|||||||
if (options.saturate) {
|
if (options.saturate) {
|
||||||
saturate(image, 40);
|
saturate(image, 40);
|
||||||
}
|
}
|
||||||
|
// rotate?
|
||||||
|
if (options.angle != 0) {
|
||||||
|
image = rotate_image(image, options.angle);
|
||||||
|
}
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,10 +28,13 @@ class GeneratedImage : public ScriptValue {
|
|||||||
/// Options for generating the image
|
/// Options for generating the image
|
||||||
struct Options {
|
struct Options {
|
||||||
Options(int width = 0, int height = 0, Package* package = nullptr, Package* local_package = nullptr, PreserveAspect preserve_aspect = ASPECT_STRETCH, bool saturate = false)
|
Options(int width = 0, int height = 0, Package* package = nullptr, Package* local_package = nullptr, PreserveAspect preserve_aspect = ASPECT_STRETCH, bool saturate = false)
|
||||||
: width(width), height(height), preserve_aspect(preserve_aspect), saturate(saturate), package(package), local_package(local_package)
|
: width(width), height(height), angle(0)
|
||||||
|
, preserve_aspect(preserve_aspect), saturate(saturate)
|
||||||
|
, package(package), local_package(local_package)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
int width, height; ///< Width to force the image to, or 0 to keep the width of the input
|
int width, height; ///< Width to force the image to, or 0 to keep the width of the input
|
||||||
|
int angle; ///< Angle to rotate image by afterwards
|
||||||
PreserveAspect preserve_aspect;
|
PreserveAspect preserve_aspect;
|
||||||
bool saturate;
|
bool saturate;
|
||||||
Package* package; ///< Package to load images from
|
Package* package; ///< Package to load images from
|
||||||
@@ -43,7 +46,7 @@ class GeneratedImage : public ScriptValue {
|
|||||||
/// Generate the image
|
/// Generate the image
|
||||||
virtual Image generate(const Options&) const = 0;
|
virtual Image generate(const Options&) const = 0;
|
||||||
/// How must the image be combined with the background?
|
/// How must the image be combined with the background?
|
||||||
virtual ImageCombine combine() const { return COMBINE_NORMAL; }
|
virtual ImageCombine combine() const { return COMBINE_DEFAULT; }
|
||||||
/// Equality should mean that every pixel in the generated images is the same if the same options are used
|
/// Equality should mean that every pixel in the generated images is the same if the same options are used
|
||||||
virtual bool operator == (const GeneratedImage& that) const = 0;
|
virtual bool operator == (const GeneratedImage& that) const = 0;
|
||||||
inline bool operator != (const GeneratedImage& that) const { return !(*this == that); }
|
inline bool operator != (const GeneratedImage& that) const { return !(*this == that); }
|
||||||
|
|||||||
+7
-1
@@ -56,6 +56,10 @@ extern const int text_scaling;
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Image rotation
|
// ----------------------------------------------------------------------------- : Image rotation
|
||||||
|
|
||||||
|
/// Is an angle sideways (90 or 270 degrees)?
|
||||||
|
// Note: angle & 2 == 0 for angle in {0, 180} and != 0 for angle in {90, 270)
|
||||||
|
inline bool sideways(int angle) { return (angle & 2) != 0; }
|
||||||
|
|
||||||
/// Rotates an image counter clockwise
|
/// Rotates an image counter clockwise
|
||||||
/// angle must be a multiple of 90, i.e. {0,90,180,270}
|
/// angle must be a multiple of 90, i.e. {0,90,180,270}
|
||||||
Image rotate_image(const Image& image, int angle);
|
Image rotate_image(const Image& image, int angle);
|
||||||
@@ -85,7 +89,9 @@ void saturate(Image& image, int amount);
|
|||||||
|
|
||||||
/// Ways in which images can be combined, similair to what Photoshop supports
|
/// Ways in which images can be combined, similair to what Photoshop supports
|
||||||
enum ImageCombine
|
enum ImageCombine
|
||||||
{ COMBINE_NORMAL
|
{ COMBINE_DEFAULT // normal combine, but with a low priority, i.e. "apply default instead of add" == "add"
|
||||||
|
// it is not representable in scripting/files, so should only be used internally
|
||||||
|
, COMBINE_NORMAL
|
||||||
, COMBINE_ADD
|
, COMBINE_ADD
|
||||||
, COMBINE_SUBTRACT
|
, COMBINE_SUBTRACT
|
||||||
, COMBINE_STAMP
|
, COMBINE_STAMP
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ void draw_resampled_text(DC& dc, const RealRect& rect, double stretch, int wc, i
|
|||||||
mdc.SelectObject(wxNullBitmap);
|
mdc.SelectObject(wxNullBitmap);
|
||||||
Image img_large = buffer.ConvertToImage();
|
Image img_large = buffer.ConvertToImage();
|
||||||
// step 2. sample down
|
// step 2. sample down
|
||||||
if ((angle & 2) == 0) w *= stretch;
|
if (!sideways(angle)) w *= stretch;
|
||||||
else h *= stretch;
|
else h *= stretch;
|
||||||
Image img_small(w, h, false);
|
Image img_small(w, h, false);
|
||||||
fill_image(img_small, dc.GetTextForeground());
|
fill_image(img_small, dc.GetTextForeground());
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ wxSize CardViewer::DoGetBestSize() const {
|
|||||||
if (!stylesheet) stylesheet = set->stylesheet;
|
if (!stylesheet) stylesheet = set->stylesheet;
|
||||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
|
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
|
||||||
wxSize size(stylesheet->card_width * ss.card_zoom(), stylesheet->card_height * ss.card_zoom());
|
wxSize size(stylesheet->card_width * ss.card_zoom(), stylesheet->card_height * ss.card_zoom());
|
||||||
bool sideways = (ss.card_angle() & 2) != 0;
|
if (sideways(ss.card_angle())) swap(size.x, size.y);
|
||||||
if (sideways) swap(size.x, size.y);
|
|
||||||
return size + ws - cs;
|
return size + ws - cs;
|
||||||
}
|
}
|
||||||
return cs;
|
return cs;
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ Image ChoiceThumbnailRequest::generate() {
|
|||||||
String name = cannocial_name_form(s.field().choices->choiceName(id));
|
String name = cannocial_name_form(s.field().choices->choiceName(id));
|
||||||
ScriptableImage& img = s.choice_images[name];
|
ScriptableImage& img = s.choice_images[name];
|
||||||
return img.isReady()
|
return img.isReady()
|
||||||
? img.generate(GeneratedImage::Options(16,16, stylesheet.get(), viewer().viewer.getSet().get(), ASPECT_BORDER, true), false)
|
? img.generate(GeneratedImage::Options(16,16, stylesheet.get(), viewer().viewer.getSet().get(), ASPECT_BORDER, true))
|
||||||
: wxImage();
|
: wxImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1183,7 +1183,7 @@ bool TextValueEditor::wordListDropDown(const WordListPosP& wl) {
|
|||||||
} else {
|
} else {
|
||||||
drop_down.reset(new DropDownWordList(&editor(), false, *this, wl, wl->word_list));
|
drop_down.reset(new DropDownWordList(&editor(), false, *this, wl, wl->word_list));
|
||||||
}
|
}
|
||||||
RealRect rect = wl->rect.move(style().left, style().top - 1, 0, 2);
|
RealRect rect = style().getRotation().tr(wl->rect).move(0, -1, 0, 2);
|
||||||
drop_down->show(false, wxPoint(0,0), &rect);
|
drop_down->show(false, wxPoint(0,0), &rect);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-45
@@ -15,30 +15,33 @@
|
|||||||
bool ChoiceValueViewer::prepare(RotatedDC& dc) {
|
bool ChoiceValueViewer::prepare(RotatedDC& dc) {
|
||||||
if (style().render_style & RENDER_IMAGE) {
|
if (style().render_style & RENDER_IMAGE) {
|
||||||
style().initImage();
|
style().initImage();
|
||||||
ScriptableImage& img = style().image;
|
CachedScriptableImage& img = style().image;
|
||||||
Context& ctx = viewer.getContext();
|
Context& ctx = viewer.getContext();
|
||||||
ctx.setVariable(_("input"), to_script(value().value()));
|
ctx.setVariable(_("input"), to_script(value().value()));
|
||||||
img.update(ctx);
|
// generate to determine the size
|
||||||
//generate
|
if (img.update(ctx) && img.isReady()) {
|
||||||
if (img.isReady()) {
|
GeneratedImage::Options img_options;
|
||||||
GeneratedImage::Options img_options(0,0, viewer.stylesheet.get(), &getSet());
|
getOptions(dc, img_options);
|
||||||
if (nativeLook()) {
|
// Generate image/bitmap (whichever is available)
|
||||||
img_options.width = img_options.height = 16;
|
// don't worry, we cache the image
|
||||||
img_options.preserve_aspect = ASPECT_BORDER;
|
ImageCombine combine = style().combine;
|
||||||
} else if(style().render_style & RENDER_TEXT) {
|
style().loadMask(*viewer.stylesheet);
|
||||||
// also drawing text, use original size
|
Bitmap bitmap; Image image;
|
||||||
|
img.generateCached(img_options, &style().mask, &combine, &bitmap, &image);
|
||||||
|
int w, h;
|
||||||
|
if (bitmap.Ok()) {
|
||||||
|
w = bitmap.GetWidth();
|
||||||
|
h = bitmap.GetHeight();
|
||||||
} else {
|
} else {
|
||||||
img_options.width = (int) dc.trX(style().width);
|
assert(image.Ok());
|
||||||
img_options.height = (int) dc.trY(style().height);
|
w = image.GetWidth();
|
||||||
img_options.preserve_aspect = (style().alignment & ALIGN_STRETCH) ? ASPECT_STRETCH : ASPECT_FIT;
|
h = image.GetHeight();
|
||||||
}
|
}
|
||||||
// don't worry we cache the image
|
if (sideways(img_options.angle)) swap(w,h);
|
||||||
Image image = img.generate(img_options, true);
|
|
||||||
// store content properties
|
// store content properties
|
||||||
if (style().content_width != image.GetWidth() ||
|
if (style().content_width != w || style().content_height != h) {
|
||||||
style().content_height != image.GetHeight()) {
|
style().content_width = w;
|
||||||
style().content_width = image.GetWidth();
|
style().content_height = h;
|
||||||
style().content_height = image.GetHeight();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,33 +56,29 @@ void ChoiceValueViewer::draw(RotatedDC& dc) {
|
|||||||
double margin = 0;
|
double margin = 0;
|
||||||
if (style().render_style & RENDER_IMAGE) {
|
if (style().render_style & RENDER_IMAGE) {
|
||||||
// draw image
|
// draw image
|
||||||
ScriptableImage& img = style().image;
|
CachedScriptableImage& img = style().image;
|
||||||
if (img.isReady()) {
|
if (img.isReady()) {
|
||||||
GeneratedImage::Options img_options(0,0, viewer.stylesheet.get(), &getSet());
|
GeneratedImage::Options img_options;
|
||||||
if (nativeLook()) {
|
getOptions(dc, img_options);
|
||||||
img_options.width = img_options.height = 16;
|
// Generate image/bitmap
|
||||||
img_options.preserve_aspect = ASPECT_BORDER;
|
ImageCombine combine = style().combine;
|
||||||
} else if(style().render_style & RENDER_TEXT) {
|
|
||||||
// also drawing text, use original size
|
|
||||||
} else {
|
|
||||||
img_options.width = (int) dc.trX(style().width);
|
|
||||||
img_options.height = (int) dc.trY(style().height);
|
|
||||||
img_options.preserve_aspect = (style().alignment & ALIGN_STRETCH) ? ASPECT_STRETCH : ASPECT_FIT;
|
|
||||||
}
|
|
||||||
Image image = img.generate(img_options, true);
|
|
||||||
ImageCombine combine = img.combine();
|
|
||||||
// apply mask?
|
|
||||||
style().loadMask(*viewer.stylesheet);
|
style().loadMask(*viewer.stylesheet);
|
||||||
if (style().mask.Ok()) {
|
Bitmap bitmap; Image image;
|
||||||
set_alpha(image, style().mask);
|
img.generateCached(img_options, &style().mask, &combine, &bitmap, &image);
|
||||||
}
|
if (bitmap.Ok()) {
|
||||||
// draw
|
// just draw it
|
||||||
dc.DrawImage(image,
|
dc.DrawPreRotatedBitmap(bitmap,
|
||||||
align_in_rect(style().alignment, dc.trInvS(RealSize(image.GetWidth(), image.GetHeight())), style().getRect()),
|
align_in_rect(style().alignment, dc.trInvNoNeg(RealSize(bitmap)), style().getRect())
|
||||||
combine == COMBINE_NORMAL ? style().combine : combine,
|
|
||||||
style().angle
|
|
||||||
);
|
);
|
||||||
margin = dc.trInvS(image.GetWidth()) + 1;
|
margin = dc.trInv(RealSize(bitmap)).width + 1;
|
||||||
|
} else {
|
||||||
|
// use combine mode
|
||||||
|
dc.DrawPreRotatedImage(image,
|
||||||
|
align_in_rect(style().alignment, dc.trInvNoNeg(RealSize(image)), style().getRect()),
|
||||||
|
combine
|
||||||
|
);
|
||||||
|
margin = dc.trInv(RealSize(image)).width + 1;
|
||||||
|
}
|
||||||
} else if (nativeLook()) {
|
} else if (nativeLook()) {
|
||||||
// always have the margin
|
// always have the margin
|
||||||
margin = 17;
|
margin = 17;
|
||||||
@@ -99,6 +98,23 @@ void ChoiceValueViewer::draw(RotatedDC& dc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChoiceValueViewer::onStyleChange(bool already_prepared) {
|
void ChoiceValueViewer::onStyleChange(int changes) {
|
||||||
if (!already_prepared) viewer.redraw(*this);
|
if (changes & CHANGE_MASK) style().image.clearCache();
|
||||||
|
ValueViewer::onStyleChange(changes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChoiceValueViewer::getOptions(Rotation& rot, GeneratedImage::Options& opts) {
|
||||||
|
opts.package = viewer.stylesheet.get();
|
||||||
|
opts.local_package = &getSet();
|
||||||
|
opts.angle = rot.trAngle(style().angle);
|
||||||
|
if (nativeLook()) {
|
||||||
|
opts.width = opts.height = 16;
|
||||||
|
opts.preserve_aspect = ASPECT_BORDER;
|
||||||
|
} else if(style().render_style & RENDER_TEXT) {
|
||||||
|
// also drawing text, use original size
|
||||||
|
} else {
|
||||||
|
opts.width = (int) rot.trX(style().width);
|
||||||
|
opts.height = (int) rot.trY(style().height);
|
||||||
|
opts.preserve_aspect = (style().alignment & ALIGN_STRETCH) ? ASPECT_STRETCH : ASPECT_FIT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ class ChoiceValueViewer : public ValueViewer {
|
|||||||
|
|
||||||
virtual bool prepare(RotatedDC& dc);
|
virtual bool prepare(RotatedDC& dc);
|
||||||
virtual void draw(RotatedDC& dc);
|
virtual void draw(RotatedDC& dc);
|
||||||
virtual void onStyleChange(bool);
|
virtual void onStyleChange(int);
|
||||||
|
private:
|
||||||
|
void getOptions(Rotation& rot, GeneratedImage::Options& opts);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : EOF
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
|||||||
@@ -86,9 +86,9 @@ bool ColorValueViewer::containsPoint(const RealPoint& p) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ColorValueViewer::onStyleChange(bool already_prepared) {
|
void ColorValueViewer::onStyleChange(int changes) {
|
||||||
alpha_mask = AlphaMaskP();
|
if (changes & CHANGE_MASK) alpha_mask = AlphaMaskP();
|
||||||
if (!already_prepared) viewer.redraw(*this);
|
ValueViewer::onStyleChange(changes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ColorValueViewer::loadMask(const Rotation& rot) const {
|
void ColorValueViewer::loadMask(const Rotation& rot) const {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class ColorValueViewer : public ValueViewer {
|
|||||||
virtual void draw(RotatedDC& dc);
|
virtual void draw(RotatedDC& dc);
|
||||||
virtual bool containsPoint(const RealPoint& p) const;
|
virtual bool containsPoint(const RealPoint& p) const;
|
||||||
|
|
||||||
virtual void onStyleChange(bool);
|
virtual void onStyleChange(int);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable AlphaMaskP alpha_mask;
|
mutable AlphaMaskP alpha_mask;
|
||||||
|
|||||||
+56
-10
@@ -16,22 +16,64 @@
|
|||||||
|
|
||||||
void ImageValueViewer::draw(RotatedDC& dc) {
|
void ImageValueViewer::draw(RotatedDC& dc) {
|
||||||
drawFieldBorder(dc);
|
drawFieldBorder(dc);
|
||||||
|
// reset?
|
||||||
|
int w = (int)dc.trX(style().width), h = (int)dc.trY(style().height);
|
||||||
|
int a = dc.trAngle(style().angle);
|
||||||
|
if (bitmap.Ok() && (a != angle || bitmap.GetWidth() != w || bitmap.GetHeight() != h)) {
|
||||||
|
bitmap = Bitmap();
|
||||||
|
}
|
||||||
// try to load image
|
// try to load image
|
||||||
if (!bitmap.Ok() && !value().filename.empty()) {
|
if (!bitmap.Ok()) {
|
||||||
|
angle = a;
|
||||||
|
is_default = false;
|
||||||
|
Image image;
|
||||||
|
loadMask(dc);
|
||||||
|
// load from file
|
||||||
|
if (!value().filename.empty()) {
|
||||||
try {
|
try {
|
||||||
InputStreamP image_file = getSet().openIn(value().filename);
|
InputStreamP image_file = getSet().openIn(value().filename);
|
||||||
Image image;
|
|
||||||
if (image.LoadFile(*image_file)) {
|
if (image.LoadFile(*image_file)) {
|
||||||
image.Rescale((int)dc.trX(style().width), (int)dc.trY(style().height));
|
image.Rescale(w, h);
|
||||||
// apply mask to image
|
|
||||||
loadMask(dc);
|
|
||||||
if (alpha_mask) alpha_mask->setAlpha(image);
|
|
||||||
bitmap = Bitmap(image);
|
|
||||||
}
|
}
|
||||||
} catch (Error e) {
|
} catch (Error e) {
|
||||||
handle_error(e, false, false); // don't handle now, we are in onPaint
|
handle_error(e, false, false); // don't handle now, we are in onPaint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// nice placeholder
|
||||||
|
if (!image.Ok() && style().default_image.isReady()) {
|
||||||
|
image = style().default_image.generate(GeneratedImage::Options(w, h, viewer.stylesheet.get(), &getSet()));
|
||||||
|
is_default = true;
|
||||||
|
if (viewer.drawEditing()) {
|
||||||
|
bitmap = imagePlaceholder(dc, w, h, image, viewer.drawEditing());
|
||||||
|
if (alpha_mask || a) {
|
||||||
|
image = bitmap.ConvertToImage(); // we need to convert back to an image
|
||||||
|
} else {
|
||||||
|
image = Image();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// checkerboard placeholder
|
||||||
|
if (!image.Ok() && !bitmap.Ok() && style().width > 40) {
|
||||||
|
// placeholder bitmap
|
||||||
|
bitmap = imagePlaceholder(dc, w, h, wxNullImage, viewer.drawEditing());
|
||||||
|
if (alpha_mask || a) {
|
||||||
|
// we need to convert back to an image
|
||||||
|
image = bitmap.ConvertToImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// done
|
||||||
|
if (image.Ok()) {
|
||||||
|
// apply mask and rotate
|
||||||
|
if (alpha_mask) alpha_mask->setAlpha(image);
|
||||||
|
image = rotate_image(image, angle);
|
||||||
|
bitmap = Bitmap(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// draw image, if any
|
||||||
|
if (bitmap.Ok()) {
|
||||||
|
dc.DrawPreRotatedBitmap(bitmap, style().getPos());
|
||||||
|
}
|
||||||
|
/*
|
||||||
// if there is no image, generate a placeholder
|
// if there is no image, generate a placeholder
|
||||||
if (!bitmap.Ok()) {
|
if (!bitmap.Ok()) {
|
||||||
UInt w = (UInt)dc.trX(style().width), h = (UInt)dc.trY(style().height);
|
UInt w = (UInt)dc.trX(style().width), h = (UInt)dc.trY(style().height);
|
||||||
@@ -56,6 +98,7 @@ void ImageValueViewer::draw(RotatedDC& dc) {
|
|||||||
if (bitmap.Ok()) {
|
if (bitmap.Ok()) {
|
||||||
dc.DrawBitmap(bitmap, style().getPos());
|
dc.DrawBitmap(bitmap, style().getPos());
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImageValueViewer::containsPoint(const RealPoint& p) const {
|
bool ImageValueViewer::containsPoint(const RealPoint& p) const {
|
||||||
@@ -77,10 +120,13 @@ void ImageValueViewer::onValueChange() {
|
|||||||
bitmap = Bitmap();
|
bitmap = Bitmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageValueViewer::onStyleChange(bool already_prepared) {
|
void ImageValueViewer::onStyleChange(int changes) {
|
||||||
|
if ((changes & CHANGE_MASK) ||
|
||||||
|
((changes & CHANGE_DEFAULT) && is_default)) {
|
||||||
bitmap = Bitmap();
|
bitmap = Bitmap();
|
||||||
alpha_mask = AlphaMaskP(); // TODO: only reload whatever has changed
|
}
|
||||||
if (!already_prepared) viewer.redraw(*this);
|
if (changes & CHANGE_MASK) alpha_mask = AlphaMaskP();
|
||||||
|
ValueViewer::onStyleChange(changes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageValueViewer::loadMask(const Rotation& rot) const {
|
void ImageValueViewer::loadMask(const Rotation& rot) const {
|
||||||
|
|||||||
@@ -27,10 +27,12 @@ class ImageValueViewer : public ValueViewer {
|
|||||||
virtual bool containsPoint(const RealPoint& p) const;
|
virtual bool containsPoint(const RealPoint& p) const;
|
||||||
|
|
||||||
virtual void onValueChange();
|
virtual void onValueChange();
|
||||||
virtual void onStyleChange(bool);
|
virtual void onStyleChange(int);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Bitmap bitmap;
|
Bitmap bitmap; ///< Cached bitmap
|
||||||
|
int angle; ///< Angle of cached bitmap
|
||||||
|
int is_default; ///< Is the default placeholder image used?
|
||||||
mutable AlphaMaskP alpha_mask;
|
mutable AlphaMaskP alpha_mask;
|
||||||
|
|
||||||
void loadMask(const Rotation& rot) const;
|
void loadMask(const Rotation& rot) const;
|
||||||
|
|||||||
@@ -44,36 +44,32 @@ void MultipleChoiceValueViewer::draw(RotatedDC& dc) {
|
|||||||
if (style().render_style & RENDER_IMAGE) {
|
if (style().render_style & RENDER_IMAGE) {
|
||||||
// draw image
|
// draw image
|
||||||
style().initImage();
|
style().initImage();
|
||||||
ScriptableImage& img = style().image;
|
CachedScriptableImage& img = style().image;
|
||||||
Context& ctx = viewer.getContext();
|
Context& ctx = viewer.getContext();
|
||||||
ctx.setVariable(_("input"), to_script(value().value()));
|
ctx.setVariable(_("input"), to_script(value().value()));
|
||||||
img.update(ctx);
|
img.update(ctx);
|
||||||
if (img.isReady()) {
|
if (img.isReady()) {
|
||||||
GeneratedImage::Options img_options(0,0, viewer.stylesheet.get(), &getSet());
|
GeneratedImage::Options img_options;
|
||||||
if (nativeLook()) {
|
getOptions(dc, img_options);
|
||||||
img_options.width = img_options.height = 16;
|
// Generate image/bitmap
|
||||||
img_options.preserve_aspect = ASPECT_BORDER;
|
ImageCombine combine = style().combine;
|
||||||
} else if(style().render_style & RENDER_TEXT) {
|
|
||||||
// also drawing text, use original size
|
|
||||||
} else {
|
|
||||||
img_options.width = (int) dc.trX(style().width);
|
|
||||||
img_options.height = (int) dc.trY(style().height);
|
|
||||||
img_options.preserve_aspect = style().alignment == ALIGN_STRETCH ? ASPECT_STRETCH : ASPECT_FIT;
|
|
||||||
}
|
|
||||||
Image image = img.generate(img_options, true);
|
|
||||||
ImageCombine combine = img.combine();
|
|
||||||
// apply mask?
|
|
||||||
style().loadMask(*viewer.stylesheet);
|
style().loadMask(*viewer.stylesheet);
|
||||||
if (style().mask.Ok()) {
|
Bitmap bitmap; Image image;
|
||||||
set_alpha(image, style().mask);
|
img.generateCached(img_options, &style().mask, &combine, &bitmap, &image);
|
||||||
}
|
if (bitmap.Ok()) {
|
||||||
// draw
|
// just draw it
|
||||||
dc.DrawImage(image,
|
dc.DrawPreRotatedBitmap(bitmap,
|
||||||
align_in_rect(style().alignment, RealSize(image.GetWidth(), image.GetHeight()), style().getRect()),
|
align_in_rect(style().alignment, dc.trInvNoNeg(RealSize(bitmap)), style().getRect())
|
||||||
combine == COMBINE_NORMAL ? style().combine : combine,
|
|
||||||
style().angle
|
|
||||||
);
|
);
|
||||||
margin = dc.trInvS(image.GetWidth()) + 1;
|
margin = dc.trInv(RealSize(bitmap)).width + 1;
|
||||||
|
} else {
|
||||||
|
// use combine mode
|
||||||
|
dc.DrawPreRotatedImage(image,
|
||||||
|
align_in_rect(style().alignment, dc.trInvNoNeg(RealSize(image)), style().getRect()),
|
||||||
|
combine
|
||||||
|
);
|
||||||
|
margin = dc.trInv(RealSize(image)).width + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (style().render_style & RENDER_TEXT) {
|
if (style().render_style & RENDER_TEXT) {
|
||||||
@@ -96,10 +92,11 @@ void MultipleChoiceValueViewer::drawChoice(RotatedDC& dc, RealPoint& pos, const
|
|||||||
if (style().render_style & RENDER_IMAGE) {
|
if (style().render_style & RENDER_IMAGE) {
|
||||||
map<String,ScriptableImage>::iterator it = style().choice_images.find(cannocial_name_form(choice));
|
map<String,ScriptableImage>::iterator it = style().choice_images.find(cannocial_name_form(choice));
|
||||||
if (it != style().choice_images.end() && it->second.isReady()) {
|
if (it != style().choice_images.end() && it->second.isReady()) {
|
||||||
Image image = it->second.generate(GeneratedImage::Options(0,0, viewer.stylesheet.get(),&getSet()), true);
|
// TODO: scaling, caching
|
||||||
|
Image image = it->second.generate(GeneratedImage::Options(0,0, viewer.stylesheet.get(),&getSet()));
|
||||||
ImageCombine combine = it->second.combine();
|
ImageCombine combine = it->second.combine();
|
||||||
// TODO : alignment?
|
// TODO : alignment?
|
||||||
dc.DrawImage(image, pos + RealSize(size.width, 0), combine == COMBINE_NORMAL ? style().combine : combine);
|
dc.DrawImage(image, pos + RealSize(size.width, 0), combine == COMBINE_DEFAULT ? style().combine : combine);
|
||||||
size = add_horizontal(size, dc.trInv(RealSize(image.GetWidth() + 1, image.GetHeight())));
|
size = add_horizontal(size, dc.trInv(RealSize(image.GetWidth() + 1, image.GetHeight())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,3 +111,20 @@ void MultipleChoiceValueViewer::drawChoice(RotatedDC& dc, RealPoint& pos, const
|
|||||||
// next position
|
// next position
|
||||||
pos = move_in_direction(style().direction, pos, size, style().spacing);
|
pos = move_in_direction(style().direction, pos, size, style().spacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// COPY from ChoiceValueViewer
|
||||||
|
void MultipleChoiceValueViewer::getOptions(Rotation& rot, GeneratedImage::Options& opts) {
|
||||||
|
opts.package = viewer.stylesheet.get();
|
||||||
|
opts.local_package = &getSet();
|
||||||
|
opts.angle = rot.trAngle(style().angle);
|
||||||
|
if (nativeLook()) {
|
||||||
|
opts.width = opts.height = 16;
|
||||||
|
opts.preserve_aspect = ASPECT_BORDER;
|
||||||
|
} else if(style().render_style & RENDER_TEXT) {
|
||||||
|
// also drawing text, use original size
|
||||||
|
} else {
|
||||||
|
opts.width = (int) rot.trX(style().width);
|
||||||
|
opts.height = (int) rot.trY(style().height);
|
||||||
|
opts.preserve_aspect = (style().alignment & ALIGN_STRETCH) ? ASPECT_STRETCH : ASPECT_FIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class MultipleChoiceValueViewer : public ValueViewer {
|
|||||||
double item_height; ///< Height of a single item, or 0 if non uniform
|
double item_height; ///< Height of a single item, or 0 if non uniform
|
||||||
private:
|
private:
|
||||||
void drawChoice(RotatedDC& dc, RealPoint& pos, const String& choice, bool active = true);
|
void drawChoice(RotatedDC& dc, RealPoint& pos, const String& choice, bool active = true);
|
||||||
|
void getOptions(Rotation& rot, GeneratedImage::Options& opts);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : EOF
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ void TextValueViewer::onValueChange() {
|
|||||||
v.reset(false);
|
v.reset(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextValueViewer::onStyleChange(bool already_prepared) {
|
void TextValueViewer::onStyleChange(int changes) {
|
||||||
v.reset(true);
|
v.reset(true);
|
||||||
if (!already_prepared) viewer.redraw(*this);
|
ValueViewer::onStyleChange(changes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextValueViewer::onAction(const Action&, bool undone) {
|
void TextValueViewer::onAction(const Action&, bool undone) {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class TextValueViewer : public ValueViewer {
|
|||||||
virtual bool prepare(RotatedDC& dc);
|
virtual bool prepare(RotatedDC& dc);
|
||||||
virtual void draw(RotatedDC& dc);
|
virtual void draw(RotatedDC& dc);
|
||||||
virtual void onValueChange();
|
virtual void onValueChange();
|
||||||
virtual void onStyleChange(bool);
|
virtual void onStyleChange(int);
|
||||||
virtual void onAction(const Action&, bool undone);
|
virtual void onAction(const Action&, bool undone);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ Set& ValueViewer::getSet() const { return *viewer.getSet(); }
|
|||||||
|
|
||||||
void ValueViewer::setValue(const ValueP& value) {
|
void ValueViewer::setValue(const ValueP& value) {
|
||||||
assert(value->fieldP == styleP->fieldP); // matching field
|
assert(value->fieldP == styleP->fieldP); // matching field
|
||||||
|
if (valueP == value) return;
|
||||||
valueP = value;
|
valueP = value;
|
||||||
onValueChange();
|
onValueChange();
|
||||||
}
|
}
|
||||||
@@ -55,6 +56,12 @@ bool ValueViewer::isCurrent() const {
|
|||||||
return viewer.focusedViewer() == this;
|
return viewer.focusedViewer() == this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ValueViewer::onStyleChange(int changes) {
|
||||||
|
if (!(changes & CHANGE_ALREADY_PREPARED)) {
|
||||||
|
viewer.redraw(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Type dispatch
|
// ----------------------------------------------------------------------------- : Type dispatch
|
||||||
|
|
||||||
#define IMPLEMENT_MAKE_VIEWER(Type) \
|
#define IMPLEMENT_MAKE_VIEWER(Type) \
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ class ValueViewer : public StyleListener {
|
|||||||
*/
|
*/
|
||||||
virtual void onValueChange() {}
|
virtual void onValueChange() {}
|
||||||
/// Called when a (scripted) property of the associated style has changed
|
/// Called when a (scripted) property of the associated style has changed
|
||||||
/** If alread_prepared, should make sure the viewer stays in a state similair to that after prepare() */
|
/** Default: redraws the viewer if needed */
|
||||||
virtual void onStyleChange(bool already_prepared) {}
|
virtual void onStyleChange(int changes);
|
||||||
/// Called when an action is performed on the associated value
|
/// Called when an action is performed on the associated value
|
||||||
virtual void onAction(const Action&, bool undone) { onValueChange(); }
|
virtual void onAction(const Action&, bool undone) { onValueChange(); }
|
||||||
|
|
||||||
|
|||||||
+97
-14
@@ -41,14 +41,7 @@ GeneratedImageP image_from_script(const ScriptValueP& value) {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------- : ScriptableImage
|
// ----------------------------------------------------------------------------- : ScriptableImage
|
||||||
|
|
||||||
Image ScriptableImage::generate(const GeneratedImage::Options& options, bool cache) const {
|
Image ScriptableImage::generate(const GeneratedImage::Options& options) const {
|
||||||
if (cached.Ok() && (cached.GetWidth() == options.width && cached.GetHeight() == options.height
|
|
||||||
|| (options.preserve_aspect == ASPECT_FIT && // only one dimension has to fit
|
|
||||||
(cached.GetWidth() == options.width || cached.GetHeight() == options.height)
|
|
||||||
))) {
|
|
||||||
// cached, so we are done
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
// generate
|
// generate
|
||||||
Image image;
|
Image image;
|
||||||
if (isReady()) {
|
if (isReady()) {
|
||||||
@@ -64,14 +57,11 @@ Image ScriptableImage::generate(const GeneratedImage::Options& options, bool cac
|
|||||||
i.SetAlpha(0,0,0);
|
i.SetAlpha(0,0,0);
|
||||||
image = i;
|
image = i;
|
||||||
}
|
}
|
||||||
image = conform_image(image, options);
|
return conform_image(image, options);
|
||||||
// cache? and return
|
|
||||||
if (cache) cached = image;
|
|
||||||
return image;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageCombine ScriptableImage::combine() const {
|
ImageCombine ScriptableImage::combine() const {
|
||||||
if (!isReady()) return COMBINE_NORMAL;
|
if (!isReady()) return COMBINE_DEFAULT;
|
||||||
return value->combine();
|
return value->combine();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +70,6 @@ bool ScriptableImage::update(Context& ctx) {
|
|||||||
GeneratedImageP new_value = image_from_script(script.invoke(ctx));
|
GeneratedImageP new_value = image_from_script(script.invoke(ctx));
|
||||||
if (!new_value || !value || *new_value != *value) {
|
if (!new_value || !value || *new_value != *value) {
|
||||||
value = new_value;
|
value = new_value;
|
||||||
cached = Image();
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -118,3 +107,97 @@ template <> void Writer::handle(const ScriptableImage& s) {
|
|||||||
template <> void GetDefaultMember::handle(const ScriptableImage& s) {
|
template <> void GetDefaultMember::handle(const ScriptableImage& s) {
|
||||||
handle(s.script.unparsed);
|
handle(s.script.unparsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : CachedScriptableImage
|
||||||
|
|
||||||
|
void CachedScriptableImage::generateCached(const GeneratedImage::Options& options,
|
||||||
|
Image* mask,
|
||||||
|
ImageCombine* combine, wxBitmap* bitmap, wxImage* image) {
|
||||||
|
// ready?
|
||||||
|
if (!isReady()) {
|
||||||
|
// error, return blank image
|
||||||
|
Image i(1,1);
|
||||||
|
i.InitAlpha();
|
||||||
|
i.SetAlpha(0,0,0);
|
||||||
|
*image = i;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// find combine mode
|
||||||
|
ImageCombine combine_i = value->combine();
|
||||||
|
if (combine_i != COMBINE_DEFAULT) *combine = combine_i;
|
||||||
|
// desired size
|
||||||
|
int ow = options.width, oh = options.height;
|
||||||
|
if (sideways(options.angle)) swap(ow,oh);
|
||||||
|
// image or bitmap?
|
||||||
|
if (*combine <= COMBINE_NORMAL) {
|
||||||
|
// bitmap
|
||||||
|
if (cached_b.Ok() && options.angle == cached_angle) {
|
||||||
|
bool w_ok = cached_b.GetWidth() == ow,
|
||||||
|
h_ok = cached_b.GetHeight() == oh;
|
||||||
|
if ((w_ok && h_ok) || (options.preserve_aspect == ASPECT_FIT && (w_ok || h_ok))) { // only one dimension has to fit when fitting
|
||||||
|
// cached, we are done
|
||||||
|
*bitmap = cached_b;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// image
|
||||||
|
if (cached_i.Ok()) {
|
||||||
|
bool w_ok = cached_i.GetWidth() == options.width,
|
||||||
|
h_ok = cached_i.GetHeight() == options.height;
|
||||||
|
if ((w_ok && h_ok) || (options.preserve_aspect == ASPECT_FIT && (w_ok || h_ok))) { // only one dimension has to fit when fitting
|
||||||
|
if (options.angle != cached_angle) {
|
||||||
|
// rotate cached image
|
||||||
|
cached_i = rotate_image(cached_i, options.angle - cached_angle + 360);
|
||||||
|
cached_angle = options.angle;
|
||||||
|
}
|
||||||
|
*image = cached_i;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// generate
|
||||||
|
cached_i = generate(options);
|
||||||
|
cached_angle = options.angle;
|
||||||
|
if (mask && mask->Ok()) {
|
||||||
|
// apply mask
|
||||||
|
if (mask->GetWidth() == cached_i.GetWidth() && mask->GetHeight() == cached_i.GetHeight()) {
|
||||||
|
set_alpha(cached_i, *mask);
|
||||||
|
} else {
|
||||||
|
Image mask_scaled(cached_i.GetWidth(),cached_i.GetHeight(), false);
|
||||||
|
resample(mask,mask_scaled);
|
||||||
|
set_alpha(cached_i, mask_scaled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*combine <= COMBINE_NORMAL) {
|
||||||
|
*bitmap = cached_b = Bitmap(cached_i);
|
||||||
|
cached_i = Image();
|
||||||
|
} else {
|
||||||
|
*image = cached_i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CachedScriptableImage::update(Context& ctx) {
|
||||||
|
bool change = ScriptableImage::update(ctx);
|
||||||
|
if (change) {
|
||||||
|
clearCache();
|
||||||
|
}
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CachedScriptableImage::clearCache() {
|
||||||
|
cached_i = Image();
|
||||||
|
cached_b = Bitmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <> void Reader::handle(CachedScriptableImage& s) {
|
||||||
|
handle((ScriptableImage&)s);
|
||||||
|
}
|
||||||
|
template <> void Writer::handle(const CachedScriptableImage& s) {
|
||||||
|
handle((const ScriptableImage&)s);
|
||||||
|
}
|
||||||
|
template <> void GetDefaultMember::handle(const CachedScriptableImage& s) {
|
||||||
|
handle((const ScriptableImage&)s);
|
||||||
|
}
|
||||||
|
|||||||
+35
-4
@@ -20,7 +20,6 @@
|
|||||||
/// An image that can also be scripted
|
/// An image that can also be scripted
|
||||||
/** Differs from Scriptable<Image> in that:
|
/** Differs from Scriptable<Image> in that:
|
||||||
* - A script is always used
|
* - A script is always used
|
||||||
* - Age is checked, chached images are used if possible
|
|
||||||
* - The image can be scaled
|
* - The image can be scaled
|
||||||
*/
|
*/
|
||||||
class ScriptableImage {
|
class ScriptableImage {
|
||||||
@@ -37,7 +36,7 @@ class ScriptableImage {
|
|||||||
inline bool isSet() const { return script || value; }
|
inline bool isSet() const { return script || value; }
|
||||||
|
|
||||||
/// Generate an image.
|
/// Generate an image.
|
||||||
Image generate(const GeneratedImage::Options& options, bool cache = false) const;
|
Image generate(const GeneratedImage::Options& options) const;
|
||||||
/// How should images be combined with the background?
|
/// How should images be combined with the background?
|
||||||
ImageCombine combine() const;
|
ImageCombine combine() const;
|
||||||
|
|
||||||
@@ -56,10 +55,9 @@ class ScriptableImage {
|
|||||||
/// Get access to the script, always returns a valid script
|
/// Get access to the script, always returns a valid script
|
||||||
ScriptP getScriptP();
|
ScriptP getScriptP();
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
OptionalScript script; ///< The script, not really optional
|
OptionalScript script; ///< The script, not really optional
|
||||||
GeneratedImageP value; ///< The image generator
|
GeneratedImageP value; ///< The image generator
|
||||||
mutable Image cached; ///< The cached actual image
|
|
||||||
|
|
||||||
DECLARE_REFLECTION();
|
DECLARE_REFLECTION();
|
||||||
};
|
};
|
||||||
@@ -70,5 +68,38 @@ inline ScriptValueP to_script(const ScriptableImage&) { return script_nil; }
|
|||||||
/// Convert a script value to a GeneratedImageP
|
/// Convert a script value to a GeneratedImageP
|
||||||
GeneratedImageP image_from_script(const ScriptValueP& value);
|
GeneratedImageP image_from_script(const ScriptValueP& value);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : CachedScriptableImage
|
||||||
|
|
||||||
|
/// A version of ScriptableImage that does caching
|
||||||
|
class CachedScriptableImage : public ScriptableImage {
|
||||||
|
public:
|
||||||
|
inline CachedScriptableImage() {}
|
||||||
|
inline CachedScriptableImage(const String& script) : ScriptableImage(script) {}
|
||||||
|
inline CachedScriptableImage(const GeneratedImageP& gen) : ScriptableImage(gen) {}
|
||||||
|
|
||||||
|
/// Generate an image, using caching if possible.
|
||||||
|
/** *combine should be set to the combine value of the style.
|
||||||
|
* It will be overwritten if the image specifies a non-default combine.
|
||||||
|
* After this call, either:
|
||||||
|
* - combine <= COMBINE_NORMAL && bitmap->Ok()
|
||||||
|
* - or combine > COMBINE_NORMAL && image->Ok()
|
||||||
|
* Optionally, an alpha mask is applied to the image.
|
||||||
|
*/
|
||||||
|
void generateCached(const GeneratedImage::Options& img_options,
|
||||||
|
Image* mask,
|
||||||
|
ImageCombine* combine, wxBitmap* bitmap, wxImage* image);
|
||||||
|
|
||||||
|
/// Update the script, returns true if the value has changed
|
||||||
|
bool update(Context& ctx);
|
||||||
|
|
||||||
|
/// Clears the cache
|
||||||
|
void clearCache();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Image cached_i; ///< The cached image
|
||||||
|
Bitmap cached_b; ///< *or* the cached bitmap
|
||||||
|
int cached_angle;
|
||||||
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : EOF
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -224,9 +224,9 @@ void SetScriptManager::updateStyles(Context& ctx, const IndexMap<FieldP,StyleP>&
|
|||||||
FOR_EACH_CONST(s, styles) {
|
FOR_EACH_CONST(s, styles) {
|
||||||
if (only_content_dependent && !s->content_dependent) continue;
|
if (only_content_dependent && !s->content_dependent) continue;
|
||||||
try {
|
try {
|
||||||
if (s->update(ctx)) {
|
if (int change = s->update(ctx)) {
|
||||||
// style has changed, tell listeners
|
// style has changed, tell listeners
|
||||||
s->tellListeners(only_content_dependent);
|
s->tellListeners(change | (only_content_dependent ? CHANGE_ALREADY_PREPARED : 0) );
|
||||||
}
|
}
|
||||||
} catch (const ScriptError& e) {
|
} catch (const ScriptError& e) {
|
||||||
// NOTE: don't handle errors now, we are likely in an onPaint handler
|
// NOTE: don't handle errors now, we are likely in an onPaint handler
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ class RealSize {
|
|||||||
inline explicit RealSize(const wxImage& img)
|
inline explicit RealSize(const wxImage& img)
|
||||||
: width(img.GetWidth()), height(img.GetHeight())
|
: width(img.GetWidth()), height(img.GetHeight())
|
||||||
{}
|
{}
|
||||||
|
/// size of a bitmap
|
||||||
|
inline explicit RealSize(const wxBitmap& img)
|
||||||
|
: width(img.GetWidth()), height(img.GetHeight())
|
||||||
|
{}
|
||||||
|
|
||||||
/// Negation of a size, negates both components
|
/// Negation of a size, negates both components
|
||||||
inline RealSize operator - () const {
|
inline RealSize operator - () const {
|
||||||
|
|||||||
@@ -160,9 +160,17 @@ void RotatedDC::DrawBitmap(const Bitmap& bitmap, const RealPoint& pos) {
|
|||||||
}
|
}
|
||||||
void RotatedDC::DrawImage (const Image& image, const RealPoint& pos, ImageCombine combine, int angle) {
|
void RotatedDC::DrawImage (const Image& image, const RealPoint& pos, ImageCombine combine, int angle) {
|
||||||
Image rotated = rotate_image(image, angle + this->angle);
|
Image rotated = rotate_image(image, angle + this->angle);
|
||||||
wxRect r = trNoNegNoZoom(RealRect(pos, RealSize(image.GetWidth(), image.GetHeight())));
|
wxRect r = trNoNegNoZoom(RealRect(pos, RealSize(image)));
|
||||||
draw_combine_image(dc, r.x, r.y, rotated, combine);
|
draw_combine_image(dc, r.x, r.y, rotated, combine);
|
||||||
}
|
}
|
||||||
|
void RotatedDC::DrawPreRotatedBitmap(const Bitmap& bitmap, const RealPoint& pos) {
|
||||||
|
RealPoint p_ext = tr(pos) - RealSize(revX()?bitmap.GetWidth():0, revY()?bitmap.GetHeight():0);
|
||||||
|
dc.DrawBitmap(bitmap, (int) p_ext.x, (int) p_ext.y, true);
|
||||||
|
}
|
||||||
|
void RotatedDC::DrawPreRotatedImage (const Image& image, const RealPoint& pos, ImageCombine combine) {
|
||||||
|
RealPoint p_ext = tr(pos) - RealSize(revX()?image.GetWidth():0, revY()?image.GetHeight():0);
|
||||||
|
draw_combine_image(dc, p_ext.x, p_ext.y, image, combine);
|
||||||
|
}
|
||||||
|
|
||||||
void RotatedDC::DrawLine (const RealPoint& p1, const RealPoint& p2) {
|
void RotatedDC::DrawLine (const RealPoint& p1, const RealPoint& p2) {
|
||||||
wxPoint p1_ext = tr(p1), p2_ext = tr(p2);
|
wxPoint p1_ext = tr(p1), p2_ext = tr(p2);
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ class Rotation {
|
|||||||
inline double trX(double s) const { return s * zoomX; }
|
inline double trX(double s) const { return s * zoomX; }
|
||||||
inline double trY(double s) const { return s * zoomY; }
|
inline double trY(double s) const { return s * zoomY; }
|
||||||
|
|
||||||
|
/// Translate an angle
|
||||||
|
inline int trAngle(int a) { return (angle + a) % 360; }
|
||||||
|
|
||||||
/// Translate a single point
|
/// Translate a single point
|
||||||
RealPoint tr(const RealPoint& p) const;
|
RealPoint tr(const RealPoint& p) const;
|
||||||
/// Translate a single size, the result may be negative
|
/// Translate a single size, the result may be negative
|
||||||
@@ -94,8 +97,7 @@ class Rotation {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
/// Is the rotation sideways (90 or 270 degrees)?
|
/// Is the rotation sideways (90 or 270 degrees)?
|
||||||
// Note: angle & 2 == 0 for angle in {0, 180} and != 0 for angle in {90, 270)
|
inline bool sideways() const { return ::sideways(angle); }
|
||||||
inline bool sideways() const { return (angle & 2) != 0; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Is the x axis 'reversed' (after turning sideways)?
|
/// Is the x axis 'reversed' (after turning sideways)?
|
||||||
@@ -153,7 +155,11 @@ class RotatedDC : public Rotation {
|
|||||||
/// Draw abitmap, it must already be zoomed!
|
/// Draw abitmap, it must already be zoomed!
|
||||||
void DrawBitmap(const Bitmap& bitmap, const RealPoint& pos);
|
void DrawBitmap(const Bitmap& bitmap, const RealPoint& pos);
|
||||||
/// Draw an image using the given combining mode, the image must already be zoomed!
|
/// Draw an image using the given combining mode, the image must already be zoomed!
|
||||||
void DrawImage (const Image& image, const RealPoint& pos, ImageCombine combine = COMBINE_NORMAL, int angle = 0);
|
void DrawImage (const Image& image, const RealPoint& pos, ImageCombine combine = COMBINE_DEFAULT, int angle = 0);
|
||||||
|
/// Draw a bitmap that is already zoomed and rotated
|
||||||
|
void DrawPreRotatedBitmap(const Bitmap& bitmap, const RealPoint& pos);
|
||||||
|
/// Draw an image that is already zoomed and rotated
|
||||||
|
void DrawPreRotatedImage(const Image& image, const RealPoint& pos, ImageCombine combine = COMBINE_DEFAULT);
|
||||||
void DrawLine (const RealPoint& p1, const RealPoint& p2);
|
void DrawLine (const RealPoint& p1, const RealPoint& p2);
|
||||||
void DrawRectangle(const RealRect& r);
|
void DrawRectangle(const RealRect& r);
|
||||||
void DrawRoundedRectangle(const RealRect& r, double radius);
|
void DrawRoundedRectangle(const RealRect& r, double radius);
|
||||||
|
|||||||
Reference in New Issue
Block a user