mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
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. git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@1182 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -214,11 +214,8 @@ void ChoiceStyle::initImage() {
|
||||
int ChoiceStyle::update(Context& ctx) {
|
||||
// Don't update the choice images, leave that to invalidate()
|
||||
int change = Style ::update(ctx)
|
||||
| font .update(ctx) * CHANGE_OTHER;
|
||||
if (mask_filename.update(ctx)) {
|
||||
change |= CHANGE_MASK;
|
||||
mask = Image();
|
||||
}
|
||||
| font .update(ctx) * CHANGE_OTHER
|
||||
| mask .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;
|
||||
@@ -255,13 +252,6 @@ void ChoiceStyle::invalidate() {
|
||||
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) {
|
||||
VALUE_N("dropdown", POPUP_DROPDOWN);
|
||||
VALUE_N("menu", POPUP_MENU);
|
||||
@@ -293,7 +283,7 @@ IMPLEMENT_REFLECTION(ChoiceStyle) {
|
||||
REFLECT_BASE(Style);
|
||||
REFLECT(popup_style);
|
||||
REFLECT(render_style);
|
||||
REFLECT_N("mask",mask_filename);
|
||||
REFLECT(mask);
|
||||
REFLECT(combine);
|
||||
REFLECT(alignment);
|
||||
REFLECT(font);
|
||||
|
||||
@@ -143,20 +143,17 @@ 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)
|
||||
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)
|
||||
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
|
||||
Alignment alignment; ///< Alignment of images
|
||||
Image mask; ///< The actual mask image
|
||||
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
|
||||
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
|
||||
void initImage();
|
||||
|
||||
|
||||
@@ -62,13 +62,13 @@ IMPLEMENT_REFLECTION(ColorStyle) {
|
||||
REFLECT(right_width);
|
||||
REFLECT(top_width);
|
||||
REFLECT(bottom_width);
|
||||
REFLECT_N("mask", mask_filename);
|
||||
REFLECT(mask);
|
||||
REFLECT(combine);
|
||||
}
|
||||
|
||||
int ColorStyle::update(Context& ctx) {
|
||||
return Style ::update(ctx)
|
||||
| mask_filename.update(ctx) * CHANGE_MASK;
|
||||
return Style::update(ctx)
|
||||
| mask.update(ctx) * CHANGE_MASK;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : ColorValue
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <util/defaultable.hpp>
|
||||
#include <data/field.hpp>
|
||||
#include <script/scriptable.hpp>
|
||||
#include <script/image.hpp>
|
||||
|
||||
// ----------------------------------------------------------------------------- : ColorField
|
||||
|
||||
@@ -56,13 +57,13 @@ class ColorStyle : public Style {
|
||||
ColorStyle(const ColorFieldP& field);
|
||||
DECLARE_STYLE_TYPE(Color);
|
||||
|
||||
double radius; ///< Radius of round corners
|
||||
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 top_width; ///< Width of the colored region on the top 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
|
||||
ImageCombine combine; ///< How to combine image with the background
|
||||
double radius; ///< Radius of round corners
|
||||
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 top_width; ///< Width of the colored region on the top side
|
||||
double bottom_width; ///< Width of the colored region on the bottom side
|
||||
CachedScriptableMask mask; ///< Mask image
|
||||
ImageCombine combine; ///< How to combine image with the background
|
||||
|
||||
virtual int update(Context&);
|
||||
};
|
||||
|
||||
@@ -23,13 +23,13 @@ IMPLEMENT_REFLECTION(ImageField) {
|
||||
|
||||
IMPLEMENT_REFLECTION(ImageStyle) {
|
||||
REFLECT_BASE(Style);
|
||||
REFLECT_N("mask", mask_filename);
|
||||
REFLECT(mask);
|
||||
REFLECT_N("default", default_image);
|
||||
}
|
||||
|
||||
int ImageStyle::update(Context& ctx) {
|
||||
return Style ::update(ctx)
|
||||
| mask_filename.update(ctx) * CHANGE_MASK
|
||||
| mask .update(ctx) * CHANGE_MASK
|
||||
| default_image.update(ctx) * CHANGE_DEFAULT;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,8 @@ class ImageStyle : public Style {
|
||||
inline ImageStyle(const ImageFieldP& field) : Style(field) {}
|
||||
DECLARE_STYLE_TYPE(Image);
|
||||
|
||||
Scriptable<String> mask_filename; ///< Filename for a mask image
|
||||
ScriptableImage default_image; ///< Placeholder
|
||||
CachedScriptableMask mask; ///< Mask image
|
||||
ScriptableImage default_image; ///< Placeholder
|
||||
|
||||
virtual int update(Context&);
|
||||
};
|
||||
|
||||
@@ -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
|
||||
///< 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
|
||||
PreserveAspect preserve_aspect;
|
||||
bool saturate;
|
||||
@@ -55,9 +55,10 @@ class GeneratedImage : public ScriptValue {
|
||||
|
||||
/// Can this image be generated safely from another thread?
|
||||
virtual bool threadSafe() const { return true; }
|
||||
|
||||
/// Is this image specific to the set (the local_package)?
|
||||
virtual bool local() const { return false; }
|
||||
/// Is this image blank?
|
||||
virtual bool isBlank() const { return false; }
|
||||
|
||||
virtual ScriptType type() const;
|
||||
virtual String typeName() const;
|
||||
@@ -88,6 +89,7 @@ class BlankImage : public GeneratedImage {
|
||||
public:
|
||||
virtual Image generate(const Options&) const;
|
||||
virtual bool operator == (const GeneratedImage& that) const;
|
||||
virtual bool isBlank() const { return true; }
|
||||
|
||||
// Why is this not thread safe? What is GTK smoking?
|
||||
#ifdef __WXGTK__
|
||||
|
||||
+5
-4
@@ -167,8 +167,9 @@ class AlphaMask : public IntrusivePtrBase<AlphaMask> {
|
||||
/// Apply the alpha mask to a bitmap
|
||||
void setAlpha(Bitmap& b) const;
|
||||
|
||||
/// Is the given location fully transparent?
|
||||
bool isTransparent(int x, int y) const;
|
||||
/// Is the given location opaque (not fully transparent)? when the mask were stretched to size
|
||||
bool isOpaque(const RealPoint& p, const RealSize& size) const;
|
||||
bool isOpaque(int x, int y) const;
|
||||
|
||||
/// Determine a convex hull polygon *around* the mask
|
||||
void convexHull(vector<wxPoint>& points) const;
|
||||
@@ -178,9 +179,9 @@ class AlphaMask : public IntrusivePtrBase<AlphaMask> {
|
||||
|
||||
/// Returns the start of a row, when the mask were stretched to size
|
||||
/** 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
|
||||
double rowRight(double y, RealSize size) const;
|
||||
double rowRight(double y, const RealSize& size) const;
|
||||
|
||||
/// Does this mask have the given size?
|
||||
inline bool hasSize(const wxSize& compare_size) const { return size == compare_size; }
|
||||
|
||||
+25
-18
@@ -17,9 +17,13 @@ AlphaMask::AlphaMask(const Image& img) : alpha(nullptr), lefts(nullptr), rights(
|
||||
load(img);
|
||||
}
|
||||
AlphaMask::~AlphaMask() {
|
||||
delete[] alpha;
|
||||
delete[] lefts;
|
||||
delete[] rights;
|
||||
clear();
|
||||
}
|
||||
|
||||
void AlphaMask::clear() {
|
||||
delete[] alpha; alpha = nullptr;
|
||||
delete[] lefts; lefts = nullptr;
|
||||
delete[] rights; rights = nullptr;
|
||||
}
|
||||
|
||||
void AlphaMask::load(const Image& img) {
|
||||
@@ -54,10 +58,14 @@ void AlphaMask::setAlpha(Bitmap& bmp) const {
|
||||
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 (!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?
|
||||
@@ -72,6 +80,10 @@ void make_convex(vector<wxPoint>& points) {
|
||||
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 {
|
||||
if (!alpha) throw InternalError(_("AlphaMask::convexHull"));
|
||||
@@ -84,36 +96,31 @@ void AlphaMask::convexHull(vector<wxPoint>& points) const {
|
||||
miny = min(miny,y);
|
||||
maxy = y;
|
||||
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));
|
||||
make_convex(points);
|
||||
add_convex_point(points, x-1, y);
|
||||
lastx = x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maxy == -1) return; // No image
|
||||
points.push_back(wxPoint(lastx-1,maxy+1));
|
||||
make_convex(points);
|
||||
add_convex_point(points, lastx-1, maxy+1);
|
||||
// Right side, bottom to top
|
||||
for (int y = maxy ; y >= miny ; --y) {
|
||||
for (int x = size.x - 1 ; x >= 0 ; --x) {
|
||||
if (alpha[x + y * size.x] >= 20) {
|
||||
// opaque pixel
|
||||
if (y == maxy) {
|
||||
points.push_back(wxPoint(x+1,y+1));
|
||||
make_convex(points);
|
||||
add_convex_point(points, x+1, y+1);
|
||||
}
|
||||
points.push_back(wxPoint(x+1,y));
|
||||
make_convex(points);
|
||||
add_convex_point(points, x+1, y);
|
||||
lastx = x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
points.push_back(wxPoint(lastx+1,miny-1));
|
||||
make_convex(points);
|
||||
add_convex_point(points, lastx+1, miny-1);
|
||||
}
|
||||
|
||||
Image AlphaMask::colorImage(const Color& color) 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();
|
||||
if (!lefts || y < 0 || y >= resize.height) {
|
||||
// no mask, or outside it
|
||||
@@ -151,7 +158,7 @@ double AlphaMask::rowLeft (double y, RealSize resize) const {
|
||||
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();
|
||||
if (!rights || y < 0 || y >= resize.height) {
|
||||
// no mask, or outside it
|
||||
|
||||
@@ -54,7 +54,7 @@ void ImageSlice::constrain(PreferedProperty prefer) {
|
||||
Image ImageSlice::getSlice() const {
|
||||
if (selection.width == target_size.GetWidth() && selection.height == target_size.GetHeight() && selection.x == 0 && selection.y == 0) {
|
||||
// exactly the right size
|
||||
return source;
|
||||
return source.GetSubImage(selection);
|
||||
}
|
||||
Image target(target_size.GetWidth(), target_size.GetHeight(), false);
|
||||
if (sharpen && sharpen_amount > 0 && sharpen_amount <= 100) {
|
||||
@@ -75,7 +75,7 @@ DEFINE_EVENT_TYPE(EVENT_SLICE_CHANGED);
|
||||
|
||||
// ----------------------------------------------------------------------------- : 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)
|
||||
, slice(source, target_size)
|
||||
{
|
||||
@@ -277,6 +277,7 @@ void ImageSliceWindow::updateControls() {
|
||||
size->SetSelection(2); // force to fit
|
||||
} else if (slice.selection.width <= slice.source.GetWidth() &&
|
||||
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.y == 0 && slice.selection.height == slice.source.GetHeight()))) {
|
||||
size->SetSelection(1); // size to fit
|
||||
@@ -334,7 +335,7 @@ END_EVENT_TABLE ()
|
||||
|
||||
// ----------------------------------------------------------------------------- : 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)
|
||||
, slice(slice)
|
||||
, mask(mask)
|
||||
@@ -359,9 +360,8 @@ void ImageSlicePreview::onPaint(wxPaintEvent&) {
|
||||
void ImageSlicePreview::draw(DC& dc) {
|
||||
if (!bitmap.Ok()) {
|
||||
Image image = slice.getSlice();
|
||||
if (mask && mask->hasSize(slice.target_size)) {
|
||||
mask->setAlpha(image);
|
||||
}
|
||||
assert(image.GetWidth() == slice.target_size.GetWidth() && image.GetHeight() == slice.target_size.GetHeight());
|
||||
mask.setAlpha(image);
|
||||
if (image.HasAlpha()) {
|
||||
// create bitmap
|
||||
bitmap = Bitmap(image.GetWidth(), image.GetHeight());
|
||||
|
||||
@@ -59,7 +59,7 @@ class ImageSlice {
|
||||
/// Dialog for selecting a slice of an image
|
||||
class ImageSliceWindow : public wxDialog {
|
||||
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
|
||||
Image getImage() const;
|
||||
@@ -115,7 +115,7 @@ class ImageSliceWindow : public wxDialog {
|
||||
/// A preview of the sliced image
|
||||
class ImageSlicePreview : public wxControl {
|
||||
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
|
||||
void update();
|
||||
@@ -124,7 +124,7 @@ class ImageSlicePreview : public wxControl {
|
||||
private:
|
||||
Bitmap bitmap;
|
||||
ImageSlice& slice;
|
||||
AlphaMaskP mask;
|
||||
const AlphaMask& mask;
|
||||
|
||||
bool mouse_down;
|
||||
int mouseX, mouseY; ///< starting mouse position
|
||||
|
||||
+4
-11
@@ -29,17 +29,10 @@ bool ImageValueEditor::onLeftDClick(const RealPoint&, wxMouseEvent&) {
|
||||
|
||||
void ImageValueEditor::sliceImage(const Image& image) {
|
||||
if (!image.Ok()) return;
|
||||
// mask?
|
||||
AlphaMaskP mask;
|
||||
if (!style().mask_filename().empty()) {
|
||||
Image mask_image;
|
||||
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);
|
||||
}
|
||||
}
|
||||
// mask
|
||||
GeneratedImage::Options options((int)style().width, (int)style().height, &viewer.getStylePackage(), &viewer.getLocalPackage());
|
||||
AlphaMask mask;
|
||||
style().mask.getNoCache(options,mask);
|
||||
// slice
|
||||
ImageSliceWindow s(wxGetTopLevelParent(&editor()), image, style().getSize(), mask);
|
||||
// clicked ok?
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <render/value/choice.hpp>
|
||||
#include <render/card/viewer.hpp>
|
||||
|
||||
DECLARE_TYPEOF_COLLECTION(wxPoint);
|
||||
|
||||
// ----------------------------------------------------------------------------- : ChoiceValueViewer
|
||||
|
||||
IMPLEMENT_VALUE_VIEWER(Choice);
|
||||
@@ -20,11 +22,48 @@ bool ChoiceValueViewer::prepare(RotatedDC& dc) {
|
||||
return prepare_choice_viewer(dc, *this, style(), value().value());
|
||||
}
|
||||
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;
|
||||
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) {
|
||||
if (style.render_style & RENDER_IMAGE) {
|
||||
style.initImage();
|
||||
@@ -38,7 +77,6 @@ bool prepare_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& styl
|
||||
// Generate image/bitmap (whichever is available)
|
||||
// don't worry, we cache the image
|
||||
ImageCombine combine = style.combine;
|
||||
style.loadMask(viewer.getStylePackage());
|
||||
Bitmap bitmap; Image image;
|
||||
RealSize 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,
|
||||
get_options(dc, viewer, style, img_options);
|
||||
// Generate image/bitmap
|
||||
ImageCombine combine = style.combine;
|
||||
style.loadMask(viewer.getStylePackage());
|
||||
Bitmap bitmap; Image image;
|
||||
RealSize 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
|
||||
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 {
|
||||
virtual bool prepare(RotatedDC& dc);
|
||||
virtual void draw(RotatedDC& dc);
|
||||
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);
|
||||
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
|
||||
#endif
|
||||
|
||||
+36
-32
@@ -11,6 +11,7 @@
|
||||
#include <render/card/viewer.hpp>
|
||||
|
||||
DECLARE_TYPEOF_COLLECTION(ColorField::ChoiceP);
|
||||
DECLARE_TYPEOF_COLLECTION(wxPoint);
|
||||
|
||||
// ----------------------------------------------------------------------------- : ColorValueViewer
|
||||
|
||||
@@ -43,9 +44,10 @@ void ColorValueViewer::draw(RotatedDC& dc) {
|
||||
dc.DrawText(color_name, RealPoint(43, 3));
|
||||
} else {
|
||||
// is there a mask?
|
||||
loadMask(dc);
|
||||
if (alpha_mask) {
|
||||
dc.DrawImage(alpha_mask->colorImage(value().value()), RealPoint(0,0), style().combine);
|
||||
int w = max(0,(int)dc.trX(style().width)), h = max(0,(int)dc.trY(style().height));
|
||||
const AlphaMask& alpha_mask = getMask(w,h);
|
||||
if (alpha_mask.isLoaded()) {
|
||||
dc.DrawImage(alpha_mask.colorImage(value().value()), RealPoint(0,0), style().combine);
|
||||
} else {
|
||||
// do we need clipping?
|
||||
bool clip = style().left_width < style().width && style().right_width < style().width &&
|
||||
@@ -64,43 +66,45 @@ void ColorValueViewer::draw(RotatedDC& dc) {
|
||||
dc.DrawRoundedRectangle(style().getInternalRect(), style().radius);
|
||||
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 {
|
||||
// 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
|
||||
if (!style().mask_filename().empty()) loadMask(getRotation());
|
||||
if (alpha_mask) {
|
||||
return !alpha_mask->isTransparent((int)left, (int)top);
|
||||
const AlphaMask& alpha_mask = getMask(0,0);
|
||||
if (alpha_mask.isLoaded()) {
|
||||
// check against mask
|
||||
return alpha_mask.isOpaque(p, style().getSize());
|
||||
} 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
|
||||
if (left >= style().left_width && right >= style().right_width && // outside horizontal border
|
||||
top >= style().top_width && bottom >= style().bottom_width) { // outside vertical border
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return left < style().left_width || right < style().right_width // inside horizontal border
|
||||
|| top < style().top_width || bottom < style().bottom_width; // inside vertical border
|
||||
}
|
||||
}
|
||||
|
||||
void ColorValueViewer::onStyleChange(int changes) {
|
||||
if (changes & CHANGE_MASK) alpha_mask = AlphaMaskP();
|
||||
ValueViewer::onStyleChange(changes);
|
||||
}
|
||||
|
||||
void ColorValueViewer::loadMask(const Rotation& rot) const {
|
||||
if (style().mask_filename().empty()) return; // no mask
|
||||
int w = (int) rot.trX(rot.getWidth()), h = (int) rot.trY(rot.getHeight());
|
||||
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);
|
||||
}
|
||||
const AlphaMask& ColorValueViewer::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);
|
||||
}
|
||||
|
||||
@@ -25,11 +25,11 @@ class ColorValueViewer : public ValueViewer {
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual bool containsPoint(const RealPoint& p) const;
|
||||
|
||||
virtual void onStyleChange(int);
|
||||
|
||||
private:
|
||||
mutable AlphaMaskP alpha_mask;
|
||||
void loadMask(const Rotation& rot) const;
|
||||
/// 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
|
||||
|
||||
+17
-26
@@ -22,6 +22,7 @@ void ImageValueViewer::draw(RotatedDC& dc) {
|
||||
// reset?
|
||||
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()?
|
||||
const AlphaMask& alpha_mask = getMask(w,h);
|
||||
if (bitmap.Ok() && (a != angle || size.width != w || size.height != h)) {
|
||||
bitmap = Bitmap();
|
||||
}
|
||||
@@ -30,7 +31,6 @@ void ImageValueViewer::draw(RotatedDC& dc) {
|
||||
angle = a;
|
||||
is_default = false;
|
||||
Image image;
|
||||
loadMask(dc);
|
||||
// load from file
|
||||
if (!value().filename.empty()) {
|
||||
try {
|
||||
@@ -48,7 +48,7 @@ void ImageValueViewer::draw(RotatedDC& dc) {
|
||||
is_default = true;
|
||||
if (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
|
||||
} else {
|
||||
image = Image();
|
||||
@@ -59,7 +59,7 @@ void ImageValueViewer::draw(RotatedDC& dc) {
|
||||
if (!image.Ok() && !bitmap.Ok() && style().width > 40) {
|
||||
// placeholder bitmap
|
||||
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
|
||||
image = bitmap.ConvertToImage();
|
||||
}
|
||||
@@ -67,27 +67,27 @@ void ImageValueViewer::draw(RotatedDC& dc) {
|
||||
// done
|
||||
if (image.Ok()) {
|
||||
// apply mask and rotate
|
||||
if (alpha_mask) alpha_mask->setAlpha(image);
|
||||
alpha_mask.setAlpha(image);
|
||||
size = RealSize(image);
|
||||
image = rotate_image(image, angle);
|
||||
bitmap = Bitmap(image);
|
||||
}
|
||||
}
|
||||
// border
|
||||
drawFieldBorder(dc);
|
||||
drawFieldBorder(dc, alpha_mask);
|
||||
// draw image, if any
|
||||
if (bitmap.Ok()) {
|
||||
dc.DrawPreRotatedBitmap(bitmap, dc.getInternalRect());
|
||||
}
|
||||
}
|
||||
|
||||
void ImageValueViewer::drawFieldBorder(RotatedDC& dc) {
|
||||
if (!alpha_mask) {
|
||||
void ImageValueViewer::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);
|
||||
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]);
|
||||
@@ -95,14 +95,8 @@ void ImageValueViewer::drawFieldBorder(RotatedDC& dc) {
|
||||
}
|
||||
|
||||
bool ImageValueViewer::containsPoint(const RealPoint& p) const {
|
||||
if (!ValueViewer::containsPoint(p)) return false;
|
||||
// check against mask
|
||||
if (!style().mask_filename().empty()) {
|
||||
loadMask(getRotation());
|
||||
return !alpha_mask || !alpha_mask->isTransparent((int)p.x, (int)p.y);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return getMask(0,0).isOpaque(p, style().getSize());
|
||||
}
|
||||
|
||||
void ImageValueViewer::onValueChange() {
|
||||
@@ -114,20 +108,17 @@ void ImageValueViewer::onStyleChange(int changes) {
|
||||
((changes & CHANGE_DEFAULT) && is_default)) {
|
||||
bitmap = Bitmap();
|
||||
}
|
||||
if (changes & CHANGE_MASK) alpha_mask = AlphaMaskP();
|
||||
ValueViewer::onStyleChange(changes);
|
||||
}
|
||||
|
||||
void ImageValueViewer::loadMask(const Rotation& rot) const {
|
||||
if (style().mask_filename().empty()) return; // no mask
|
||||
int w = (int) rot.trX(style().width), h = (int) rot.trY(style().height);
|
||||
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)) {
|
||||
alpha_mask = new_intrusive1<AlphaMask>(resample(image,w,h));
|
||||
}
|
||||
const AlphaMask& ImageValueViewer::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);
|
||||
}
|
||||
|
||||
// is an image very light?
|
||||
|
||||
@@ -34,15 +34,15 @@ class ImageValueViewer : public ValueViewer {
|
||||
RealSize size; ///< Size of 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;
|
||||
|
||||
/// Generate a placeholder image
|
||||
static Bitmap imagePlaceholder(const Rotation& rot, UInt w, UInt h, const Image& background, bool editing);
|
||||
|
||||
/// 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
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <gui/util.hpp>
|
||||
|
||||
DECLARE_TYPEOF_COLLECTION(String);
|
||||
DECLARE_TYPEOF_COLLECTION(wxPoint);
|
||||
|
||||
// ----------------------------------------------------------------------------- : MultipleChoiceValueViewer
|
||||
|
||||
@@ -24,7 +25,9 @@ bool MultipleChoiceValueViewer::prepare(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;
|
||||
RealPoint pos = align_in_rect(style().alignment, RealSize(0,0), style().getInternalRect());
|
||||
// selected choices
|
||||
@@ -83,7 +86,35 @@ void MultipleChoiceValueViewer::drawChoice(RotatedDC& dc, RealPoint& pos, const
|
||||
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) {
|
||||
if (changes & CHANGE_MASK) style().image.clearCache();
|
||||
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 {
|
||||
virtual bool prepare(RotatedDC& dc);
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual void onStyleChange(int);
|
||||
virtual bool containsPoint(const RealPoint& p) const;
|
||||
protected:
|
||||
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);
|
||||
/// 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
|
||||
|
||||
+57
-4
@@ -94,7 +94,7 @@ template <> void GetDefaultMember::handle(const ScriptableImage& s) {
|
||||
// ----------------------------------------------------------------------------- : CachedScriptableImage
|
||||
|
||||
void CachedScriptableImage::generateCached(const GeneratedImage::Options& options,
|
||||
Image* mask,
|
||||
CachedScriptableMask* mask,
|
||||
ImageCombine* combine, wxBitmap* bitmap, wxImage* image, RealSize* size) {
|
||||
// ready?
|
||||
if (!isReady()) {
|
||||
@@ -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
|
||||
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);
|
||||
if (mask && mask->Ok()) {
|
||||
if (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) {
|
||||
*bitmap = cached_b = Bitmap(cached_i);
|
||||
@@ -176,3 +187,45 @@ template <> void Writer::handle(const CachedScriptableImage& s) {
|
||||
template <> void GetDefaultMember::handle(const CachedScriptableImage& 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);
|
||||
}
|
||||
|
||||
+29
-2
@@ -15,6 +15,8 @@
|
||||
#include <script/scriptable.hpp>
|
||||
#include <gfx/generated_image.hpp>
|
||||
|
||||
class CachedScriptableMask;
|
||||
|
||||
// ----------------------------------------------------------------------------- : ScriptableImage
|
||||
|
||||
/// An image that can also be scripted
|
||||
@@ -49,9 +51,10 @@ class ScriptableImage {
|
||||
|
||||
/// Can this be safely generated from another thread?
|
||||
inline bool threadSafe() const { return !value || value->threadSafe(); }
|
||||
|
||||
/// Is this image specific to the set (the local_package)?
|
||||
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
|
||||
inline Script& getMutableScript() { return script.getMutableScript(); }
|
||||
@@ -89,7 +92,7 @@ class CachedScriptableImage : public ScriptableImage {
|
||||
* Optionally, an alpha mask is applied to the image.
|
||||
*/
|
||||
void generateCached(const GeneratedImage::Options& img_options,
|
||||
Image* mask,
|
||||
CachedScriptableMask* mask,
|
||||
ImageCombine* combine, wxBitmap* bitmap, wxImage* image, RealSize* size);
|
||||
|
||||
/// Update the script, returns true if the value has changed
|
||||
@@ -105,5 +108,29 @@ class CachedScriptableImage : public ScriptableImage {
|
||||
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
|
||||
#endif
|
||||
|
||||
@@ -277,7 +277,11 @@ class ScriptString : public ScriptValue {
|
||||
return c;
|
||||
}
|
||||
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 ScriptValueP getMember(const String& name) const {
|
||||
|
||||
Reference in New Issue
Block a user