Commit 1098ce52 authored by twanvl's avatar twanvl

Merged behaviour from ContourMask into AlphaMask.

parent 237f6e3e
...@@ -214,8 +214,11 @@ void ChoiceStyle::initImage() { ...@@ -214,8 +214,11 @@ 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;
| mask_filename.update(ctx) * CHANGE_MASK; if (mask_filename.update(ctx)) {
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;
......
...@@ -68,7 +68,7 @@ class TextStyle : public Style { ...@@ -68,7 +68,7 @@ class TextStyle : public Style {
double line_height_line_max; ///< Maximum line height double line_height_line_max; ///< Maximum line height
double paragraph_height; ///< Fixed height of paragraphs double paragraph_height; ///< Fixed height of paragraphs
String mask_filename; ///< Filename of the mask 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? Direction direction; ///< In what direction is text layed out?
// information from text rendering // information from text rendering
double content_width, content_height; ///< Size of the rendered text double content_width, content_height; ///< Size of the rendered text
......
...@@ -77,11 +77,9 @@ void mask_blend(Image& img1, const Image& img2, const Image& mask) { ...@@ -77,11 +77,9 @@ void mask_blend(Image& img1, const Image& img2, const Image& mask) {
// ----------------------------------------------------------------------------- : Alpha // ----------------------------------------------------------------------------- : Alpha
void set_alpha(Image& img, const Image& img_alpha) { void set_alpha(Image& img, const Image& img_alpha) {
if (img.GetWidth() != img_alpha.GetWidth() || img.GetHeight() != img_alpha.GetHeight()) { Image img_alpha_resampled = resample(img_alpha, img.GetWidth(), img.GetHeight());
throw Error(_("Image must have same size as mask"));
}
if (!img.HasAlpha()) img.InitAlpha(); 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(); size_t size = img.GetWidth() * img.GetHeight();
for (size_t i = 0 ; i < size ; ++i) { for (size_t i = 0 ; i < size ; ++i) {
im[i] = (im[i] * al[i*3]) / 255; im[i] = (im[i] * al[i*3]) / 255;
......
...@@ -153,9 +153,15 @@ void set_alpha(Image& img, double alpha); ...@@ -153,9 +153,15 @@ void set_alpha(Image& img, double alpha);
*/ */
class AlphaMask : public IntrusivePtrBase<AlphaMask> { class AlphaMask : public IntrusivePtrBase<AlphaMask> {
public: public:
AlphaMask();
AlphaMask(const Image& mask); AlphaMask(const Image& mask);
~AlphaMask(); ~AlphaMask();
/// Load an alpha mask
void load(const Image& image);
/// Unload the mask
void clear();
/// Apply the alpha mask to an image /// Apply the alpha mask to an image
void setAlpha(Image& i) const; void setAlpha(Image& i) const;
/// Apply the alpha mask to a bitmap /// Apply the alpha mask to a bitmap
...@@ -167,36 +173,27 @@ class AlphaMask : public IntrusivePtrBase<AlphaMask> { ...@@ -167,36 +173,27 @@ class AlphaMask : public IntrusivePtrBase<AlphaMask> {
/// 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;
/// Size of the mask /// Make an image of the given color using this mask
wxSize size; Image colorImage(const Color& color) const;
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; }
/// 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 */
double rowLeft (double y, RealSize size) const; double rowLeft (double y, 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, 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: private:
int width, height; wxSize size; ///< Size of the mask
int *lefts, *rights; Byte* alpha; ///< Data of alpha mask
mutable int *lefts, *rights; ///< Row sizes
/// Compute lefts and rights from alpha
void loadRowSizes() const;
}; };
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
......
...@@ -12,29 +12,43 @@ ...@@ -12,29 +12,43 @@
// ----------------------------------------------------------------------------- : AlphaMask // ----------------------------------------------------------------------------- : AlphaMask
AlphaMask::AlphaMask(const Image& img) AlphaMask::AlphaMask() : alpha(nullptr), lefts(nullptr), rights(nullptr) {}
: size(img.GetWidth(), img.GetHeight()) 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 // Copy red chanel to alpha
size_t n = size.GetWidth() * size.GetHeight();
alpha = new Byte[n];
Byte* from = img.GetData(), *to = alpha; Byte* from = img.GetData(), *to = alpha;
for (size_t i = 0 ; i < n ; ++i) { for (size_t i = 0 ; i < n ; ++i) {
*to = *from; to[i] = from[3*i];
from += 3;
to += 1;
} }
} }
AlphaMask::~AlphaMask() {
delete[] alpha;
}
void AlphaMask::setAlpha(Image& img) const { void AlphaMask::setAlpha(Image& img) const {
if (!alpha) return;
set_alpha(img, alpha, size); set_alpha(img, alpha, size);
} }
void AlphaMask::setAlpha(Bitmap& bmp) const { void AlphaMask::setAlpha(Bitmap& bmp) const {
if (!alpha) return;
Image img = bmp.ConvertToImage(); Image img = bmp.ConvertToImage();
setAlpha(img); setAlpha(img);
bmp = Bitmap(img); bmp = Bitmap(img);
...@@ -42,6 +56,7 @@ void AlphaMask::setAlpha(Bitmap& bmp) const { ...@@ -42,6 +56,7 @@ void AlphaMask::setAlpha(Bitmap& bmp) const {
bool AlphaMask::isTransparent(int x, int y) const { bool AlphaMask::isTransparent(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;
return alpha[x + y * size.x] < 20; return alpha[x + y * size.x] < 20;
} }
...@@ -59,7 +74,8 @@ void make_convex(vector<wxPoint>& points) { ...@@ -59,7 +74,8 @@ void make_convex(vector<wxPoint>& points) {
} }
void AlphaMask::convexHull(vector<wxPoint>& points) const { void AlphaMask::convexHull(vector<wxPoint>& points) const {
// Left side if (!alpha) throw InternalError(_("AlphaMask::convexHull"));
// Left side, top to bottom
int miny = size.y, maxy = -1, lastx = 0; int miny = size.y, maxy = -1, lastx = 0;
for (int y = 0 ; y < size.y ; ++y) { for (int y = 0 ; y < size.y ; ++y) {
for (int x = 0 ; x < size.x ; ++x) { for (int x = 0 ; x < size.x ; ++x) {
...@@ -80,7 +96,7 @@ void AlphaMask::convexHull(vector<wxPoint>& points) const { ...@@ -80,7 +96,7 @@ void AlphaMask::convexHull(vector<wxPoint>& points) const {
if (maxy == -1) return; // No image if (maxy == -1) return; // No image
points.push_back(wxPoint(lastx-1,maxy+1)); points.push_back(wxPoint(lastx-1,maxy+1));
make_convex(points); make_convex(points);
// Right side // 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) {
...@@ -100,54 +116,46 @@ void AlphaMask::convexHull(vector<wxPoint>& points) const { ...@@ -100,54 +116,46 @@ void AlphaMask::convexHull(vector<wxPoint>& points) const {
make_convex(points); make_convex(points);
} }
// ----------------------------------------------------------------------------- : ContourMask Image AlphaMask::colorImage(const Color& color) const {
Image image(size.x, size.y);
ContourMask::ContourMask() fill_image(image, color);
: width(0), height(0), lefts(nullptr), rights(nullptr) setAlpha(image);
{} return image;
ContourMask::~ContourMask() {
unload();
} }
void ContourMask::load(const Image& image) { // ----------------------------------------------------------------------------- : Contour Mask
unload();
width = image.GetWidth(); void AlphaMask::loadRowSizes() const {
height = image.GetHeight(); if (lefts || !alpha) return;
lefts = new int[height]; lefts = new int[size.y];
rights = new int[height]; rights = new int[size.y];
// for each row: determine left and rightmost white pixel // for each row: determine left and rightmost white pixel
Byte* data = image.GetData(); for (int y = 0 ; y < size.y ; ++y) {
for (int y = 0 ; y < height ; ++y) { lefts[y] = size.x;
lefts[y] = width; rights[y] = width; rights[y] = 0;
for (int x = 0 ; x < width ; ++x) { for (int x = 0 ; x < size.x ; ++x) {
int v = data[0] + data[1] + data[2]; if (alpha[y * size.x + x] > 64) { // white enough
if (v > 50) { // white enough
rights[y] = x; rights[y] = x;
if (x < lefts[y]) lefts[y] = x; if (x < lefts[y]) lefts[y] = x;
} }
data += 3;
} }
} }
} }
void ContourMask::unload() { double AlphaMask::rowLeft (double y, RealSize resize) const {
delete lefts; loadRowSizes();
delete rights; if (!lefts || y < 0 || y >= resize.height) {
lefts = rights = nullptr;
width = height = 0;
}
double ContourMask::rowLeft (double y, RealSize size) const {
if (!ok() || y < 0 || y >= size.height) {
// no mask, or outside it // no mask, or outside it
return 0; 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 // 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;
} }
...@@ -359,7 +359,7 @@ void ImageSlicePreview::onPaint(wxPaintEvent&) { ...@@ -359,7 +359,7 @@ 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->size == slice.target_size) { if (mask && mask->hasSize(slice.target_size)) {
mask->setAlpha(image); mask->setAlpha(image);
} }
if (image.HasAlpha()) { if (image.HasAlpha()) {
......
...@@ -45,10 +45,7 @@ void ColorValueViewer::draw(RotatedDC& dc) { ...@@ -45,10 +45,7 @@ void ColorValueViewer::draw(RotatedDC& dc) {
// is there a mask? // is there a mask?
loadMask(dc); loadMask(dc);
if (alpha_mask) { if (alpha_mask) {
Image img(alpha_mask->size.x, alpha_mask->size.y); dc.DrawImage(alpha_mask->colorImage(value().value()), RealPoint(0,0), style().combine);
fill_image(img, value().value());
alpha_mask->setAlpha(img);
dc.DrawImage(img, 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 &&
...@@ -97,7 +94,7 @@ void ColorValueViewer::onStyleChange(int changes) { ...@@ -97,7 +94,7 @@ void ColorValueViewer::onStyleChange(int changes) {
void ColorValueViewer::loadMask(const Rotation& rot) const { void ColorValueViewer::loadMask(const Rotation& rot) const {
if (style().mask_filename().empty()) return; // no mask if (style().mask_filename().empty()) return; // no mask
int w = (int) rot.trX(rot.getWidth()), h = (int) rot.trY(rot.getHeight()); 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 // (re) load the mask
Image image; Image image;
InputStreamP image_file = getStylePackage().openIn(style().mask_filename); InputStreamP image_file = getStylePackage().openIn(style().mask_filename);
......
...@@ -121,7 +121,7 @@ void ImageValueViewer::onStyleChange(int changes) { ...@@ -121,7 +121,7 @@ void ImageValueViewer::onStyleChange(int changes) {
void ImageValueViewer::loadMask(const Rotation& rot) const { void ImageValueViewer::loadMask(const Rotation& rot) const {
if (style().mask_filename().empty()) return; // no mask if (style().mask_filename().empty()) return; // no mask
int w = (int) rot.trX(style().width), h = (int) rot.trY(style().height); 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 // (re) load the mask
Image image; Image image;
InputStreamP image_file = getStylePackage().openIn(style().mask_filename); InputStreamP image_file = getStylePackage().openIn(style().mask_filename);
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
IMPLEMENT_VALUE_VIEWER(Text); IMPLEMENT_VALUE_VIEWER(Text);
bool TextValueViewer::prepare(RotatedDC& dc) { bool TextValueViewer::prepare(RotatedDC& dc) {
if (!style().mask_filename.empty() && !style().mask.ok()) { if (!style().mask_filename.empty() && !style().mask.isLoaded()) {
// load contour mask // load contour mask
Image image; Image image;
InputStreamP image_file = getStylePackage().openIn(style().mask_filename); InputStreamP image_file = getStylePackage().openIn(style().mask_filename);
......
...@@ -143,13 +143,7 @@ void CachedScriptableImage::generateCached(const GeneratedImage::Options& option ...@@ -143,13 +143,7 @@ void CachedScriptableImage::generateCached(const GeneratedImage::Options& option
*size = cached_size = RealSize(options.width, options.height); *size = cached_size = RealSize(options.width, options.height);
if (mask && mask->Ok()) { if (mask && mask->Ok()) {
// apply mask // apply mask
if (mask->GetWidth() == cached_i.GetWidth() && mask->GetHeight() == cached_i.GetHeight()) { set_alpha(cached_i, *mask);
set_alpha(cached_i, *mask);
} else {
Image mask_scaled(cached_i.GetWidth(),cached_i.GetHeight(), false);
resample(*mask,mask_scaled);
set_alpha(cached_i, mask_scaled);
}
} }
if (*combine <= COMBINE_NORMAL) { if (*combine <= COMBINE_NORMAL) {
*bitmap = cached_b = Bitmap(cached_i); *bitmap = cached_b = Bitmap(cached_i);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment