From acb3493b597b783704c9790c1a3456bb01786061 Mon Sep 17 00:00:00 2001 From: twanvl Date: Sat, 30 Aug 2008 17:15:22 +0000 Subject: [PATCH] Merged behaviour from ContourMask into AlphaMask. git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@1181 0fc631ac-6414-0410-93d0-97cfa31319b6 --- src/data/field/choice.cpp | 7 ++- src/data/field/text.hpp | 2 +- src/gfx/blend_image.cpp | 6 +- src/gfx/gfx.hpp | 43 +++++++------- src/gfx/mask_image.cpp | 102 ++++++++++++++++++--------------- src/gui/image_slice_window.cpp | 2 +- src/render/value/color.cpp | 7 +-- src/render/value/image.cpp | 2 +- src/render/value/text.cpp | 2 +- src/script/image.cpp | 8 +-- 10 files changed, 89 insertions(+), 92 deletions(-) diff --git a/src/data/field/choice.cpp b/src/data/field/choice.cpp index bc211e1d..9ed4f313 100644 --- a/src/data/field/choice.cpp +++ b/src/data/field/choice.cpp @@ -214,8 +214,11 @@ 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 - | mask_filename.update(ctx) * CHANGE_MASK; + | font .update(ctx) * CHANGE_OTHER; + if (mask_filename.update(ctx)) { + change |= CHANGE_MASK; + mask = Image(); + } 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; diff --git a/src/data/field/text.hpp b/src/data/field/text.hpp index 8f5481e6..001add04 100644 --- a/src/data/field/text.hpp +++ b/src/data/field/text.hpp @@ -68,7 +68,7 @@ class TextStyle : public Style { double line_height_line_max; ///< Maximum line height double paragraph_height; ///< Fixed height of paragraphs String mask_filename; ///< Filename of the mask - ContourMask mask; ///< Mask to fit the text to (may be null) + AlphaMask mask; ///< Mask to fit the text to (may be null) Direction direction; ///< In what direction is text layed out? // information from text rendering double content_width, content_height; ///< Size of the rendered text diff --git a/src/gfx/blend_image.cpp b/src/gfx/blend_image.cpp index 9de5b83e..a03007ec 100644 --- a/src/gfx/blend_image.cpp +++ b/src/gfx/blend_image.cpp @@ -77,11 +77,9 @@ void mask_blend(Image& img1, const Image& img2, const Image& mask) { // ----------------------------------------------------------------------------- : Alpha void set_alpha(Image& img, const Image& img_alpha) { - if (img.GetWidth() != img_alpha.GetWidth() || img.GetHeight() != img_alpha.GetHeight()) { - throw Error(_("Image must have same size as mask")); - } + Image img_alpha_resampled = resample(img_alpha, img.GetWidth(), img.GetHeight()); if (!img.HasAlpha()) img.InitAlpha(); - Byte *im = img.GetAlpha(), *al = img_alpha.GetData(); + Byte *im = img.GetAlpha(), *al = img_alpha_resampled.GetData(); size_t size = img.GetWidth() * img.GetHeight(); for (size_t i = 0 ; i < size ; ++i) { im[i] = (im[i] * al[i*3]) / 255; diff --git a/src/gfx/gfx.hpp b/src/gfx/gfx.hpp index 6deeb17c..acd7d95b 100644 --- a/src/gfx/gfx.hpp +++ b/src/gfx/gfx.hpp @@ -153,9 +153,15 @@ void set_alpha(Image& img, double alpha); */ class AlphaMask : public IntrusivePtrBase { public: + AlphaMask(); AlphaMask(const Image& mask); ~AlphaMask(); + /// Load an alpha mask + void load(const Image& image); + /// Unload the mask + void clear(); + /// Apply the alpha mask to an image void setAlpha(Image& i) const; /// Apply the alpha mask to a bitmap @@ -167,36 +173,27 @@ class AlphaMask : public IntrusivePtrBase { /// Determine a convex hull polygon *around* the mask void convexHull(vector& points) const; - /// Size of the mask - wxSize size; - private: - Byte* alpha; -}; - -/// A contour mask stores the size and position of each line in the image -/** It is created by treating black in the source image as transparent and white (red) as opaque - * The left is the first non-transparent pixel, the right is the last non-transparent pixel - */ -class ContourMask { - public: - ContourMask(); - ~ContourMask(); - - /// Load a contour mask - void load(const Image& image); - /// Unload the mask - void unload(); - /// Is a mask loaded? - inline bool ok() const { return width > 0 && height > 0; } + /// Make an image of the given color using this mask + Image colorImage(const Color& color) const; /// 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; /// Returns the end of a row, when the mask were stretched to size double rowRight(double y, RealSize size) const; + /// Does this mask have the given size? + inline bool hasSize(const wxSize& compare_size) const { return size == compare_size; } + /// Is the mask loaded? + inline bool isLoaded() const { return alpha; } + private: - int width, height; - int *lefts, *rights; + wxSize size; ///< Size of the mask + Byte* alpha; ///< Data of alpha mask + mutable int *lefts, *rights; ///< Row sizes + + /// Compute lefts and rights from alpha + void loadRowSizes() const; }; // ----------------------------------------------------------------------------- : EOF diff --git a/src/gfx/mask_image.cpp b/src/gfx/mask_image.cpp index 52d34fe5..38710c95 100644 --- a/src/gfx/mask_image.cpp +++ b/src/gfx/mask_image.cpp @@ -12,29 +12,43 @@ // ----------------------------------------------------------------------------- : AlphaMask -AlphaMask::AlphaMask(const Image& img) - : size(img.GetWidth(), img.GetHeight()) -{ +AlphaMask::AlphaMask() : alpha(nullptr), lefts(nullptr), rights(nullptr) {} +AlphaMask::AlphaMask(const Image& img) : alpha(nullptr), lefts(nullptr), rights(nullptr) { + load(img); +} +AlphaMask::~AlphaMask() { + delete[] alpha; + delete[] lefts; + delete[] rights; +} + +void AlphaMask::load(const Image& img) { + size_t old_n = alpha ? size.x * size.y : 0; + size.x = img.GetWidth(); + size.y = img.GetHeight(); + // Memory + size_t n = size.x * size.y; + if (n != old_n) { + delete[] alpha; + alpha = new Byte[n]; + } + delete[] lefts; lefts = nullptr; + delete[] rights; rights = nullptr; // Copy red chanel to alpha - size_t n = size.GetWidth() * size.GetHeight(); - alpha = new Byte[n]; Byte* from = img.GetData(), *to = alpha; for (size_t i = 0 ; i < n ; ++i) { - *to = *from; - from += 3; - to += 1; + to[i] = from[3*i]; } } -AlphaMask::~AlphaMask() { - delete[] alpha; -} void AlphaMask::setAlpha(Image& img) const { + if (!alpha) return; set_alpha(img, alpha, size); } void AlphaMask::setAlpha(Bitmap& bmp) const { + if (!alpha) return; Image img = bmp.ConvertToImage(); setAlpha(img); bmp = Bitmap(img); @@ -42,6 +56,7 @@ void AlphaMask::setAlpha(Bitmap& bmp) const { bool AlphaMask::isTransparent(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; } @@ -59,7 +74,8 @@ void make_convex(vector& points) { } void AlphaMask::convexHull(vector& points) const { - // Left side + if (!alpha) throw InternalError(_("AlphaMask::convexHull")); + // Left side, top to bottom int miny = size.y, maxy = -1, lastx = 0; for (int y = 0 ; y < size.y ; ++y) { for (int x = 0 ; x < size.x ; ++x) { @@ -80,7 +96,7 @@ void AlphaMask::convexHull(vector& points) const { if (maxy == -1) return; // No image points.push_back(wxPoint(lastx-1,maxy+1)); make_convex(points); - // Right side + // 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) { @@ -100,54 +116,46 @@ void AlphaMask::convexHull(vector& points) const { make_convex(points); } -// ----------------------------------------------------------------------------- : ContourMask - -ContourMask::ContourMask() - : width(0), height(0), lefts(nullptr), rights(nullptr) -{} -ContourMask::~ContourMask() { - unload(); +Image AlphaMask::colorImage(const Color& color) const { + Image image(size.x, size.y); + fill_image(image, color); + setAlpha(image); + return image; } -void ContourMask::load(const Image& image) { - unload(); - width = image.GetWidth(); - height = image.GetHeight(); - lefts = new int[height]; - rights = new int[height]; +// ----------------------------------------------------------------------------- : Contour Mask + +void AlphaMask::loadRowSizes() const { + if (lefts || !alpha) return; + lefts = new int[size.y]; + rights = new int[size.y]; // for each row: determine left and rightmost white pixel - Byte* data = image.GetData(); - for (int y = 0 ; y < height ; ++y) { - lefts[y] = width; rights[y] = width; - for (int x = 0 ; x < width ; ++x) { - int v = data[0] + data[1] + data[2]; - if (v > 50) { // white enough + for (int y = 0 ; y < size.y ; ++y) { + lefts[y] = size.x; + rights[y] = 0; + for (int x = 0 ; x < size.x ; ++x) { + if (alpha[y * size.x + x] > 64) { // white enough rights[y] = x; if (x < lefts[y]) lefts[y] = x; } - data += 3; } } } -void ContourMask::unload() { - delete lefts; - delete rights; - lefts = rights = nullptr; - width = height = 0; -} - -double ContourMask::rowLeft (double y, RealSize size) const { - if (!ok() || y < 0 || y >= size.height) { +double AlphaMask::rowLeft (double y, RealSize resize) const { + loadRowSizes(); + if (!lefts || y < 0 || y >= resize.height) { // no mask, or outside it return 0; } - return lefts[(int)(y * size.height / height)] * size.width / width; + return lefts[(int)(y * resize.height / size.y)] * resize.width / size.x; } -double ContourMask::rowRight(double y, RealSize size) const { - if (!ok() || y < 0 || y >= size.height) { + +double AlphaMask::rowRight(double y, RealSize resize) const { + loadRowSizes(); + if (!rights || y < 0 || y >= resize.height) { // no mask, or outside it - return size.width; + return resize.width; } - return rights[(int)(y * size.height / height)] * size.width / width; + return rights[(int)(y * resize.height / size.y)] * resize.width / size.x; } diff --git a/src/gui/image_slice_window.cpp b/src/gui/image_slice_window.cpp index c4cd6db3..94bdd1dd 100644 --- a/src/gui/image_slice_window.cpp +++ b/src/gui/image_slice_window.cpp @@ -359,7 +359,7 @@ void ImageSlicePreview::onPaint(wxPaintEvent&) { void ImageSlicePreview::draw(DC& dc) { if (!bitmap.Ok()) { Image image = slice.getSlice(); - if (mask && mask->size == slice.target_size) { + if (mask && mask->hasSize(slice.target_size)) { mask->setAlpha(image); } if (image.HasAlpha()) { diff --git a/src/render/value/color.cpp b/src/render/value/color.cpp index ee3c6c0f..531cff8e 100644 --- a/src/render/value/color.cpp +++ b/src/render/value/color.cpp @@ -45,10 +45,7 @@ void ColorValueViewer::draw(RotatedDC& dc) { // is there a mask? loadMask(dc); if (alpha_mask) { - Image img(alpha_mask->size.x, alpha_mask->size.y); - fill_image(img, value().value()); - alpha_mask->setAlpha(img); - dc.DrawImage(img, RealPoint(0,0), style().combine); + 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 && @@ -97,7 +94,7 @@ void ColorValueViewer::onStyleChange(int 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->size == wxSize(w,h)) return; // mask loaded and right size + 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); diff --git a/src/render/value/image.cpp b/src/render/value/image.cpp index e2d2a13e..fc98aa8f 100644 --- a/src/render/value/image.cpp +++ b/src/render/value/image.cpp @@ -121,7 +121,7 @@ void ImageValueViewer::onStyleChange(int 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->size == wxSize(w,h)) return; // mask loaded and right size + 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); diff --git a/src/render/value/text.cpp b/src/render/value/text.cpp index 2d63d567..11d842b1 100644 --- a/src/render/value/text.cpp +++ b/src/render/value/text.cpp @@ -15,7 +15,7 @@ IMPLEMENT_VALUE_VIEWER(Text); bool TextValueViewer::prepare(RotatedDC& dc) { - if (!style().mask_filename.empty() && !style().mask.ok()) { + if (!style().mask_filename.empty() && !style().mask.isLoaded()) { // load contour mask Image image; InputStreamP image_file = getStylePackage().openIn(style().mask_filename); diff --git a/src/script/image.cpp b/src/script/image.cpp index 513b1361..acba6423 100644 --- a/src/script/image.cpp +++ b/src/script/image.cpp @@ -143,13 +143,7 @@ void CachedScriptableImage::generateCached(const GeneratedImage::Options& option *size = cached_size = RealSize(options.width, options.height); if (mask && mask->Ok()) { // apply mask - if (mask->GetWidth() == cached_i.GetWidth() && mask->GetHeight() == cached_i.GetHeight()) { - set_alpha(cached_i, *mask); - } else { - Image mask_scaled(cached_i.GetWidth(),cached_i.GetHeight(), false); - resample(*mask,mask_scaled); - set_alpha(cached_i, mask_scaled); - } + set_alpha(cached_i, *mask); } if (*combine <= COMBINE_NORMAL) { *bitmap = cached_b = Bitmap(cached_i);