Commit 7fa3d02f authored by twanvl's avatar twanvl

New class CachedScriptableMask: like CachedScriptableImage, only containing an...

New class CachedScriptableMask: like CachedScriptableImage, only containing an AlphaMask instead of an Image/Bitmap.
Use CachedScriptableMask for all masks.

TODO: This introduces some duplicate code in ValueViewers that could be fixed by moving mask to the Style base class.
parent 1098ce52
...@@ -214,11 +214,8 @@ void ChoiceStyle::initImage() { ...@@ -214,11 +214,8 @@ void ChoiceStyle::initImage() {
int 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()
int change = Style ::update(ctx) int change = Style ::update(ctx)
| font .update(ctx) * CHANGE_OTHER; | font .update(ctx) * CHANGE_OTHER
if (mask_filename.update(ctx)) { | mask .update(ctx) * CHANGE_MASK;
change |= CHANGE_MASK;
mask = Image();
}
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;
...@@ -255,13 +252,6 @@ void ChoiceStyle::invalidate() { ...@@ -255,13 +252,6 @@ void ChoiceStyle::invalidate() {
tellListeners(CHANGE_OTHER); tellListeners(CHANGE_OTHER);
} }
void ChoiceStyle::loadMask(Package& pkg) {
if (mask.Ok() || mask_filename().empty()) return;
// load file
InputStreamP image_file = pkg.openIn(mask_filename);
mask.LoadFile(*image_file);
}
IMPLEMENT_REFLECTION_ENUM(ChoicePopupStyle) { IMPLEMENT_REFLECTION_ENUM(ChoicePopupStyle) {
VALUE_N("dropdown", POPUP_DROPDOWN); VALUE_N("dropdown", POPUP_DROPDOWN);
VALUE_N("menu", POPUP_MENU); VALUE_N("menu", POPUP_MENU);
...@@ -293,7 +283,7 @@ IMPLEMENT_REFLECTION(ChoiceStyle) { ...@@ -293,7 +283,7 @@ IMPLEMENT_REFLECTION(ChoiceStyle) {
REFLECT_BASE(Style); REFLECT_BASE(Style);
REFLECT(popup_style); REFLECT(popup_style);
REFLECT(render_style); REFLECT(render_style);
REFLECT_N("mask",mask_filename); REFLECT(mask);
REFLECT(combine); REFLECT(combine);
REFLECT(alignment); REFLECT(alignment);
REFLECT(font); REFLECT(font);
......
...@@ -143,20 +143,17 @@ class ChoiceStyle : public Style { ...@@ -143,20 +143,17 @@ 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)
CachedScriptableImage 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 CachedScriptableMask mask; ///< Mask image
ImageCombine combine; ///< Combining mode for drawing the images ImageCombine combine; ///< Combining mode for drawing the images
Alignment alignment; ///< Alignment of images Alignment alignment; ///< Alignment of images
Image mask; ///< The actual mask image
wxImageList* thumbnails; ///< Thumbnails for the choices wxImageList* thumbnails; ///< Thumbnails for the choices
vector<ThumbnailStatus> thumbnails_status; ///< Which thumbnails are up to date? vector<ThumbnailStatus> thumbnails_status; ///< Which thumbnails are up to date?
// information from image rendering // information from image rendering
double content_width, content_height; ///< Size of the rendered image/text double content_width, content_height; ///< Size of the rendered image/text
/// Load the mask image, if it's not already done
void loadMask(Package& pkg);
/// Initialize image from choice_images /// Initialize image from choice_images
void initImage(); void initImage();
......
...@@ -62,13 +62,13 @@ IMPLEMENT_REFLECTION(ColorStyle) { ...@@ -62,13 +62,13 @@ IMPLEMENT_REFLECTION(ColorStyle) {
REFLECT(right_width); REFLECT(right_width);
REFLECT(top_width); REFLECT(top_width);
REFLECT(bottom_width); REFLECT(bottom_width);
REFLECT_N("mask", mask_filename); REFLECT(mask);
REFLECT(combine); REFLECT(combine);
} }
int ColorStyle::update(Context& ctx) { int ColorStyle::update(Context& ctx) {
return Style ::update(ctx) return Style::update(ctx)
| mask_filename.update(ctx) * CHANGE_MASK; | mask.update(ctx) * CHANGE_MASK;
} }
// ----------------------------------------------------------------------------- : ColorValue // ----------------------------------------------------------------------------- : ColorValue
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <util/defaultable.hpp> #include <util/defaultable.hpp>
#include <data/field.hpp> #include <data/field.hpp>
#include <script/scriptable.hpp> #include <script/scriptable.hpp>
#include <script/image.hpp>
// ----------------------------------------------------------------------------- : ColorField // ----------------------------------------------------------------------------- : ColorField
...@@ -56,13 +57,13 @@ class ColorStyle : public Style { ...@@ -56,13 +57,13 @@ class ColorStyle : public Style {
ColorStyle(const ColorFieldP& field); ColorStyle(const ColorFieldP& field);
DECLARE_STYLE_TYPE(Color); DECLARE_STYLE_TYPE(Color);
double radius; ///< Radius of round corners double radius; ///< Radius of round corners
double left_width; ///< Width of the colored region on the left side double left_width; ///< Width of the colored region on the left side
double right_width; ///< Width of the colored region on the right side double right_width; ///< Width of the colored region on the right side
double top_width; ///< Width of the colored region on the top side double top_width; ///< Width of the colored region on the top side
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 CachedScriptableMask mask; ///< Mask image
ImageCombine combine; ///< How to combine image with the background ImageCombine combine; ///< How to combine image with the background
virtual int update(Context&); virtual int update(Context&);
}; };
......
...@@ -23,13 +23,13 @@ IMPLEMENT_REFLECTION(ImageField) { ...@@ -23,13 +23,13 @@ IMPLEMENT_REFLECTION(ImageField) {
IMPLEMENT_REFLECTION(ImageStyle) { IMPLEMENT_REFLECTION(ImageStyle) {
REFLECT_BASE(Style); REFLECT_BASE(Style);
REFLECT_N("mask", mask_filename); REFLECT(mask);
REFLECT_N("default", default_image); REFLECT_N("default", default_image);
} }
int ImageStyle::update(Context& ctx) { int ImageStyle::update(Context& ctx) {
return Style ::update(ctx) return Style ::update(ctx)
| mask_filename.update(ctx) * CHANGE_MASK | mask .update(ctx) * CHANGE_MASK
| default_image.update(ctx) * CHANGE_DEFAULT; | default_image.update(ctx) * CHANGE_DEFAULT;
} }
......
...@@ -35,8 +35,8 @@ class ImageStyle : public Style { ...@@ -35,8 +35,8 @@ 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<String> mask_filename; ///< Filename for a mask image CachedScriptableMask mask; ///< Mask image
ScriptableImage default_image; ///< Placeholder ScriptableImage default_image; ///< Placeholder
virtual int update(Context&); virtual int update(Context&);
}; };
......
...@@ -35,7 +35,7 @@ class GeneratedImage : public ScriptValue { ...@@ -35,7 +35,7 @@ class GeneratedImage : public ScriptValue {
mutable int width, height; ///< Width to force the image to, or 0 to keep the width of the input mutable int width, height; ///< Width to force the image to, or 0 to keep the width of the input
///< In that case, width and height will be later set to the actual size ///< In that case, width and height will be later set to the actual size
double zoom; ///< Zoom factor to use, when witdth=height=0 double zoom; ///< Zoom factor to use, when width=height=0
int angle; ///< Angle to rotate image by afterwards int angle; ///< Angle to rotate image by afterwards
PreserveAspect preserve_aspect; PreserveAspect preserve_aspect;
bool saturate; bool saturate;
...@@ -55,9 +55,10 @@ class GeneratedImage : public ScriptValue { ...@@ -55,9 +55,10 @@ class GeneratedImage : public ScriptValue {
/// Can this image be generated safely from another thread? /// Can this image be generated safely from another thread?
virtual bool threadSafe() const { return true; } virtual bool threadSafe() const { return true; }
/// Is this image specific to the set (the local_package)? /// Is this image specific to the set (the local_package)?
virtual bool local() const { return false; } virtual bool local() const { return false; }
/// Is this image blank?
virtual bool isBlank() const { return false; }
virtual ScriptType type() const; virtual ScriptType type() const;
virtual String typeName() const; virtual String typeName() const;
...@@ -88,6 +89,7 @@ class BlankImage : public GeneratedImage { ...@@ -88,6 +89,7 @@ class BlankImage : public GeneratedImage {
public: public:
virtual Image generate(const Options&) const; virtual Image generate(const Options&) const;
virtual bool operator == (const GeneratedImage& that) const; virtual bool operator == (const GeneratedImage& that) const;
virtual bool isBlank() const { return true; }
// Why is this not thread safe? What is GTK smoking? // Why is this not thread safe? What is GTK smoking?
#ifdef __WXGTK__ #ifdef __WXGTK__
......
...@@ -167,8 +167,9 @@ class AlphaMask : public IntrusivePtrBase<AlphaMask> { ...@@ -167,8 +167,9 @@ class AlphaMask : public IntrusivePtrBase<AlphaMask> {
/// Apply the alpha mask to a bitmap /// Apply the alpha mask to a bitmap
void setAlpha(Bitmap& b) const; void setAlpha(Bitmap& b) const;
/// Is the given location fully transparent? /// Is the given location opaque (not fully transparent)? when the mask were stretched to size
bool isTransparent(int x, int y) const; bool isOpaque(const RealPoint& p, const RealSize& size) const;
bool isOpaque(int x, int y) const;
/// Determine a convex hull polygon *around* the mask /// Determine a convex hull polygon *around* the mask
void convexHull(vector<wxPoint>& points) const; void convexHull(vector<wxPoint>& points) const;
...@@ -178,9 +179,9 @@ class AlphaMask : public IntrusivePtrBase<AlphaMask> { ...@@ -178,9 +179,9 @@ class AlphaMask : public IntrusivePtrBase<AlphaMask> {
/// Returns the start of a row, when the mask were stretched to size /// Returns the start of a row, when the mask were stretched to size
/** This is: the x coordinate of the first non-transparent pixel */ /** This is: the x coordinate of the first non-transparent pixel */
double rowLeft (double y, RealSize size) const; double rowLeft (double y, const RealSize& size) const;
/// Returns the end of a row, when the mask were stretched to size /// Returns the end of a row, when the mask were stretched to size
double rowRight(double y, RealSize size) const; double rowRight(double y, const RealSize& size) const;
/// Does this mask have the given size? /// Does this mask have the given size?
inline bool hasSize(const wxSize& compare_size) const { return size == compare_size; } inline bool hasSize(const wxSize& compare_size) const { return size == compare_size; }
......
...@@ -17,9 +17,13 @@ AlphaMask::AlphaMask(const Image& img) : alpha(nullptr), lefts(nullptr), rights( ...@@ -17,9 +17,13 @@ AlphaMask::AlphaMask(const Image& img) : alpha(nullptr), lefts(nullptr), rights(
load(img); load(img);
} }
AlphaMask::~AlphaMask() { AlphaMask::~AlphaMask() {
delete[] alpha; clear();
delete[] lefts; }
delete[] rights;
void AlphaMask::clear() {
delete[] alpha; alpha = nullptr;
delete[] lefts; lefts = nullptr;
delete[] rights; rights = nullptr;
} }
void AlphaMask::load(const Image& img) { void AlphaMask::load(const Image& img) {
...@@ -54,10 +58,14 @@ void AlphaMask::setAlpha(Bitmap& bmp) const { ...@@ -54,10 +58,14 @@ void AlphaMask::setAlpha(Bitmap& bmp) const {
bmp = Bitmap(img); bmp = Bitmap(img);
} }
bool AlphaMask::isTransparent(int x, int y) const { bool AlphaMask::isOpaque(int x, int y) const {
if (x < 0 || y < 0 || x >= size.x || y >= size.y) return false; if (x < 0 || y < 0 || x >= size.x || y >= size.y) return false;
if (!alpha) return true; if (!alpha) return true;
return alpha[x + y * size.x] < 20; return alpha[x + y * size.x] >= 20;
}
bool AlphaMask::isOpaque(const RealPoint& p, const RealSize& resize) const {
return isOpaque((int)(p.x * size.x / resize.width)
,(int)(p.y * size.y / resize.height));
} }
/// Do the points form a (counter??)clockwise angle? /// Do the points form a (counter??)clockwise angle?
...@@ -72,6 +80,10 @@ void make_convex(vector<wxPoint>& points) { ...@@ -72,6 +80,10 @@ void make_convex(vector<wxPoint>& points) {
points.erase(points.end() - 2); points.erase(points.end() - 2);
} }
} }
void add_convex_point(vector<wxPoint>& points, int x, int y) {
points.push_back(wxPoint(x,y));
make_convex(points);
}
void AlphaMask::convexHull(vector<wxPoint>& points) const { void AlphaMask::convexHull(vector<wxPoint>& points) const {
if (!alpha) throw InternalError(_("AlphaMask::convexHull")); if (!alpha) throw InternalError(_("AlphaMask::convexHull"));
...@@ -84,36 +96,31 @@ void AlphaMask::convexHull(vector<wxPoint>& points) const { ...@@ -84,36 +96,31 @@ void AlphaMask::convexHull(vector<wxPoint>& points) const {
miny = min(miny,y); miny = min(miny,y);
maxy = y; maxy = y;
if (y == miny) { if (y == miny) {
points.push_back(wxPoint(x-1,y-1)); add_convex_point(points, x-1, y-1);
} }
points.push_back(wxPoint(x-1,y)); add_convex_point(points, x-1, y);
make_convex(points);
lastx = x; lastx = x;
break; break;
} }
} }
} }
if (maxy == -1) return; // No image if (maxy == -1) return; // No image
points.push_back(wxPoint(lastx-1,maxy+1)); add_convex_point(points, lastx-1, maxy+1);
make_convex(points);
// Right side, bottom to top // Right side, bottom to top
for (int y = maxy ; y >= miny ; --y) { for (int y = maxy ; y >= miny ; --y) {
for (int x = size.x - 1 ; x >= 0 ; --x) { for (int x = size.x - 1 ; x >= 0 ; --x) {
if (alpha[x + y * size.x] >= 20) { if (alpha[x + y * size.x] >= 20) {
// opaque pixel // opaque pixel
if (y == maxy) { if (y == maxy) {
points.push_back(wxPoint(x+1,y+1)); add_convex_point(points, x+1, y+1);
make_convex(points);
} }
points.push_back(wxPoint(x+1,y)); add_convex_point(points, x+1, y);
make_convex(points);
lastx = x; lastx = x;
break; break;
} }
} }
} }
points.push_back(wxPoint(lastx+1,miny-1)); add_convex_point(points, lastx+1, miny-1);
make_convex(points);
} }
Image AlphaMask::colorImage(const Color& color) const { Image AlphaMask::colorImage(const Color& color) const {
...@@ -142,7 +149,7 @@ void AlphaMask::loadRowSizes() const { ...@@ -142,7 +149,7 @@ void AlphaMask::loadRowSizes() const {
} }
} }
double AlphaMask::rowLeft (double y, RealSize resize) const { double AlphaMask::rowLeft (double y, const RealSize& resize) const {
loadRowSizes(); loadRowSizes();
if (!lefts || y < 0 || y >= resize.height) { if (!lefts || y < 0 || y >= resize.height) {
// no mask, or outside it // no mask, or outside it
...@@ -151,7 +158,7 @@ double AlphaMask::rowLeft (double y, RealSize resize) const { ...@@ -151,7 +158,7 @@ double AlphaMask::rowLeft (double y, RealSize resize) const {
return lefts[(int)(y * resize.height / size.y)] * resize.width / size.x; return lefts[(int)(y * resize.height / size.y)] * resize.width / size.x;
} }
double AlphaMask::rowRight(double y, RealSize resize) const { double AlphaMask::rowRight(double y, const RealSize& resize) const {
loadRowSizes(); loadRowSizes();
if (!rights || y < 0 || y >= resize.height) { if (!rights || y < 0 || y >= resize.height) {
// no mask, or outside it // no mask, or outside it
......
...@@ -54,7 +54,7 @@ void ImageSlice::constrain(PreferedProperty prefer) { ...@@ -54,7 +54,7 @@ void ImageSlice::constrain(PreferedProperty prefer) {
Image ImageSlice::getSlice() const { Image ImageSlice::getSlice() const {
if (selection.width == target_size.GetWidth() && selection.height == target_size.GetHeight() && selection.x == 0 && selection.y == 0) { if (selection.width == target_size.GetWidth() && selection.height == target_size.GetHeight() && selection.x == 0 && selection.y == 0) {
// exactly the right size // exactly the right size
return source; return source.GetSubImage(selection);
} }
Image target(target_size.GetWidth(), target_size.GetHeight(), false); Image target(target_size.GetWidth(), target_size.GetHeight(), false);
if (sharpen && sharpen_amount > 0 && sharpen_amount <= 100) { if (sharpen && sharpen_amount > 0 && sharpen_amount <= 100) {
...@@ -75,7 +75,7 @@ DEFINE_EVENT_TYPE(EVENT_SLICE_CHANGED); ...@@ -75,7 +75,7 @@ DEFINE_EVENT_TYPE(EVENT_SLICE_CHANGED);
// ----------------------------------------------------------------------------- : ImageSliceWindow // ----------------------------------------------------------------------------- : ImageSliceWindow
ImageSliceWindow::ImageSliceWindow(Window* parent, const Image& source, const wxSize& target_size, const AlphaMaskP& mask) ImageSliceWindow::ImageSliceWindow(Window* parent, const Image& source, const wxSize& target_size, const AlphaMask& mask)
: wxDialog(parent,wxID_ANY,_TITLE_("slice image"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE) : wxDialog(parent,wxID_ANY,_TITLE_("slice image"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE)
, slice(source, target_size) , slice(source, target_size)
{ {
...@@ -277,6 +277,7 @@ void ImageSliceWindow::updateControls() { ...@@ -277,6 +277,7 @@ void ImageSliceWindow::updateControls() {
size->SetSelection(2); // force to fit size->SetSelection(2); // force to fit
} else if (slice.selection.width <= slice.source.GetWidth() && } else if (slice.selection.width <= slice.source.GetWidth() &&
slice.selection.height <= slice.source.GetHeight() && slice.selection.height <= slice.source.GetHeight() &&
fabs(slice.zoomX() - slice.zoomY()) < 0.01 &&
( (slice.selection.x == 0 && slice.selection.width == slice.source.GetWidth()) ( (slice.selection.x == 0 && slice.selection.width == slice.source.GetWidth())
||(slice.selection.y == 0 && slice.selection.height == slice.source.GetHeight()))) { ||(slice.selection.y == 0 && slice.selection.height == slice.source.GetHeight()))) {
size->SetSelection(1); // size to fit size->SetSelection(1); // size to fit
...@@ -334,7 +335,7 @@ END_EVENT_TABLE () ...@@ -334,7 +335,7 @@ END_EVENT_TABLE ()
// ----------------------------------------------------------------------------- : ImageSlicePreview // ----------------------------------------------------------------------------- : ImageSlicePreview
ImageSlicePreview::ImageSlicePreview(Window* parent, int id, ImageSlice& slice, const AlphaMaskP& mask) ImageSlicePreview::ImageSlicePreview(Window* parent, int id, ImageSlice& slice, const AlphaMask& mask)
: wxControl(parent, id) : wxControl(parent, id)
, slice(slice) , slice(slice)
, mask(mask) , mask(mask)
...@@ -359,9 +360,8 @@ void ImageSlicePreview::onPaint(wxPaintEvent&) { ...@@ -359,9 +360,8 @@ void ImageSlicePreview::onPaint(wxPaintEvent&) {
void ImageSlicePreview::draw(DC& dc) { void ImageSlicePreview::draw(DC& dc) {
if (!bitmap.Ok()) { if (!bitmap.Ok()) {
Image image = slice.getSlice(); Image image = slice.getSlice();
if (mask && mask->hasSize(slice.target_size)) { assert(image.GetWidth() == slice.target_size.GetWidth() && image.GetHeight() == slice.target_size.GetHeight());
mask->setAlpha(image); mask.setAlpha(image);
}
if (image.HasAlpha()) { if (image.HasAlpha()) {
// create bitmap // create bitmap
bitmap = Bitmap(image.GetWidth(), image.GetHeight()); bitmap = Bitmap(image.GetWidth(), image.GetHeight());
......
...@@ -59,7 +59,7 @@ class ImageSlice { ...@@ -59,7 +59,7 @@ class ImageSlice {
/// Dialog for selecting a slice of an image /// Dialog for selecting a slice of an image
class ImageSliceWindow : public wxDialog { class ImageSliceWindow : public wxDialog {
public: public:
ImageSliceWindow(Window* parent, const Image& source, const wxSize& target_size, const AlphaMaskP& target_mask); ImageSliceWindow(Window* parent, const Image& source, const wxSize& target_size, const AlphaMask& target_mask);
/// Return the sliced image /// Return the sliced image
Image getImage() const; Image getImage() const;
...@@ -115,7 +115,7 @@ class ImageSliceWindow : public wxDialog { ...@@ -115,7 +115,7 @@ class ImageSliceWindow : public wxDialog {
/// A preview of the sliced image /// A preview of the sliced image
class ImageSlicePreview : public wxControl { class ImageSlicePreview : public wxControl {
public: public:
ImageSlicePreview(Window* parent, int id, ImageSlice& slice, const AlphaMaskP& mask); ImageSlicePreview(Window* parent, int id, ImageSlice& slice, const AlphaMask& mask);
/// Notify that the slice was updated /// Notify that the slice was updated
void update(); void update();
...@@ -124,7 +124,7 @@ class ImageSlicePreview : public wxControl { ...@@ -124,7 +124,7 @@ class ImageSlicePreview : public wxControl {
private: private:
Bitmap bitmap; Bitmap bitmap;
ImageSlice& slice; ImageSlice& slice;
AlphaMaskP mask; const AlphaMask& mask;
bool mouse_down; bool mouse_down;
int mouseX, mouseY; ///< starting mouse position int mouseX, mouseY; ///< starting mouse position
......
...@@ -29,17 +29,10 @@ bool ImageValueEditor::onLeftDClick(const RealPoint&, wxMouseEvent&) { ...@@ -29,17 +29,10 @@ bool ImageValueEditor::onLeftDClick(const RealPoint&, wxMouseEvent&) {
void ImageValueEditor::sliceImage(const Image& image) { void ImageValueEditor::sliceImage(const Image& image) {
if (!image.Ok()) return; if (!image.Ok()) return;
// mask? // mask
AlphaMaskP mask; GeneratedImage::Options options((int)style().width, (int)style().height, &viewer.getStylePackage(), &viewer.getLocalPackage());
if (!style().mask_filename().empty()) { AlphaMask mask;
Image mask_image; style().mask.getNoCache(options,mask);
InputStreamP image_file = getStylePackage().openIn(style().mask_filename);
if (mask_image.LoadFile(*image_file)) {
Image resampled(style().width, style().height);
resample(mask_image, resampled);
mask = new_intrusive1<AlphaMask>(resampled);
}
}
// slice // slice
ImageSliceWindow s(wxGetTopLevelParent(&editor()), image, style().getSize(), mask); ImageSliceWindow s(wxGetTopLevelParent(&editor()), image, style().getSize(), mask);
// clicked ok? // clicked ok?
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include <render/value/choice.hpp> #include <render/value/choice.hpp>
#include <render/card/viewer.hpp> #include <render/card/viewer.hpp>
DECLARE_TYPEOF_COLLECTION(wxPoint);
// ----------------------------------------------------------------------------- : ChoiceValueViewer // ----------------------------------------------------------------------------- : ChoiceValueViewer
IMPLEMENT_VALUE_VIEWER(Choice); IMPLEMENT_VALUE_VIEWER(Choice);
...@@ -20,11 +22,48 @@ bool ChoiceValueViewer::prepare(RotatedDC& dc) { ...@@ -20,11 +22,48 @@ bool ChoiceValueViewer::prepare(RotatedDC& dc) {
return prepare_choice_viewer(dc, *this, style(), value().value()); return prepare_choice_viewer(dc, *this, style(), value().value());
} }
void ChoiceValueViewer::draw(RotatedDC& dc) { void ChoiceValueViewer::draw(RotatedDC& dc) {
drawFieldBorder(dc); int w = max(0,(int)dc.trX(style().width)), h = max(0,(int)dc.trY(style().height));
const AlphaMask& alpha_mask = getMask(w,h);
drawFieldBorder(dc, alpha_mask);
if (style().render_style & RENDER_HIDDEN) return; if (style().render_style & RENDER_HIDDEN) return;
draw_choice_viewer(dc, *this, style(), value().value()); draw_choice_viewer(dc, *this, style(), value().value());
} }
void ChoiceValueViewer::drawFieldBorder(RotatedDC& dc, const AlphaMask& alpha_mask) {
if (!alpha_mask.isLoaded()) {
ValueViewer::drawFieldBorder(dc);
} else if (setFieldBorderPen(dc)) {
dc.SetBrush(*wxTRANSPARENT_BRUSH);
vector<wxPoint> points;
alpha_mask.convexHull(points);
if (points.size() < 3) return;
FOR_EACH(p, points) p = dc.trPixelNoZoom(RealPoint(p.x,p.y));
dc.getDC().DrawPolygon((int)points.size(), &points[0]);
}
}
bool ChoiceValueViewer::containsPoint(const RealPoint& p) const {
// check against mask
return getMask(0,0).isOpaque(p, style().getSize());
}
void ChoiceValueViewer::onStyleChange(int changes) {
if (changes & CHANGE_MASK) style().image.clearCache();
ValueViewer::onStyleChange(changes);
}
const AlphaMask& ChoiceValueViewer::getMask(int w, int h) const {
GeneratedImage::Options opts;
opts.package = &viewer.getStylePackage();
opts.local_package = &viewer.getLocalPackage();
opts.angle = 0;
opts.width = w;
opts.height = h;
return style().mask.get(opts);
}
// ----------------------------------------------------------------------------- : Generic draw/prepare
bool prepare_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& style, const String& value) { bool prepare_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& style, const String& value) {
if (style.render_style & RENDER_IMAGE) { if (style.render_style & RENDER_IMAGE) {
style.initImage(); style.initImage();
...@@ -38,7 +77,6 @@ bool prepare_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& styl ...@@ -38,7 +77,6 @@ bool prepare_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& styl
// Generate image/bitmap (whichever is available) // Generate image/bitmap (whichever is available)
// don't worry, we cache the image // don't worry, we cache the image
ImageCombine combine = style.combine; ImageCombine combine = style.combine;
style.loadMask(viewer.getStylePackage());
Bitmap bitmap; Image image; Bitmap bitmap; Image image;
RealSize size; RealSize size;
img.generateCached(img_options, &style.mask, &combine, &bitmap, &image, &size); img.generateCached(img_options, &style.mask, &combine, &bitmap, &image, &size);
...@@ -70,7 +108,6 @@ void draw_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& style, ...@@ -70,7 +108,6 @@ void draw_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& style,
get_options(dc, viewer, style, img_options); get_options(dc, viewer, style, img_options);
// Generate image/bitmap // Generate image/bitmap
ImageCombine combine = style.combine; ImageCombine combine = style.combine;
style.loadMask(viewer.getStylePackage());
Bitmap bitmap; Image image; Bitmap bitmap; Image image;
RealSize size; RealSize size;
img.generateCached(img_options, &style.mask, &combine, &bitmap, &image, &size); img.generateCached(img_options, &style.mask, &combine, &bitmap, &image, &size);
...@@ -112,8 +149,3 @@ void get_options(Rotation& rot, ValueViewer& viewer, const ChoiceStyle& style, G ...@@ -112,8 +149,3 @@ void get_options(Rotation& rot, ValueViewer& viewer, const ChoiceStyle& style, G
opts.preserve_aspect = (style.alignment & ALIGN_STRETCH) ? ASPECT_STRETCH : ASPECT_FIT; opts.preserve_aspect = (style.alignment & ALIGN_STRETCH) ? ASPECT_STRETCH : ASPECT_FIT;
} }
} }
void ChoiceValueViewer::onStyleChange(int changes) {
if (changes & CHANGE_MASK) style().image.clearCache();
ValueViewer::onStyleChange(changes);
}
...@@ -23,10 +23,19 @@ class ChoiceValueViewer : public ValueViewer { ...@@ -23,10 +23,19 @@ 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(int); virtual void onStyleChange(int);
virtual bool containsPoint(const RealPoint& p) const;
private:
/// Draws a border around the field
void drawFieldBorder(RotatedDC& dc, const AlphaMask& alpha_mask);
/// Load the AlphaMask for this field
const AlphaMask& getMask(int w, int h) const;
}; };
bool prepare_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& style, const String& value); bool prepare_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& style, const String& value);
void draw_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& style, const String& value); void draw_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& style, const String& value);
const AlphaMask& get_mask(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& style, int w, int h);
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <render/card/viewer.hpp> #include <render/card/viewer.hpp>
DECLARE_TYPEOF_COLLECTION(ColorField::ChoiceP); DECLARE_TYPEOF_COLLECTION(ColorField::ChoiceP);
DECLARE_TYPEOF_COLLECTION(wxPoint);
// ----------------------------------------------------------------------------- : ColorValueViewer // ----------------------------------------------------------------------------- : ColorValueViewer
...@@ -43,9 +44,10 @@ void ColorValueViewer::draw(RotatedDC& dc) { ...@@ -43,9 +44,10 @@ void ColorValueViewer::draw(RotatedDC& dc) {
dc.DrawText(color_name, RealPoint(43, 3)); dc.DrawText(color_name, RealPoint(43, 3));
} else { } else {
// is there a mask? // is there a mask?
loadMask(dc); int w = max(0,(int)dc.trX(style().width)), h = max(0,(int)dc.trY(style().height));
if (alpha_mask) { const AlphaMask& alpha_mask = getMask(w,h);
dc.DrawImage(alpha_mask->colorImage(value().value()), RealPoint(0,0), style().combine); if (alpha_mask.isLoaded()) {
dc.DrawImage(alpha_mask.colorImage(value().value()), RealPoint(0,0), style().combine);
} else { } else {
// do we need clipping? // do we need clipping?
bool clip = style().left_width < style().width && style().right_width < style().width && bool clip = style().left_width < style().width && style().right_width < style().width &&
...@@ -64,43 +66,45 @@ void ColorValueViewer::draw(RotatedDC& dc) { ...@@ -64,43 +66,45 @@ void ColorValueViewer::draw(RotatedDC& dc) {
dc.DrawRoundedRectangle(style().getInternalRect(), style().radius); dc.DrawRoundedRectangle(style().getInternalRect(), style().radius);
if (clip) dc.getDC().DestroyClippingRegion(); if (clip) dc.getDC().DestroyClippingRegion();
} }
drawFieldBorder(dc, alpha_mask);
}
}
void ColorValueViewer::drawFieldBorder(RotatedDC& dc, const AlphaMask& alpha_mask) {
if (!alpha_mask.isLoaded()) {
ValueViewer::drawFieldBorder(dc);
} else if (setFieldBorderPen(dc)) {
dc.SetBrush(*wxTRANSPARENT_BRUSH);
vector<wxPoint> points;
alpha_mask.convexHull(points);
if (points.size() < 3) return;
FOR_EACH(p, points) p = dc.trPixelNoZoom(RealPoint(p.x,p.y));
dc.getDC().DrawPolygon((int)points.size(), &points[0]);
} }
} }
bool ColorValueViewer::containsPoint(const RealPoint& p) const { bool ColorValueViewer::containsPoint(const RealPoint& p) const {
// distance to each side
double left = p.x, right = style().width - p.x - 1;
double top = p.y, bottom = style().height - p.y - 1;
if (left < 0 || right < 0 || top < 0 || bottom < 0) return false; // outside bounding box
// check against mask // check against mask
if (!style().mask_filename().empty()) loadMask(getRotation()); const AlphaMask& alpha_mask = getMask(0,0);
if (alpha_mask) { if (alpha_mask.isLoaded()) {
return !alpha_mask->isTransparent((int)left, (int)top); // check against mask
return alpha_mask.isOpaque(p, style().getSize());
} else { } else {
double left = p.x, right = style().width - p.x - 1;
double top = p.y, bottom = style().height - p.y - 1;
if (left < 0 || right < 0 || top < 0 || bottom < 0) return false; // outside bounding box
// check against border // check against border
if (left >= style().left_width && right >= style().right_width && // outside horizontal border return left < style().left_width || right < style().right_width // inside horizontal border
top >= style().top_width && bottom >= style().bottom_width) { // outside vertical border || top < style().top_width || bottom < style().bottom_width; // inside vertical border
return false;
}
return true;
} }
} }
void ColorValueViewer::onStyleChange(int changes) { const AlphaMask& ColorValueViewer::getMask(int w, int h) const {
if (changes & CHANGE_MASK) alpha_mask = AlphaMaskP(); GeneratedImage::Options opts;
ValueViewer::onStyleChange(changes); opts.package = &viewer.getStylePackage();
} opts.local_package = &viewer.getLocalPackage();
opts.angle = 0;
void ColorValueViewer::loadMask(const Rotation& rot) const { opts.width = w;
if (style().mask_filename().empty()) return; // no mask opts.height = h;
int w = (int) rot.trX(rot.getWidth()), h = (int) rot.trY(rot.getHeight()); return style().mask.get(opts);
if (alpha_mask && alpha_mask->hasSize(wxSize(w,h))) return; // mask loaded and right size
// (re) load the mask
Image image;
InputStreamP image_file = getStylePackage().openIn(style().mask_filename);
if (image.LoadFile(*image_file)) {
Image resampled(w,h);
resample(image, resampled);
alpha_mask = new_intrusive1<AlphaMask>(resampled);
}
} }
...@@ -25,11 +25,11 @@ class ColorValueViewer : public ValueViewer { ...@@ -25,11 +25,11 @@ 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(int);
private: private:
mutable AlphaMaskP alpha_mask; /// Draws a border around the field
void loadMask(const Rotation& rot) const; void drawFieldBorder(RotatedDC& dc, const AlphaMask& alpha_mask);
/// Load the AlphaMask for this field
const AlphaMask& getMask(int w, int h) const;
}; };
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
......
...@@ -22,6 +22,7 @@ void ImageValueViewer::draw(RotatedDC& dc) { ...@@ -22,6 +22,7 @@ void ImageValueViewer::draw(RotatedDC& dc) {
// reset? // reset?
int w = max(0,(int)dc.trX(style().width)), h = max(0,(int)dc.trY(style().height)); int w = max(0,(int)dc.trX(style().width)), h = max(0,(int)dc.trY(style().height));
int a = dc.trAngle(0); //% TODO : Add getAngle()? int a = dc.trAngle(0); //% TODO : Add getAngle()?
const AlphaMask& alpha_mask = getMask(w,h);
if (bitmap.Ok() && (a != angle || size.width != w || size.height != h)) { if (bitmap.Ok() && (a != angle || size.width != w || size.height != h)) {
bitmap = Bitmap(); bitmap = Bitmap();
} }
...@@ -30,7 +31,6 @@ void ImageValueViewer::draw(RotatedDC& dc) { ...@@ -30,7 +31,6 @@ void ImageValueViewer::draw(RotatedDC& dc) {
angle = a; angle = a;
is_default = false; is_default = false;
Image image; Image image;
loadMask(dc);
// load from file // load from file
if (!value().filename.empty()) { if (!value().filename.empty()) {
try { try {
...@@ -48,7 +48,7 @@ void ImageValueViewer::draw(RotatedDC& dc) { ...@@ -48,7 +48,7 @@ void ImageValueViewer::draw(RotatedDC& dc) {
is_default = true; is_default = true;
if (what & DRAW_EDITING) { if (what & DRAW_EDITING) {
bitmap = imagePlaceholder(dc, w, h, image, what & DRAW_EDITING); bitmap = imagePlaceholder(dc, w, h, image, what & DRAW_EDITING);
if (alpha_mask || a) { if (alpha_mask.isLoaded() || a) {
image = bitmap.ConvertToImage(); // we need to convert back to an image image = bitmap.ConvertToImage(); // we need to convert back to an image
} else { } else {
image = Image(); image = Image();
...@@ -59,7 +59,7 @@ void ImageValueViewer::draw(RotatedDC& dc) { ...@@ -59,7 +59,7 @@ void ImageValueViewer::draw(RotatedDC& dc) {
if (!image.Ok() && !bitmap.Ok() && style().width > 40) { if (!image.Ok() && !bitmap.Ok() && style().width > 40) {
// placeholder bitmap // placeholder bitmap
bitmap = imagePlaceholder(dc, w, h, wxNullImage, what & DRAW_EDITING); bitmap = imagePlaceholder(dc, w, h, wxNullImage, what & DRAW_EDITING);
if (alpha_mask || a) { if (alpha_mask.isLoaded() || a) {
// we need to convert back to an image // we need to convert back to an image
image = bitmap.ConvertToImage(); image = bitmap.ConvertToImage();
} }
...@@ -67,27 +67,27 @@ void ImageValueViewer::draw(RotatedDC& dc) { ...@@ -67,27 +67,27 @@ void ImageValueViewer::draw(RotatedDC& dc) {
// done // done
if (image.Ok()) { if (image.Ok()) {
// apply mask and rotate // apply mask and rotate
if (alpha_mask) alpha_mask->setAlpha(image); alpha_mask.setAlpha(image);
size = RealSize(image); size = RealSize(image);
image = rotate_image(image, angle); image = rotate_image(image, angle);
bitmap = Bitmap(image); bitmap = Bitmap(image);
} }
} }
// border // border
drawFieldBorder(dc); drawFieldBorder(dc, alpha_mask);
// draw image, if any // draw image, if any
if (bitmap.Ok()) { if (bitmap.Ok()) {
dc.DrawPreRotatedBitmap(bitmap, dc.getInternalRect()); dc.DrawPreRotatedBitmap(bitmap, dc.getInternalRect());
} }
} }
void ImageValueViewer::drawFieldBorder(RotatedDC& dc) { void ImageValueViewer::drawFieldBorder(RotatedDC& dc, const AlphaMask& alpha_mask) {
if (!alpha_mask) { if (!alpha_mask.isLoaded()) {
ValueViewer::drawFieldBorder(dc); ValueViewer::drawFieldBorder(dc);
} else if (setFieldBorderPen(dc)) { } else if (setFieldBorderPen(dc)) {
dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.SetBrush(*wxTRANSPARENT_BRUSH);
vector<wxPoint> points; vector<wxPoint> points;
alpha_mask->convexHull(points); alpha_mask.convexHull(points);
if (points.size() < 3) return; if (points.size() < 3) return;
FOR_EACH(p, points) p = dc.trPixelNoZoom(RealPoint(p.x,p.y)); FOR_EACH(p, points) p = dc.trPixelNoZoom(RealPoint(p.x,p.y));
dc.getDC().DrawPolygon((int)points.size(), &points[0]); dc.getDC().DrawPolygon((int)points.size(), &points[0]);
...@@ -95,14 +95,8 @@ void ImageValueViewer::drawFieldBorder(RotatedDC& dc) { ...@@ -95,14 +95,8 @@ void ImageValueViewer::drawFieldBorder(RotatedDC& dc) {
} }
bool ImageValueViewer::containsPoint(const RealPoint& p) const { bool ImageValueViewer::containsPoint(const RealPoint& p) const {
if (!ValueViewer::containsPoint(p)) return false;
// check against mask // check against mask
if (!style().mask_filename().empty()) { return getMask(0,0).isOpaque(p, style().getSize());
loadMask(getRotation());
return !alpha_mask || !alpha_mask->isTransparent((int)p.x, (int)p.y);
} else {
return true;
}
} }
void ImageValueViewer::onValueChange() { void ImageValueViewer::onValueChange() {
...@@ -114,20 +108,17 @@ void ImageValueViewer::onStyleChange(int changes) { ...@@ -114,20 +108,17 @@ void ImageValueViewer::onStyleChange(int changes) {
((changes & CHANGE_DEFAULT) && is_default)) { ((changes & CHANGE_DEFAULT) && is_default)) {
bitmap = Bitmap(); bitmap = Bitmap();
} }
if (changes & CHANGE_MASK) alpha_mask = AlphaMaskP();
ValueViewer::onStyleChange(changes); ValueViewer::onStyleChange(changes);
} }
void ImageValueViewer::loadMask(const Rotation& rot) const { const AlphaMask& ImageValueViewer::getMask(int w, int h) const {
if (style().mask_filename().empty()) return; // no mask GeneratedImage::Options opts;
int w = (int) rot.trX(style().width), h = (int) rot.trY(style().height); opts.package = &viewer.getStylePackage();
if (alpha_mask && alpha_mask->hasSize(wxSize(w,h))) return; // mask loaded and right size opts.local_package = &viewer.getLocalPackage();
// (re) load the mask opts.angle = 0;
Image image; opts.width = w;
InputStreamP image_file = getStylePackage().openIn(style().mask_filename); opts.height = h;
if (image.LoadFile(*image_file)) { return style().mask.get(opts);
alpha_mask = new_intrusive1<AlphaMask>(resample(image,w,h));
}
} }
// is an image very light? // is an image very light?
......
...@@ -34,15 +34,15 @@ class ImageValueViewer : public ValueViewer { ...@@ -34,15 +34,15 @@ class ImageValueViewer : public ValueViewer {
RealSize size; ///< Size of cached bitmap RealSize size; ///< Size of cached bitmap
int angle; ///< Angle of cached bitmap int angle; ///< Angle of cached bitmap
int is_default; ///< Is the default placeholder image used? int is_default; ///< Is the default placeholder image used?
mutable AlphaMaskP alpha_mask;
void loadMask(const Rotation& rot) const;
/// Generate a placeholder image /// Generate a placeholder image
static Bitmap imagePlaceholder(const Rotation& rot, UInt w, UInt h, const Image& background, bool editing); static Bitmap imagePlaceholder(const Rotation& rot, UInt w, UInt h, const Image& background, bool editing);
/// Draws a border around the field /// Draws a border around the field
void drawFieldBorder(RotatedDC& dc); void drawFieldBorder(RotatedDC& dc, const AlphaMask& alpha_mask);
/// Load the AlphaMask for this field
const AlphaMask& getMask(int w, int h) const;
}; };
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <gui/util.hpp> #include <gui/util.hpp>
DECLARE_TYPEOF_COLLECTION(String); DECLARE_TYPEOF_COLLECTION(String);
DECLARE_TYPEOF_COLLECTION(wxPoint);
// ----------------------------------------------------------------------------- : MultipleChoiceValueViewer // ----------------------------------------------------------------------------- : MultipleChoiceValueViewer
...@@ -24,7 +25,9 @@ bool MultipleChoiceValueViewer::prepare(RotatedDC& dc) { ...@@ -24,7 +25,9 @@ bool MultipleChoiceValueViewer::prepare(RotatedDC& dc) {
} }
void MultipleChoiceValueViewer::draw(RotatedDC& dc) { void MultipleChoiceValueViewer::draw(RotatedDC& dc) {
drawFieldBorder(dc); int w = max(0,(int)dc.trX(style().width)), h = max(0,(int)dc.trY(style().height));
const AlphaMask& alpha_mask = getMask(w,h);
drawFieldBorder(dc, alpha_mask);
if (style().render_style & RENDER_HIDDEN) return; if (style().render_style & RENDER_HIDDEN) return;
RealPoint pos = align_in_rect(style().alignment, RealSize(0,0), style().getInternalRect()); RealPoint pos = align_in_rect(style().alignment, RealSize(0,0), style().getInternalRect());
// selected choices // selected choices
...@@ -83,7 +86,35 @@ void MultipleChoiceValueViewer::drawChoice(RotatedDC& dc, RealPoint& pos, const ...@@ -83,7 +86,35 @@ void MultipleChoiceValueViewer::drawChoice(RotatedDC& dc, RealPoint& pos, const
pos = move_in_direction(style().direction, pos, size, style().spacing); pos = move_in_direction(style().direction, pos, size, style().spacing);
} }
void MultipleChoiceValueViewer::drawFieldBorder(RotatedDC& dc, const AlphaMask& alpha_mask) {
if (!alpha_mask.isLoaded()) {
ValueViewer::drawFieldBorder(dc);
} else if (setFieldBorderPen(dc)) {
dc.SetBrush(*wxTRANSPARENT_BRUSH);
vector<wxPoint> points;
alpha_mask.convexHull(points);
if (points.size() < 3) return;
FOR_EACH(p, points) p = dc.trPixelNoZoom(RealPoint(p.x,p.y));
dc.getDC().DrawPolygon((int)points.size(), &points[0]);
}
}
bool MultipleChoiceValueViewer::containsPoint(const RealPoint& p) const {
// check against mask
return getMask(0,0).isOpaque(p, style().getSize());
}
void MultipleChoiceValueViewer::onStyleChange(int changes) { void MultipleChoiceValueViewer::onStyleChange(int changes) {
if (changes & CHANGE_MASK) style().image.clearCache(); if (changes & CHANGE_MASK) style().image.clearCache();
ValueViewer::onStyleChange(changes); ValueViewer::onStyleChange(changes);
} }
const AlphaMask& MultipleChoiceValueViewer::getMask(int w, int h) const {
GeneratedImage::Options opts;
opts.package = &viewer.getStylePackage();
opts.local_package = &viewer.getLocalPackage();
opts.angle = 0;
opts.width = w;
opts.height = h;
return style().mask.get(opts);
}
...@@ -23,10 +23,15 @@ class MultipleChoiceValueViewer : public ValueViewer { ...@@ -23,10 +23,15 @@ class MultipleChoiceValueViewer : 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(int); virtual void onStyleChange(int);
virtual bool containsPoint(const RealPoint& p) const;
protected: protected:
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);
/// Draws a border around the field
void drawFieldBorder(RotatedDC& dc, const AlphaMask& alpha_mask);
/// Load the AlphaMask for this field
const AlphaMask& getMask(int w, int h) const;
}; };
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
......
...@@ -94,7 +94,7 @@ template <> void GetDefaultMember::handle(const ScriptableImage& s) { ...@@ -94,7 +94,7 @@ template <> void GetDefaultMember::handle(const ScriptableImage& s) {
// ----------------------------------------------------------------------------- : CachedScriptableImage // ----------------------------------------------------------------------------- : CachedScriptableImage
void CachedScriptableImage::generateCached(const GeneratedImage::Options& options, void CachedScriptableImage::generateCached(const GeneratedImage::Options& options,
Image* mask, CachedScriptableMask* mask,
ImageCombine* combine, wxBitmap* bitmap, wxImage* image, RealSize* size) { ImageCombine* combine, wxBitmap* bitmap, wxImage* image, RealSize* size) {
// ready? // ready?
if (!isReady()) { if (!isReady()) {
...@@ -137,13 +137,24 @@ void CachedScriptableImage::generateCached(const GeneratedImage::Options& option ...@@ -137,13 +137,24 @@ void CachedScriptableImage::generateCached(const GeneratedImage::Options& option
} }
} }
} }
// hack: temporarily set angle to 0, do actual rotation after applying mask
int a = options.angle;
const_cast<GeneratedImage::Options&>(options).angle = 0;
// generate // generate
cached_i = generate(options); cached_i = generate(options);
cached_angle = options.angle; const_cast<GeneratedImage::Options&>(options).angle = cached_angle = a;
*size = cached_size = RealSize(options.width, options.height); *size = cached_size = RealSize(options.width, options.height);
if (mask && mask->Ok()) { if (mask) {
// apply mask // apply mask
set_alpha(cached_i, *mask); GeneratedImage::Options mask_opts(options);
mask_opts.width = cached_i.GetWidth();
mask_opts.height = cached_i.GetHeight();
mask_opts.angle = 0;
mask->get(mask_opts).setAlpha(cached_i);
}
if (options.angle != 0) {
// hack(pt2) do the actual rotation now
cached_i = rotate_image(cached_i, options.angle);
} }
if (*combine <= COMBINE_NORMAL) { if (*combine <= COMBINE_NORMAL) {
*bitmap = cached_b = Bitmap(cached_i); *bitmap = cached_b = Bitmap(cached_i);
...@@ -176,3 +187,45 @@ template <> void Writer::handle(const CachedScriptableImage& s) { ...@@ -176,3 +187,45 @@ template <> void Writer::handle(const CachedScriptableImage& s) {
template <> void GetDefaultMember::handle(const CachedScriptableImage& s) { template <> void GetDefaultMember::handle(const CachedScriptableImage& s) {
handle((const ScriptableImage&)s); handle((const ScriptableImage&)s);
} }
// ----------------------------------------------------------------------------- : CachedScriptableMask
bool CachedScriptableMask::update(Context& ctx) {
if (script.update(ctx)) {
mask.clear();
return true;
} else {
return false;
}
}
const AlphaMask& CachedScriptableMask::get(const GeneratedImage::Options& img_options) {
if (mask.isLoaded()) {
// already loaded?
if (img_options.width == 0 && img_options.height == 0) return mask;
if (mask.hasSize(wxSize(img_options.width,img_options.height))) return mask;
}
// load?
getNoCache(img_options,mask);
return mask;
}
void CachedScriptableMask::getNoCache(const GeneratedImage::Options& img_options, AlphaMask& other_mask) {
if (script.isBlank()) {
other_mask.clear();
} else {
Image image = script.generate(img_options);
other_mask.load(image);
}
}
template <> void Reader::handle(CachedScriptableMask& i) {
handle(i.script);
}
template <> void Writer::handle(const CachedScriptableMask& i) {
handle(i.script);
}
template <> void GetDefaultMember::handle(const CachedScriptableMask& i) {
handle(i.script);
}
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#include <script/scriptable.hpp> #include <script/scriptable.hpp>
#include <gfx/generated_image.hpp> #include <gfx/generated_image.hpp>
class CachedScriptableMask;
// ----------------------------------------------------------------------------- : ScriptableImage // ----------------------------------------------------------------------------- : ScriptableImage
/// An image that can also be scripted /// An image that can also be scripted
...@@ -49,9 +51,10 @@ class ScriptableImage { ...@@ -49,9 +51,10 @@ class ScriptableImage {
/// Can this be safely generated from another thread? /// Can this be safely generated from another thread?
inline bool threadSafe() const { return !value || value->threadSafe(); } inline bool threadSafe() const { return !value || value->threadSafe(); }
/// Is this image specific to the set (the local_package)? /// Is this image specific to the set (the local_package)?
inline bool local() const { return value && value->local(); } inline bool local() const { return value && value->local(); }
/// Is this image blank?
inline bool isBlank() const { return !value || value->isBlank(); }
/// Get access to the script, be careful /// Get access to the script, be careful
inline Script& getMutableScript() { return script.getMutableScript(); } inline Script& getMutableScript() { return script.getMutableScript(); }
...@@ -89,7 +92,7 @@ class CachedScriptableImage : public ScriptableImage { ...@@ -89,7 +92,7 @@ class CachedScriptableImage : public ScriptableImage {
* Optionally, an alpha mask is applied to the image. * Optionally, an alpha mask is applied to the image.
*/ */
void generateCached(const GeneratedImage::Options& img_options, void generateCached(const GeneratedImage::Options& img_options,
Image* mask, CachedScriptableMask* mask,
ImageCombine* combine, wxBitmap* bitmap, wxImage* image, RealSize* size); ImageCombine* combine, wxBitmap* bitmap, wxImage* image, RealSize* size);
/// Update the script, returns true if the value has changed /// Update the script, returns true if the value has changed
...@@ -105,5 +108,29 @@ class CachedScriptableImage : public ScriptableImage { ...@@ -105,5 +108,29 @@ class CachedScriptableImage : public ScriptableImage {
int cached_angle; int cached_angle;
}; };
// ----------------------------------------------------------------------------- : CachedScriptableMask
/// A version of ScriptableImage that caches an AlphaMask
class CachedScriptableMask {
public:
/// Update the script, returns true if the value has changed
bool update(Context& ctx);
/// Get the alpha mask; with the given options
/** if img_options.width == 0 and the mask is already loaded, just returns it. */
const AlphaMask& get(const GeneratedImage::Options& img_options);
/// Get a mask that is not cached
void getNoCache(const GeneratedImage::Options& img_options, AlphaMask& mask);
private:
ScriptableImage script;
AlphaMask mask;
friend class Reader;
friend class Writer;
friend class GetDefaultMember;
};
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -277,7 +277,11 @@ class ScriptString : public ScriptValue { ...@@ -277,7 +277,11 @@ class ScriptString : public ScriptValue {
return c; return c;
} }
virtual GeneratedImageP toImage(const ScriptValueP&) const { virtual GeneratedImageP toImage(const ScriptValueP&) const {
return new_intrusive1<PackagedImage>(value); if (value.empty()) {
return new_intrusive<BlankImage>();
} else {
return new_intrusive1<PackagedImage>(value);
}
} }
virtual int itemCount() const { return (int)value.size(); } virtual int itemCount() const { return (int)value.size(); }
virtual ScriptValueP getMember(const String& name) const { virtual ScriptValueP getMember(const String& name) const {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment