mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57: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"));
|
||||
}
|
||||
|
||||
bool Style::update(Context& ctx) {
|
||||
int Style::update(Context& ctx) {
|
||||
bool changed =
|
||||
left .update(ctx)
|
||||
| width .update(ctx)
|
||||
@@ -196,8 +196,8 @@ void Style::removeListener(StyleListener* listener) {
|
||||
listeners.end()
|
||||
);
|
||||
}
|
||||
void Style::tellListeners(bool already_prepared) {
|
||||
FOR_EACH(l, listeners) l->onStyleChange(already_prepared);
|
||||
void Style::tellListeners(int changes) {
|
||||
FOR_EACH(l, listeners) l->onStyleChange(changes);
|
||||
}
|
||||
|
||||
StyleListener::StyleListener(const StyleP& style)
|
||||
|
||||
+18
-6
@@ -119,9 +119,11 @@ class Style : public IntrusivePtrVirtualBase {
|
||||
/** thisP is a smart pointer to this */
|
||||
virtual ValueViewerP makeEditor(DataEditor& parent, const StyleP& thisP) = 0;
|
||||
|
||||
/// Update scripted values of this style, return true if anything has changed.
|
||||
/** The caller should tellListeners() */
|
||||
virtual bool update(Context&);
|
||||
/// Update scripted values of this style, return nonzero if anything has changed.
|
||||
/** The caller should tellListeners()
|
||||
* 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
|
||||
/** Only use for things that need invalidate() */
|
||||
virtual void initDependencies(Context&, const Dependency&) const;
|
||||
@@ -140,7 +142,8 @@ class Style : public IntrusivePtrVirtualBase {
|
||||
/// Remove a StyleListener
|
||||
void removeListener(StyleListener*);
|
||||
/// 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:
|
||||
DECLARE_REFLECTION_VIRTUAL();
|
||||
@@ -148,6 +151,15 @@ class Style : public IntrusivePtrVirtualBase {
|
||||
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&);
|
||||
inline const FieldP& get_key (const StyleP& s) { return s->fieldP; }
|
||||
inline const String& get_key_name(const StyleP& s) { return s->fieldP->name; }
|
||||
@@ -168,8 +180,8 @@ class StyleListener : public IntrusivePtrVirtualBase {
|
||||
virtual ~StyleListener();
|
||||
|
||||
/// Called when a (scripted) property of the viewed style has changed
|
||||
/** already_prepared indicates that this change happend after preparing text for content properties */
|
||||
virtual void onStyleChange(bool already_prepared) {}
|
||||
/** changes is a combination of StyleChange flags */
|
||||
virtual void onStyleChange(int changes) {}
|
||||
protected:
|
||||
const StyleP styleP; ///< The style we are listening to
|
||||
};
|
||||
|
||||
@@ -216,17 +216,17 @@ void ChoiceStyle::initImage() {
|
||||
script.addInstruction(I_RET);
|
||||
}
|
||||
|
||||
bool ChoiceStyle::update(Context& ctx) {
|
||||
int ChoiceStyle::update(Context& ctx) {
|
||||
// Don't update the choice images, leave that to invalidate()
|
||||
bool change = Style ::update(ctx)
|
||||
| font .update(ctx)
|
||||
| mask_filename.update(ctx);
|
||||
int change = Style ::update(ctx)
|
||||
| font .update(ctx) * CHANGE_OTHER
|
||||
| mask_filename.update(ctx) * CHANGE_MASK;
|
||||
if (!choice_images_initialized) {
|
||||
// we only want to do this once because it is rather slow, other updates are handled by dependencies
|
||||
choice_images_initialized = true;
|
||||
FOR_EACH(ci, choice_images) {
|
||||
if (ci.second.update(ctx)) {
|
||||
change = true;
|
||||
change |= CHANGE_OTHER;
|
||||
// TODO : remove this thumbnail
|
||||
}
|
||||
}
|
||||
@@ -253,7 +253,7 @@ void ChoiceStyle::invalidate(Context& ctx) {
|
||||
thumbnails_status[i] = THUMB_CHANGED;
|
||||
}
|
||||
}
|
||||
if (change) tellListeners(false);
|
||||
if (change) tellListeners(CHANGE_OTHER);
|
||||
}
|
||||
|
||||
void ChoiceStyle::loadMask(Package& pkg) {
|
||||
|
||||
@@ -145,7 +145,7 @@ class ChoiceStyle : public Style {
|
||||
ChoicePopupStyle popup_style; ///< Style of popups/menus
|
||||
ChoiceRenderStyle render_style; ///< Style of rendering
|
||||
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)
|
||||
bool choice_images_initialized;
|
||||
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
|
||||
void initImage();
|
||||
|
||||
virtual bool update(Context&);
|
||||
virtual int update(Context&);
|
||||
virtual void initDependencies(Context&, const Dependency&) const;
|
||||
virtual void invalidate(Context&);
|
||||
|
||||
|
||||
@@ -67,9 +67,9 @@ IMPLEMENT_REFLECTION(ColorStyle) {
|
||||
REFLECT_N("mask", mask_filename);
|
||||
}
|
||||
|
||||
bool ColorStyle::update(Context& ctx) {
|
||||
int ColorStyle::update(Context& ctx) {
|
||||
return Style ::update(ctx)
|
||||
| mask_filename.update(ctx);
|
||||
| mask_filename.update(ctx) * CHANGE_MASK;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : ColorValue
|
||||
|
||||
@@ -66,7 +66,7 @@ class ColorStyle : public Style {
|
||||
double bottom_width; ///< Width of the colored region on the bottom side
|
||||
Scriptable<String> mask_filename; ///< Filename of an additional mask over the images
|
||||
|
||||
virtual bool update(Context&);
|
||||
virtual int update(Context&);
|
||||
|
||||
private:
|
||||
DECLARE_REFLECTION();
|
||||
|
||||
@@ -25,14 +25,16 @@ IMPLEMENT_REFLECTION(ImageField) {
|
||||
|
||||
IMPLEMENT_REFLECTION(ImageStyle) {
|
||||
REFLECT_BASE(Style);
|
||||
REFLECT(angle);
|
||||
REFLECT_N("mask", mask_filename);
|
||||
REFLECT_N("default", default_image);
|
||||
}
|
||||
|
||||
bool ImageStyle::update(Context& ctx) {
|
||||
int ImageStyle::update(Context& ctx) {
|
||||
return Style ::update(ctx)
|
||||
| mask_filename.update(ctx)
|
||||
| default_image.update(ctx);
|
||||
| angle .update(ctx) * CHANGE_OTHER
|
||||
| mask_filename.update(ctx) * CHANGE_MASK
|
||||
| default_image.update(ctx) * CHANGE_DEFAULT;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : ImageValue
|
||||
|
||||
@@ -38,10 +38,11 @@ class ImageStyle : public Style {
|
||||
inline ImageStyle(const ImageFieldP& field) : Style(field) {}
|
||||
DECLARE_STYLE_TYPE(Image);
|
||||
|
||||
Scriptable<int> angle; ///< Rotation of images
|
||||
Scriptable<String> mask_filename; ///< Filename for a mask image
|
||||
ScriptableImage default_image; ///< Placeholder
|
||||
|
||||
virtual bool update(Context&);
|
||||
virtual int update(Context&);
|
||||
|
||||
private:
|
||||
DECLARE_REFLECTION();
|
||||
|
||||
@@ -40,9 +40,9 @@ InfoStyle::InfoStyle(const InfoFieldP& field)
|
||||
, background_color(255,255,255)
|
||||
{}
|
||||
|
||||
bool InfoStyle::update(Context& ctx) {
|
||||
int InfoStyle::update(Context& ctx) {
|
||||
return Style ::update(ctx)
|
||||
| font .update(ctx);
|
||||
| font .update(ctx) * CHANGE_OTHER;
|
||||
}
|
||||
void InfoStyle::initDependencies(Context& ctx, const Dependency& dep) const {
|
||||
Style ::initDependencies(ctx, dep);
|
||||
|
||||
@@ -51,7 +51,7 @@ class InfoStyle : public Style {
|
||||
double padding_top, padding_bottom;
|
||||
Color background_color;
|
||||
|
||||
virtual bool update(Context&);
|
||||
virtual int update(Context&);
|
||||
virtual void initDependencies(Context&, const Dependency&) const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -68,12 +68,12 @@ double TextStyle::getStretch() const {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
bool TextStyle::update(Context& ctx) {
|
||||
int TextStyle::update(Context& ctx) {
|
||||
return Style ::update(ctx)
|
||||
| font .update(ctx)
|
||||
| symbol_font.update(ctx)
|
||||
| alignment .update(ctx)
|
||||
| angle .update(ctx);
|
||||
| font .update(ctx) * CHANGE_OTHER
|
||||
| symbol_font.update(ctx) * CHANGE_OTHER
|
||||
| alignment .update(ctx) * CHANGE_OTHER
|
||||
| angle .update(ctx) * CHANGE_OTHER;
|
||||
}
|
||||
void TextStyle::initDependencies(Context& ctx, const Dependency& dep) const {
|
||||
Style ::initDependencies(ctx, dep);
|
||||
|
||||
@@ -75,7 +75,7 @@ class TextStyle : public Style {
|
||||
double content_width, content_height; ///< Size of the rendered text
|
||||
int content_lines; ///< Number of rendered lines
|
||||
|
||||
virtual bool update(Context&);
|
||||
virtual int update(Context&);
|
||||
virtual void initDependencies(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
|
||||
switch(combine) {
|
||||
#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
|
||||
DISPATCH(COMBINE_ADD);
|
||||
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) {
|
||||
if (combine == COMBINE_NORMAL) {
|
||||
if (combine <= COMBINE_NORMAL) {
|
||||
dc.DrawBitmap(img, x, y);
|
||||
} else {
|
||||
// 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) {
|
||||
saturate(image, 40);
|
||||
}
|
||||
// rotate?
|
||||
if (options.angle != 0) {
|
||||
image = rotate_image(image, options.angle);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,10 +28,13 @@ class GeneratedImage : public ScriptValue {
|
||||
/// Options for generating the image
|
||||
struct Options {
|
||||
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 angle; ///< Angle to rotate image by afterwards
|
||||
PreserveAspect preserve_aspect;
|
||||
bool saturate;
|
||||
Package* package; ///< Package to load images from
|
||||
@@ -43,7 +46,7 @@ class GeneratedImage : public ScriptValue {
|
||||
/// Generate the image
|
||||
virtual Image generate(const Options&) const = 0;
|
||||
/// 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
|
||||
virtual bool operator == (const GeneratedImage& that) const = 0;
|
||||
inline bool operator != (const GeneratedImage& that) const { return !(*this == that); }
|
||||
|
||||
+7
-1
@@ -56,6 +56,10 @@ extern const int text_scaling;
|
||||
|
||||
// ----------------------------------------------------------------------------- : 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
|
||||
/// angle must be a multiple of 90, i.e. {0,90,180,270}
|
||||
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
|
||||
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_SUBTRACT
|
||||
, COMBINE_STAMP
|
||||
|
||||
@@ -163,7 +163,7 @@ void draw_resampled_text(DC& dc, const RealRect& rect, double stretch, int wc, i
|
||||
mdc.SelectObject(wxNullBitmap);
|
||||
Image img_large = buffer.ConvertToImage();
|
||||
// step 2. sample down
|
||||
if ((angle & 2) == 0) w *= stretch;
|
||||
if (!sideways(angle)) w *= stretch;
|
||||
else h *= stretch;
|
||||
Image img_small(w, h, false);
|
||||
fill_image(img_small, dc.GetTextForeground());
|
||||
|
||||
@@ -29,8 +29,7 @@ wxSize CardViewer::DoGetBestSize() const {
|
||||
if (!stylesheet) stylesheet = set->stylesheet;
|
||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
|
||||
wxSize size(stylesheet->card_width * ss.card_zoom(), stylesheet->card_height * ss.card_zoom());
|
||||
bool sideways = (ss.card_angle() & 2) != 0;
|
||||
if (sideways) swap(size.x, size.y);
|
||||
if (sideways(ss.card_angle())) swap(size.x, size.y);
|
||||
return size + ws - cs;
|
||||
}
|
||||
return cs;
|
||||
|
||||
@@ -56,7 +56,7 @@ Image ChoiceThumbnailRequest::generate() {
|
||||
String name = cannocial_name_form(s.field().choices->choiceName(id));
|
||||
ScriptableImage& img = s.choice_images[name];
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -1183,7 +1183,7 @@ bool TextValueEditor::wordListDropDown(const WordListPosP& wl) {
|
||||
} else {
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
+61
-45
@@ -15,30 +15,33 @@
|
||||
bool ChoiceValueViewer::prepare(RotatedDC& dc) {
|
||||
if (style().render_style & RENDER_IMAGE) {
|
||||
style().initImage();
|
||||
ScriptableImage& img = style().image;
|
||||
CachedScriptableImage& img = style().image;
|
||||
Context& ctx = viewer.getContext();
|
||||
ctx.setVariable(_("input"), to_script(value().value()));
|
||||
img.update(ctx);
|
||||
//generate
|
||||
if (img.isReady()) {
|
||||
GeneratedImage::Options img_options(0,0, viewer.stylesheet.get(), &getSet());
|
||||
if (nativeLook()) {
|
||||
img_options.width = img_options.height = 16;
|
||||
img_options.preserve_aspect = ASPECT_BORDER;
|
||||
} else if(style().render_style & RENDER_TEXT) {
|
||||
// also drawing text, use original size
|
||||
// generate to determine the size
|
||||
if (img.update(ctx) && img.isReady()) {
|
||||
GeneratedImage::Options img_options;
|
||||
getOptions(dc, img_options);
|
||||
// Generate image/bitmap (whichever is available)
|
||||
// don't worry, we cache the image
|
||||
ImageCombine combine = style().combine;
|
||||
style().loadMask(*viewer.stylesheet);
|
||||
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 {
|
||||
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;
|
||||
assert(image.Ok());
|
||||
w = image.GetWidth();
|
||||
h = image.GetHeight();
|
||||
}
|
||||
// don't worry we cache the image
|
||||
Image image = img.generate(img_options, true);
|
||||
if (sideways(img_options.angle)) swap(w,h);
|
||||
// store content properties
|
||||
if (style().content_width != image.GetWidth() ||
|
||||
style().content_height != image.GetHeight()) {
|
||||
style().content_width = image.GetWidth();
|
||||
style().content_height = image.GetHeight();
|
||||
if (style().content_width != w || style().content_height != h) {
|
||||
style().content_width = w;
|
||||
style().content_height = h;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -53,33 +56,29 @@ void ChoiceValueViewer::draw(RotatedDC& dc) {
|
||||
double margin = 0;
|
||||
if (style().render_style & RENDER_IMAGE) {
|
||||
// draw image
|
||||
ScriptableImage& img = style().image;
|
||||
CachedScriptableImage& img = style().image;
|
||||
if (img.isReady()) {
|
||||
GeneratedImage::Options img_options(0,0, viewer.stylesheet.get(), &getSet());
|
||||
if (nativeLook()) {
|
||||
img_options.width = img_options.height = 16;
|
||||
img_options.preserve_aspect = ASPECT_BORDER;
|
||||
} 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?
|
||||
GeneratedImage::Options img_options;
|
||||
getOptions(dc, img_options);
|
||||
// Generate image/bitmap
|
||||
ImageCombine combine = style().combine;
|
||||
style().loadMask(*viewer.stylesheet);
|
||||
if (style().mask.Ok()) {
|
||||
set_alpha(image, style().mask);
|
||||
Bitmap bitmap; Image image;
|
||||
img.generateCached(img_options, &style().mask, &combine, &bitmap, &image);
|
||||
if (bitmap.Ok()) {
|
||||
// just draw it
|
||||
dc.DrawPreRotatedBitmap(bitmap,
|
||||
align_in_rect(style().alignment, dc.trInvNoNeg(RealSize(bitmap)), style().getRect())
|
||||
);
|
||||
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;
|
||||
}
|
||||
// draw
|
||||
dc.DrawImage(image,
|
||||
align_in_rect(style().alignment, dc.trInvS(RealSize(image.GetWidth(), image.GetHeight())), style().getRect()),
|
||||
combine == COMBINE_NORMAL ? style().combine : combine,
|
||||
style().angle
|
||||
);
|
||||
margin = dc.trInvS(image.GetWidth()) + 1;
|
||||
} else if (nativeLook()) {
|
||||
// always have the margin
|
||||
margin = 17;
|
||||
@@ -99,6 +98,23 @@ void ChoiceValueViewer::draw(RotatedDC& dc) {
|
||||
}
|
||||
}
|
||||
|
||||
void ChoiceValueViewer::onStyleChange(bool already_prepared) {
|
||||
if (!already_prepared) viewer.redraw(*this);
|
||||
void ChoiceValueViewer::onStyleChange(int changes) {
|
||||
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 void draw(RotatedDC& dc);
|
||||
virtual void onStyleChange(bool);
|
||||
virtual void onStyleChange(int);
|
||||
private:
|
||||
void getOptions(Rotation& rot, GeneratedImage::Options& opts);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -86,9 +86,9 @@ bool ColorValueViewer::containsPoint(const RealPoint& p) const {
|
||||
}
|
||||
}
|
||||
|
||||
void ColorValueViewer::onStyleChange(bool already_prepared) {
|
||||
alpha_mask = AlphaMaskP();
|
||||
if (!already_prepared) viewer.redraw(*this);
|
||||
void ColorValueViewer::onStyleChange(int changes) {
|
||||
if (changes & CHANGE_MASK) alpha_mask = AlphaMaskP();
|
||||
ValueViewer::onStyleChange(changes);
|
||||
}
|
||||
|
||||
void ColorValueViewer::loadMask(const Rotation& rot) const {
|
||||
|
||||
@@ -25,7 +25,7 @@ class ColorValueViewer : public ValueViewer {
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual bool containsPoint(const RealPoint& p) const;
|
||||
|
||||
virtual void onStyleChange(bool);
|
||||
virtual void onStyleChange(int);
|
||||
|
||||
private:
|
||||
mutable AlphaMaskP alpha_mask;
|
||||
|
||||
+62
-16
@@ -16,22 +16,64 @@
|
||||
|
||||
void ImageValueViewer::draw(RotatedDC& 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
|
||||
if (!bitmap.Ok() && !value().filename.empty()) {
|
||||
try {
|
||||
InputStreamP image_file = getSet().openIn(value().filename);
|
||||
Image image;
|
||||
if (image.LoadFile(*image_file)) {
|
||||
image.Rescale((int)dc.trX(style().width), (int)dc.trY(style().height));
|
||||
// apply mask to image
|
||||
loadMask(dc);
|
||||
if (alpha_mask) alpha_mask->setAlpha(image);
|
||||
bitmap = Bitmap(image);
|
||||
if (!bitmap.Ok()) {
|
||||
angle = a;
|
||||
is_default = false;
|
||||
Image image;
|
||||
loadMask(dc);
|
||||
// load from file
|
||||
if (!value().filename.empty()) {
|
||||
try {
|
||||
InputStreamP image_file = getSet().openIn(value().filename);
|
||||
if (image.LoadFile(*image_file)) {
|
||||
image.Rescale(w, h);
|
||||
}
|
||||
} catch (Error e) {
|
||||
handle_error(e, false, false); // don't handle now, we are in onPaint
|
||||
}
|
||||
} catch (Error e) {
|
||||
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 (!bitmap.Ok()) {
|
||||
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()) {
|
||||
dc.DrawBitmap(bitmap, style().getPos());
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
bool ImageValueViewer::containsPoint(const RealPoint& p) const {
|
||||
@@ -77,10 +120,13 @@ void ImageValueViewer::onValueChange() {
|
||||
bitmap = Bitmap();
|
||||
}
|
||||
|
||||
void ImageValueViewer::onStyleChange(bool already_prepared) {
|
||||
bitmap = Bitmap();
|
||||
alpha_mask = AlphaMaskP(); // TODO: only reload whatever has changed
|
||||
if (!already_prepared) viewer.redraw(*this);
|
||||
void ImageValueViewer::onStyleChange(int changes) {
|
||||
if ((changes & CHANGE_MASK) ||
|
||||
((changes & CHANGE_DEFAULT) && is_default)) {
|
||||
bitmap = Bitmap();
|
||||
}
|
||||
if (changes & CHANGE_MASK) alpha_mask = AlphaMaskP();
|
||||
ValueViewer::onStyleChange(changes);
|
||||
}
|
||||
|
||||
void ImageValueViewer::loadMask(const Rotation& rot) const {
|
||||
|
||||
@@ -27,10 +27,12 @@ class ImageValueViewer : public ValueViewer {
|
||||
virtual bool containsPoint(const RealPoint& p) const;
|
||||
|
||||
virtual void onValueChange();
|
||||
virtual void onStyleChange(bool);
|
||||
virtual void onStyleChange(int);
|
||||
|
||||
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;
|
||||
|
||||
void loadMask(const Rotation& rot) const;
|
||||
|
||||
@@ -44,36 +44,32 @@ void MultipleChoiceValueViewer::draw(RotatedDC& dc) {
|
||||
if (style().render_style & RENDER_IMAGE) {
|
||||
// draw image
|
||||
style().initImage();
|
||||
ScriptableImage& img = style().image;
|
||||
CachedScriptableImage& img = style().image;
|
||||
Context& ctx = viewer.getContext();
|
||||
ctx.setVariable(_("input"), to_script(value().value()));
|
||||
img.update(ctx);
|
||||
if (img.isReady()) {
|
||||
GeneratedImage::Options img_options(0,0, viewer.stylesheet.get(), &getSet());
|
||||
if (nativeLook()) {
|
||||
img_options.width = img_options.height = 16;
|
||||
img_options.preserve_aspect = ASPECT_BORDER;
|
||||
} 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?
|
||||
GeneratedImage::Options img_options;
|
||||
getOptions(dc, img_options);
|
||||
// Generate image/bitmap
|
||||
ImageCombine combine = style().combine;
|
||||
style().loadMask(*viewer.stylesheet);
|
||||
if (style().mask.Ok()) {
|
||||
set_alpha(image, style().mask);
|
||||
Bitmap bitmap; Image image;
|
||||
img.generateCached(img_options, &style().mask, &combine, &bitmap, &image);
|
||||
if (bitmap.Ok()) {
|
||||
// just draw it
|
||||
dc.DrawPreRotatedBitmap(bitmap,
|
||||
align_in_rect(style().alignment, dc.trInvNoNeg(RealSize(bitmap)), style().getRect())
|
||||
);
|
||||
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;
|
||||
}
|
||||
// draw
|
||||
dc.DrawImage(image,
|
||||
align_in_rect(style().alignment, RealSize(image.GetWidth(), image.GetHeight()), style().getRect()),
|
||||
combine == COMBINE_NORMAL ? style().combine : combine,
|
||||
style().angle
|
||||
);
|
||||
margin = dc.trInvS(image.GetWidth()) + 1;
|
||||
}
|
||||
}
|
||||
if (style().render_style & RENDER_TEXT) {
|
||||
@@ -96,10 +92,11 @@ void MultipleChoiceValueViewer::drawChoice(RotatedDC& dc, RealPoint& pos, const
|
||||
if (style().render_style & RENDER_IMAGE) {
|
||||
map<String,ScriptableImage>::iterator it = style().choice_images.find(cannocial_name_form(choice));
|
||||
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();
|
||||
// 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())));
|
||||
}
|
||||
}
|
||||
@@ -114,3 +111,20 @@ void MultipleChoiceValueViewer::drawChoice(RotatedDC& dc, RealPoint& pos, const
|
||||
// next position
|
||||
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
|
||||
private:
|
||||
void drawChoice(RotatedDC& dc, RealPoint& pos, const String& choice, bool active = true);
|
||||
void getOptions(Rotation& rot, GeneratedImage::Options& opts);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -42,9 +42,9 @@ void TextValueViewer::onValueChange() {
|
||||
v.reset(false);
|
||||
}
|
||||
|
||||
void TextValueViewer::onStyleChange(bool already_prepared) {
|
||||
void TextValueViewer::onStyleChange(int changes) {
|
||||
v.reset(true);
|
||||
if (!already_prepared) viewer.redraw(*this);
|
||||
ValueViewer::onStyleChange(changes);
|
||||
}
|
||||
|
||||
void TextValueViewer::onAction(const Action&, bool undone) {
|
||||
|
||||
@@ -24,7 +24,7 @@ class TextValueViewer : public ValueViewer {
|
||||
virtual bool prepare(RotatedDC& dc);
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual void onValueChange();
|
||||
virtual void onStyleChange(bool);
|
||||
virtual void onStyleChange(int);
|
||||
virtual void onAction(const Action&, bool undone);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -26,6 +26,7 @@ Set& ValueViewer::getSet() const { return *viewer.getSet(); }
|
||||
|
||||
void ValueViewer::setValue(const ValueP& value) {
|
||||
assert(value->fieldP == styleP->fieldP); // matching field
|
||||
if (valueP == value) return;
|
||||
valueP = value;
|
||||
onValueChange();
|
||||
}
|
||||
@@ -55,6 +56,12 @@ bool ValueViewer::isCurrent() const {
|
||||
return viewer.focusedViewer() == this;
|
||||
}
|
||||
|
||||
void ValueViewer::onStyleChange(int changes) {
|
||||
if (!(changes & CHANGE_ALREADY_PREPARED)) {
|
||||
viewer.redraw(*this);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Type dispatch
|
||||
|
||||
#define IMPLEMENT_MAKE_VIEWER(Type) \
|
||||
|
||||
@@ -57,8 +57,8 @@ class ValueViewer : public StyleListener {
|
||||
*/
|
||||
virtual void onValueChange() {}
|
||||
/// 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() */
|
||||
virtual void onStyleChange(bool already_prepared) {}
|
||||
/** Default: redraws the viewer if needed */
|
||||
virtual void onStyleChange(int changes);
|
||||
/// Called when an action is performed on the associated value
|
||||
virtual void onAction(const Action&, bool undone) { onValueChange(); }
|
||||
|
||||
|
||||
+97
-14
@@ -41,14 +41,7 @@ GeneratedImageP image_from_script(const ScriptValueP& value) {
|
||||
|
||||
// ----------------------------------------------------------------------------- : ScriptableImage
|
||||
|
||||
Image ScriptableImage::generate(const GeneratedImage::Options& options, bool cache) 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;
|
||||
}
|
||||
Image ScriptableImage::generate(const GeneratedImage::Options& options) const {
|
||||
// generate
|
||||
Image image;
|
||||
if (isReady()) {
|
||||
@@ -64,14 +57,11 @@ Image ScriptableImage::generate(const GeneratedImage::Options& options, bool cac
|
||||
i.SetAlpha(0,0,0);
|
||||
image = i;
|
||||
}
|
||||
image = conform_image(image, options);
|
||||
// cache? and return
|
||||
if (cache) cached = image;
|
||||
return image;
|
||||
return conform_image(image, options);
|
||||
}
|
||||
|
||||
ImageCombine ScriptableImage::combine() const {
|
||||
if (!isReady()) return COMBINE_NORMAL;
|
||||
if (!isReady()) return COMBINE_DEFAULT;
|
||||
return value->combine();
|
||||
}
|
||||
|
||||
@@ -80,7 +70,6 @@ bool ScriptableImage::update(Context& ctx) {
|
||||
GeneratedImageP new_value = image_from_script(script.invoke(ctx));
|
||||
if (!new_value || !value || *new_value != *value) {
|
||||
value = new_value;
|
||||
cached = Image();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -118,3 +107,97 @@ template <> void Writer::handle(const ScriptableImage& s) {
|
||||
template <> void GetDefaultMember::handle(const ScriptableImage& s) {
|
||||
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
|
||||
/** Differs from Scriptable<Image> in that:
|
||||
* - A script is always used
|
||||
* - Age is checked, chached images are used if possible
|
||||
* - The image can be scaled
|
||||
*/
|
||||
class ScriptableImage {
|
||||
@@ -37,7 +36,7 @@ class ScriptableImage {
|
||||
inline bool isSet() const { return script || value; }
|
||||
|
||||
/// 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?
|
||||
ImageCombine combine() const;
|
||||
|
||||
@@ -56,10 +55,9 @@ class ScriptableImage {
|
||||
/// Get access to the script, always returns a valid script
|
||||
ScriptP getScriptP();
|
||||
|
||||
private:
|
||||
protected:
|
||||
OptionalScript script; ///< The script, not really optional
|
||||
GeneratedImageP value; ///< The image generator
|
||||
mutable Image cached; ///< The cached actual image
|
||||
|
||||
DECLARE_REFLECTION();
|
||||
};
|
||||
@@ -70,5 +68,38 @@ inline ScriptValueP to_script(const ScriptableImage&) { return script_nil; }
|
||||
/// Convert a script value to a GeneratedImageP
|
||||
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
|
||||
#endif
|
||||
|
||||
@@ -224,9 +224,9 @@ void SetScriptManager::updateStyles(Context& ctx, const IndexMap<FieldP,StyleP>&
|
||||
FOR_EACH_CONST(s, styles) {
|
||||
if (only_content_dependent && !s->content_dependent) continue;
|
||||
try {
|
||||
if (s->update(ctx)) {
|
||||
if (int change = s->update(ctx)) {
|
||||
// style has changed, tell listeners
|
||||
s->tellListeners(only_content_dependent);
|
||||
s->tellListeners(change | (only_content_dependent ? CHANGE_ALREADY_PREPARED : 0) );
|
||||
}
|
||||
} catch (const ScriptError& e) {
|
||||
// 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)
|
||||
: 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
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
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) {
|
||||
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 trY(double s) const { return s * zoomY; }
|
||||
|
||||
/// Translate an angle
|
||||
inline int trAngle(int a) { return (angle + a) % 360; }
|
||||
|
||||
/// Translate a single point
|
||||
RealPoint tr(const RealPoint& p) const;
|
||||
/// Translate a single size, the result may be negative
|
||||
@@ -94,8 +97,7 @@ class Rotation {
|
||||
|
||||
public:
|
||||
/// 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 (angle & 2) != 0; }
|
||||
inline bool sideways() const { return ::sideways(angle); }
|
||||
|
||||
protected:
|
||||
/// Is the x axis 'reversed' (after turning sideways)?
|
||||
@@ -153,7 +155,11 @@ class RotatedDC : public Rotation {
|
||||
/// Draw abitmap, it must already be zoomed!
|
||||
void DrawBitmap(const Bitmap& bitmap, const RealPoint& pos);
|
||||
/// 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 DrawRectangle(const RealRect& r);
|
||||
void DrawRoundedRectangle(const RealRect& r, double radius);
|
||||
|
||||
Reference in New Issue
Block a user