mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-13 05: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) {
|
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() {
|
|||||||
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) {
|
|||||||
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 {
|
|||||||
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) {
|
|||||||
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 @@
|
|||||||
#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 {
|
|||||||
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) {
|
|||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
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 {
|
|||||||
|
|
||||||
/// 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 {
|
|||||||
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__
|
||||||
|
|||||||
+5
-4
@@ -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> {
|
|||||||
|
|
||||||
/// 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; }
|
||||||
|
|||||||
+25
-18
@@ -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 {
|
|||||||
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) {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
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) {
|
|||||||
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);
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------- : 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() {
|
|||||||
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 ()
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------- : 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&) {
|
|||||||
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 {
|
|||||||
/// 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 {
|
|||||||
/// 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 {
|
|||||||
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
|
||||||
|
|||||||
+4
-11
@@ -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 @@
|
|||||||
#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) {
|
|||||||
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
|
|||||||
// 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,
|
|||||||
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
|
|||||||
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 {
|
|||||||
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
|
||||||
|
|||||||
+36
-32
@@ -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) {
|
|||||||
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) {
|
|||||||
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 {
|
|||||||
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
|
||||||
|
|||||||
+17
-26
@@ -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) {
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
// 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) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
|||||||
((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 {
|
|||||||
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 @@
|
|||||||
#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) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
|||||||
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 {
|
|||||||
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
|
||||||
|
|||||||
+57
-4
@@ -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
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 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) {
|
|||||||
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);
|
||||||
|
}
|
||||||
|
|||||||
+29
-2
@@ -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 {
|
|||||||
|
|
||||||
/// 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 {
|
|||||||
* 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 {
|
|||||||
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 {
|
|||||||
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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user