mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
add stroke text, stretch symbols
This commit is contained in:
+14
-1
@@ -22,9 +22,13 @@ Font::Font()
|
||||
, scale_down_to(100000)
|
||||
, max_stretch(1.0)
|
||||
, color(Color(0,0,0))
|
||||
, shadow_color(Color(0,0,0))
|
||||
, shadow_displacement_x(0)
|
||||
, shadow_displacement_y(0)
|
||||
, shadow_blur(0)
|
||||
, stroke_color(Color(0,0,0))
|
||||
, stroke_radius(0)
|
||||
, stroke_blur(0)
|
||||
, separator_color(Color(0,0,0,128))
|
||||
, flags(FONT_NORMAL)
|
||||
{}
|
||||
@@ -96,6 +100,9 @@ bool Font::update(Context& ctx) {
|
||||
changes |= shadow_displacement_x.update(ctx);
|
||||
changes |= shadow_displacement_y.update(ctx);
|
||||
changes |= shadow_blur .update(ctx);
|
||||
changes |= stroke_color .update(ctx);
|
||||
changes |= stroke_radius .update(ctx);
|
||||
changes |= stroke_blur .update(ctx);
|
||||
flags = (flags & ~FONT_BOLD & ~FONT_ITALIC)
|
||||
| (weight() == _("bold") ? FONT_BOLD : FONT_NORMAL)
|
||||
| (style() == _("italic") ? FONT_ITALIC : FONT_NORMAL);
|
||||
@@ -114,6 +121,9 @@ void Font::initDependencies(Context& ctx, const Dependency& dep) const {
|
||||
shadow_displacement_x.initDependencies(ctx, dep);
|
||||
shadow_displacement_y.initDependencies(ctx, dep);
|
||||
shadow_blur .initDependencies(ctx, dep);
|
||||
stroke_color .initDependencies(ctx, dep);
|
||||
stroke_blur .initDependencies(ctx, dep);
|
||||
stroke_radius .initDependencies(ctx, dep);
|
||||
}
|
||||
|
||||
FontP Font::make(int add_flags, bool add_underline, bool add_strikethrough, String const* other_family, Color const* other_color, double const* other_size) const {
|
||||
@@ -204,9 +214,12 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(Font) {
|
||||
REFLECT(color);
|
||||
REFLECT(scale_down_to);
|
||||
REFLECT(max_stretch);
|
||||
REFLECT(shadow_color);
|
||||
REFLECT(shadow_displacement_x);
|
||||
REFLECT(shadow_displacement_y);
|
||||
REFLECT(shadow_color);
|
||||
REFLECT(shadow_blur);
|
||||
REFLECT(stroke_color);
|
||||
REFLECT(stroke_radius);
|
||||
REFLECT(stroke_blur);
|
||||
REFLECT(separator_color);
|
||||
}
|
||||
|
||||
+19
-12
@@ -29,11 +29,11 @@ enum FontFlags
|
||||
, FONT_CODE_OPER = 0x80 // syntax highlighting
|
||||
};
|
||||
|
||||
/// A font for rendering text
|
||||
/// A reference to a font for rendering text
|
||||
/** Contains additional information about scaling, color and shadow */
|
||||
class Font : public IntrusivePtrBase<Font> {
|
||||
public:
|
||||
Scriptable<String> name; ///< Name of the font
|
||||
Scriptable<String> name; ///< Name of the referenced font
|
||||
Scriptable<String> italic_name; ///< Font name for italic text (optional)
|
||||
Scriptable<double> size; ///< Size of the font
|
||||
Scriptable<String> weight, style; ///< Weight and style of the font (bold/italic)
|
||||
@@ -42,10 +42,13 @@ public:
|
||||
double scale_down_to; ///< Smallest size to scale down to
|
||||
double max_stretch; ///< How much should the font be stretched before scaling down?
|
||||
Scriptable<Color> color; ///< Color to use
|
||||
Scriptable<Color> shadow_color; ///< Color for shadow
|
||||
Scriptable<double> shadow_displacement_x;///< Position of the shadow
|
||||
Scriptable<double> shadow_displacement_y;///< Position of the shadow
|
||||
Scriptable<double> shadow_blur; ///< Blur radius of the shadow
|
||||
Scriptable<Color> shadow_color; ///< Color for the shadow
|
||||
Scriptable<double> shadow_displacement_x;///< Offset of the shadow in pixels, for a font size of 15
|
||||
Scriptable<double> shadow_displacement_y;///< Offset of the shadow in pixels, for a font size of 15
|
||||
Scriptable<double> shadow_blur; ///< Blur radius of the shadow in pixels, for a font size of 15
|
||||
Scriptable<Color> stroke_color; ///< Color for the stroke
|
||||
Scriptable<double> stroke_radius; ///< Thickness of the stroke in pixels, for a font size of 15
|
||||
Scriptable<double> stroke_blur; ///< Blur radius of the stroke in pixels, for a font size of 15
|
||||
Color separator_color; ///< Color for <sep> text
|
||||
int flags; ///< FontFlags for this font
|
||||
|
||||
@@ -61,16 +64,20 @@ public:
|
||||
bool update(Context& ctx);
|
||||
/// Add the given dependency to the dependent_scripts list for the variables this font depends on
|
||||
void initDependencies(Context&, const Dependency&) const;
|
||||
|
||||
/// Does this font have a shadow?
|
||||
|
||||
/// Add a shadow under the text?
|
||||
inline bool hasShadow() const {
|
||||
return shadow_displacement_x != 0.0 || shadow_displacement_y != 0.0;
|
||||
return (!almost_equal(shadow_blur(), 0.0) || !almost_equal(shadow_displacement_x(), 0.0) || !almost_equal(shadow_displacement_y(), 0.0)) && shadow_color().Alpha() != 0;
|
||||
}
|
||||
|
||||
/// Add style to a font, and optionally change the font family, color and size
|
||||
/// Add a stroke effect around the text?
|
||||
inline bool hasStroke() const {
|
||||
return (!almost_equal(stroke_blur(), 0.0) || !almost_equal(stroke_radius(), 0.0)) && stroke_color().Alpha() != 0;
|
||||
}
|
||||
|
||||
/// Add style, and optionally change the font family, color and size
|
||||
FontP make(int add_flags, bool add_underline, bool add_strikethrough, String const* other_family, Color const* other_color, double const* other_size) const;
|
||||
|
||||
/// Convert this font to a wxFont
|
||||
/// Convert this font reference to a wxFont
|
||||
wxFont toWxFont(double scale) const;
|
||||
|
||||
private:
|
||||
|
||||
+163
-26
@@ -7,6 +7,7 @@
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <util/prec.hpp>
|
||||
#include <gfx/gfx.hpp>
|
||||
#include <data/symbol_font.hpp>
|
||||
#include <data/stylesheet.hpp>
|
||||
#include <util/dynamic_arg.hpp>
|
||||
@@ -269,31 +270,131 @@ SymbolInFont* SymbolFont::defaultSymbol() const {
|
||||
|
||||
// ----------------------------------------------------------------------------- : SymbolFont : drawing
|
||||
|
||||
void SymbolFont::draw(RotatedDC& dc, Context& ctx, const RealRect& rect, double font_size, const Alignment& align, const String& text) {
|
||||
void SymbolFont::draw(RotatedDC& dc, Context& ctx, const RealRect& rect, double scale, const SymbolFontRef& font, const String& text) {
|
||||
SplitSymbols symbols;
|
||||
update(ctx);
|
||||
split(text, symbols);
|
||||
draw(dc, rect, font_size, align, symbols);
|
||||
draw(dc, rect, scale, font, symbols);
|
||||
}
|
||||
|
||||
void SymbolFont::draw(RotatedDC& dc, RealRect rect, double font_size, const Alignment& align, const SplitSymbols& text) {
|
||||
void SymbolFont::draw(RotatedDC& dc, RealRect rect, double scale, const SymbolFontRef& font, const SplitSymbols& text) {
|
||||
double stretch = dc.getStretch();
|
||||
FOR_EACH_CONST(sym, text) {
|
||||
RealSize size = dc.trInvS(symbolSize(dc.trS(font_size), sym));
|
||||
RealRect sym_rect = split_left(rect, size);
|
||||
drawSymbol(dc, sym_rect, font_size, align, *sym.symbol, sym.draw_text);
|
||||
RealSize size = dc.trInvS(symbolSize(dc.trS(scale * font.size), sym));
|
||||
size = RealSize(size.width*stretch, size.height);
|
||||
RealRect sym_rect = split_left(rect, size);
|
||||
// TODO: draw all stroke effects and shadows first, then draw all symbols
|
||||
drawSymbol(dc, sym_rect, scale, font, *sym.symbol, sym.draw_text, stretch);
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolFont::drawSymbol(RotatedDC& dc, RealRect sym_rect, double font_size, const Alignment& align, SymbolInFont& sym, const String& text) {
|
||||
// 1. draw symbol
|
||||
// find bitmap
|
||||
Bitmap bmp = sym.getBitmap(*this, dc.trS(font_size));
|
||||
// draw aligned in the rectangle
|
||||
void SymbolFont::drawSymbol(RotatedDC& dc, RealRect sym_rect, double scale, const SymbolFontRef& font, SymbolInFont& sym, const String& text, double stretch) {
|
||||
// 1. find bitmap and pos
|
||||
double font_size = scale * font.size();
|
||||
double font_size_ext = dc.trS(scale * font.size());
|
||||
Bitmap bmp = sym.getBitmap(*this, font_size_ext);
|
||||
if (!almost_equal(stretch, 1.0)) {
|
||||
bmp = Bitmap(resample(bmp.ConvertToImage(), bmp.GetWidth() * stretch, bmp.GetHeight()));
|
||||
}
|
||||
RealSize bmp_size = dc.trInvS(RealSize(bmp));
|
||||
RealPoint bmp_pos = align_in_rect(align, bmp_size, sym_rect);
|
||||
RealPoint bmp_pos = align_in_rect(font.alignment(), bmp_size, sym_rect);
|
||||
// 2. draw potential stroke or shadow
|
||||
double s_scale = font_size_ext / 15.;
|
||||
if (font.hasStroke()) {
|
||||
// add margin
|
||||
Image img = bmp.ConvertToImage();
|
||||
if (!img.HasAlpha()) set_alpha(img, 0);
|
||||
int blur_radius = lround(font.stroke_blur() * s_scale);
|
||||
int stroke_radius = lround(font.stroke_radius() * s_scale);
|
||||
int margin = blur_radius + stroke_radius;
|
||||
int s_width = img.GetWidth() + 2 * margin, s_height = img.GetHeight() + 2 * margin;
|
||||
int x_end = s_width - margin;
|
||||
int y_end = s_height - margin;
|
||||
wxImage s_img(s_width, s_height, false);
|
||||
s_img.InitAlpha();
|
||||
// convert to stroke color
|
||||
Byte* s_data = s_img.GetData();
|
||||
Byte* s_alpha = s_img.GetAlpha(), *alpha = img.GetAlpha();
|
||||
Color color = font.stroke_color();
|
||||
unsigned char r = color.Red();
|
||||
unsigned char g = color.Green();
|
||||
unsigned char b = color.Blue();
|
||||
unsigned char a = color.Alpha();
|
||||
for (int y = 0 ; y < s_height ; ++y) {
|
||||
for (int x = 0 ; x < s_width ; ++x) {
|
||||
s_data[0] = r;
|
||||
s_data[1] = g;
|
||||
s_data[2] = b;
|
||||
s_data += 3;
|
||||
if (margin <= x && x < x_end && margin <= y && y < y_end) {
|
||||
s_alpha[0] = alpha[0] * a / 255;
|
||||
alpha += 1;
|
||||
}
|
||||
else {
|
||||
s_alpha[0] = 0;
|
||||
}
|
||||
s_alpha += 1;
|
||||
}
|
||||
}
|
||||
// add stroke effect
|
||||
for (int i = 0 ; i < stroke_radius ; ++i) {
|
||||
thicken_image_alpha(s_img, 1);
|
||||
}
|
||||
// add blur
|
||||
for (int i = 0 ; i < blur_radius ; ++i) {
|
||||
blur_image_alpha(s_img, 3);
|
||||
}
|
||||
// draw
|
||||
RealSize s_size = dc.trInvS(RealSize(s_img));
|
||||
RealPoint s_pos(bmp_pos.x - (s_size.width - bmp_size.width)/2, bmp_pos.y - (s_size.height - bmp_size.height)/2);
|
||||
dc.DrawImage(s_img, s_pos);
|
||||
}
|
||||
else if (font.hasShadow()) {
|
||||
// add margin
|
||||
Image img = bmp.ConvertToImage();
|
||||
if (!img.HasAlpha()) set_alpha(img, 0);
|
||||
int margin = lround(font.shadow_blur() * s_scale);
|
||||
int s_width = img.GetWidth() + 2 * margin, s_height = img.GetHeight() + 2 * margin;
|
||||
int x_end = s_width - margin;
|
||||
int y_end = s_height - margin;
|
||||
wxImage s_img(s_width, s_height, false);
|
||||
s_img.InitAlpha();
|
||||
// convert to shadow color
|
||||
Byte* s_data = s_img.GetData();
|
||||
Byte* s_alpha = s_img.GetAlpha(), *alpha = img.GetAlpha();
|
||||
Color color = font.shadow_color();
|
||||
unsigned char r = color.Red();
|
||||
unsigned char g = color.Green();
|
||||
unsigned char b = color.Blue();
|
||||
unsigned char a = color.Alpha();
|
||||
for (int y = 0 ; y < s_height ; ++y) {
|
||||
for (int x = 0 ; x < s_width ; ++x) {
|
||||
s_data[0] = r;
|
||||
s_data[1] = g;
|
||||
s_data[2] = b;
|
||||
s_data += 3;
|
||||
if (margin <= x && x < x_end && margin <= y && y < y_end) {
|
||||
s_alpha[0] = alpha[0] * a / 255;
|
||||
alpha += 1;
|
||||
}
|
||||
else {
|
||||
s_alpha[0] = 0;
|
||||
}
|
||||
s_alpha += 1;
|
||||
}
|
||||
}
|
||||
// add blur
|
||||
for (int i = 0 ; i < margin ; ++i) {
|
||||
blur_image_alpha(s_img, 3);
|
||||
}
|
||||
// draw
|
||||
RealSize s_size = dc.trInvS(RealSize(s_img));
|
||||
RealPoint s_pos(bmp_pos.x - (s_size.width - bmp_size.width)/2, bmp_pos.y - (s_size.height - bmp_size.height)/2);
|
||||
dc.DrawImage(s_img, s_pos + RealPoint(font.shadow_displacement_x(), font.shadow_displacement_y()) * scale);
|
||||
}
|
||||
// 3. draw bitmap
|
||||
dc.DrawBitmap(bmp, bmp_pos);
|
||||
|
||||
// 2. draw text
|
||||
// 4. draw text
|
||||
if (text.empty() || !sym.text_font) return;
|
||||
// only use the bitmap rectangle
|
||||
sym_rect = RealRect(bmp_pos, bmp_size);
|
||||
@@ -304,7 +405,7 @@ void SymbolFont::drawSymbol(RotatedDC& dc, RealRect sym_rect, double font_size,
|
||||
sym_rect.height -= font_size * (sym.text_margin_top + sym.text_margin_bottom);
|
||||
// setup text, shrink it
|
||||
double size = font_size * sym.text_font->size;
|
||||
double stretch = 1.0;
|
||||
double text_stretch = 1.0;
|
||||
RealSize ts;
|
||||
while (true) {
|
||||
if (size <= 0) return; // text too small
|
||||
@@ -314,7 +415,7 @@ void SymbolFont::drawSymbol(RotatedDC& dc, RealRect sym_rect, double font_size,
|
||||
if (ts.width <= sym_rect.width) {
|
||||
break; // text fits
|
||||
} else if (ts.width * sym.text_font->max_stretch <= sym_rect.width) {
|
||||
stretch = sym_rect.width / ts.width;
|
||||
text_stretch = sym_rect.width / ts.width;
|
||||
ts.width = sym_rect.width; // for alignment
|
||||
break;
|
||||
}
|
||||
@@ -325,7 +426,7 @@ void SymbolFont::drawSymbol(RotatedDC& dc, RealRect sym_rect, double font_size,
|
||||
// align text
|
||||
RealPoint text_pos = align_in_rect(sym.text_alignment, ts, sym_rect);
|
||||
// draw text
|
||||
dc.DrawTextWithShadow(text, *sym.text_font, text_pos, font_size, stretch);
|
||||
dc.DrawTextWithShadowOrStroke(text, *sym.text_font, text_pos, font_size, text_stretch);
|
||||
}
|
||||
|
||||
Image SymbolFont::getImage(double font_size, const DrawableSymbol& sym) {
|
||||
@@ -345,7 +446,7 @@ Image SymbolFont::getImage(double font_size, const DrawableSymbol& sym) {
|
||||
sym_rect.height -= font_size * (sym.symbol->text_margin_top + sym.symbol->text_margin_bottom);
|
||||
// setup text, shrink it
|
||||
double size = font_size * sym.symbol->text_font->size;
|
||||
double stretch = 1.0;
|
||||
double text_stretch = 1.0;
|
||||
RealSize ts;
|
||||
while (true) {
|
||||
if (size <= 0) return sym.symbol->getImage(*this, font_size); // text too small
|
||||
@@ -355,7 +456,7 @@ Image SymbolFont::getImage(double font_size, const DrawableSymbol& sym) {
|
||||
if (ts.width <= sym_rect.width) {
|
||||
break; // text fits
|
||||
} else if (ts.width * sym.symbol->text_font->max_stretch <= sym_rect.width) {
|
||||
stretch = sym_rect.width / ts.width;
|
||||
text_stretch = sym_rect.width / ts.width;
|
||||
ts.width = sym_rect.width; // for alignment
|
||||
break;
|
||||
}
|
||||
@@ -366,7 +467,7 @@ Image SymbolFont::getImage(double font_size, const DrawableSymbol& sym) {
|
||||
// align text
|
||||
RealPoint text_pos = align_in_rect(sym.symbol->text_alignment, ts, sym_rect);
|
||||
// draw text
|
||||
rdc.DrawTextWithShadow(sym.draw_text, *sym.symbol->text_font, text_pos, font_size, stretch);
|
||||
rdc.DrawTextWithShadowOrStroke(sym.draw_text, *sym.symbol->text_font, text_pos, font_size, text_stretch);
|
||||
// done
|
||||
dc.SelectObject(wxNullBitmap);
|
||||
return bmp.ConvertToImage();
|
||||
@@ -568,8 +669,17 @@ void after_reading(InsertSymbolMenu& m, Version ver) {
|
||||
SymbolFontRef::SymbolFontRef()
|
||||
: size(12)
|
||||
, scale_down_to(1)
|
||||
, underline(false)
|
||||
, strikethrough(false)
|
||||
, alignment(ALIGN_MIDDLE_CENTER)
|
||||
{}
|
||||
, shadow_color(Color(0,0,0))
|
||||
, shadow_displacement_x(0)
|
||||
, shadow_displacement_y(0)
|
||||
, shadow_blur(0)
|
||||
, stroke_color(Color(0,0,0))
|
||||
, stroke_radius(0)
|
||||
, stroke_blur(0)
|
||||
{}
|
||||
|
||||
bool SymbolFontRef::valid() const {
|
||||
return !!font;
|
||||
@@ -584,14 +694,32 @@ bool SymbolFontRef::update(Context& ctx) {
|
||||
} else if (!font) {
|
||||
loadFont(ctx);
|
||||
}
|
||||
changes |= size.update(ctx);
|
||||
changes |= alignment.update(ctx);
|
||||
changes |= size .update(ctx);
|
||||
changes |= underline .update(ctx);
|
||||
changes |= strikethrough .update(ctx);
|
||||
changes |= alignment .update(ctx);
|
||||
changes |= shadow_color .update(ctx);
|
||||
changes |= shadow_displacement_x.update(ctx);
|
||||
changes |= shadow_displacement_y.update(ctx);
|
||||
changes |= shadow_blur .update(ctx);
|
||||
changes |= stroke_color .update(ctx);
|
||||
changes |= stroke_radius .update(ctx);
|
||||
changes |= stroke_blur .update(ctx);
|
||||
return changes;
|
||||
}
|
||||
void SymbolFontRef::initDependencies(Context& ctx, const Dependency& dep) const {
|
||||
name.initDependencies(ctx, dep);
|
||||
size.initDependencies(ctx, dep);
|
||||
alignment.initDependencies(ctx, dep);
|
||||
name .initDependencies(ctx, dep);
|
||||
size .initDependencies(ctx, dep);
|
||||
underline .initDependencies(ctx, dep);
|
||||
strikethrough .initDependencies(ctx, dep);
|
||||
alignment .initDependencies(ctx, dep);
|
||||
shadow_color .initDependencies(ctx, dep);
|
||||
shadow_displacement_x.initDependencies(ctx, dep);
|
||||
shadow_displacement_y.initDependencies(ctx, dep);
|
||||
shadow_blur .initDependencies(ctx, dep);
|
||||
stroke_color .initDependencies(ctx, dep);
|
||||
stroke_radius .initDependencies(ctx, dep);
|
||||
stroke_blur .initDependencies(ctx, dep);
|
||||
}
|
||||
|
||||
void SymbolFontRef::loadFont(Context& ctx) {
|
||||
@@ -614,5 +742,14 @@ IMPLEMENT_REFLECTION(SymbolFontRef) {
|
||||
REFLECT(name);
|
||||
REFLECT(size);
|
||||
REFLECT(scale_down_to);
|
||||
REFLECT(underline);
|
||||
REFLECT(strikethrough);
|
||||
REFLECT(alignment);
|
||||
REFLECT(shadow_color);
|
||||
REFLECT(shadow_displacement_x);
|
||||
REFLECT(shadow_displacement_y);
|
||||
REFLECT(shadow_blur);
|
||||
REFLECT(stroke_color);
|
||||
REFLECT(stroke_radius);
|
||||
REFLECT(stroke_blur);
|
||||
}
|
||||
|
||||
+30
-11
@@ -19,7 +19,8 @@ DECLARE_POINTER_TYPE(Font);
|
||||
DECLARE_POINTER_TYPE(SymbolFont);
|
||||
DECLARE_POINTER_TYPE(SymbolInFont);
|
||||
DECLARE_POINTER_TYPE(InsertSymbolMenu);
|
||||
class RotatedDC;
|
||||
class RotatedDC;
|
||||
class SymbolFontRef;
|
||||
struct CharInfo;
|
||||
|
||||
// ----------------------------------------------------------------------------- : SymbolFont
|
||||
@@ -56,12 +57,12 @@ public:
|
||||
size_t recognizePrefix(const String& text, size_t start) const;
|
||||
|
||||
/// Draw a piece of text
|
||||
void draw(RotatedDC& dc, Context& ctx, const RealRect& rect, double font_size, const Alignment& align, const String& text);
|
||||
void draw(RotatedDC& dc, Context& ctx, const RealRect& rect, double scale, const SymbolFontRef& font, const String& text);
|
||||
/// Get information on characters in a string
|
||||
void getCharInfo(RotatedDC& dc, Context& ctx, double font_size, const String& text, vector<CharInfo>& out);
|
||||
|
||||
/// Draw a piece of text prepared using split
|
||||
void draw(RotatedDC& dc, RealRect rect, double font_size, const Alignment& align, const SplitSymbols& text);
|
||||
void draw(RotatedDC& dc, RealRect rect, double scale, const SymbolFontRef& font, const SplitSymbols& text);
|
||||
/// Get information on characters in a string
|
||||
void getCharInfo(RotatedDC& dc, double font_size, const SplitSymbols& text, vector<CharInfo>& out);
|
||||
|
||||
@@ -99,7 +100,7 @@ private:
|
||||
SymbolInFont* defaultSymbol() const;
|
||||
|
||||
/// Draws a single symbol inside the given rectangle
|
||||
void drawSymbol (RotatedDC& dc, RealRect sym_rect, double font_size, const Alignment& align, SymbolInFont& sym, const String& text);
|
||||
void drawSymbol(RotatedDC& dc, RealRect sym_rect, double scale, const SymbolFontRef& font, SymbolInFont& sym, const String& text, double stretch);
|
||||
|
||||
/// Size of a single symbol, including spacing
|
||||
RealSize symbolSize (double font_size, const DrawableSymbol& sym);
|
||||
@@ -158,15 +159,33 @@ public:
|
||||
bool update(Context& ctx);
|
||||
void initDependencies(Context&, const Dependency&) const;
|
||||
|
||||
/// Is a font loaded?
|
||||
/// Is the referenced symbol font loaded?
|
||||
bool valid() const;
|
||||
|
||||
Scriptable<String> name; ///< Font package name, can be changed with script
|
||||
Scriptable<double> size; ///< Size of the font
|
||||
double scale_down_to; ///< Mimumum size of the font
|
||||
Scriptable<Alignment> alignment; ///< Alignment of symbols in a line of text
|
||||
SymbolFontP font; ///< The font, if it is loaded
|
||||
|
||||
Scriptable<String> name; ///< The referenced symbol font's package name (folder name)
|
||||
Scriptable<double> size; ///< Size of the font
|
||||
double scale_down_to; ///< Minimum size of the font
|
||||
Scriptable<bool> underline; ///< Underlined?
|
||||
Scriptable<bool> strikethrough; ///< Struck through?
|
||||
Scriptable<Alignment> alignment; ///< Alignment of symbols in a line of text
|
||||
Scriptable<Color> shadow_color; ///< Color for the shadow
|
||||
Scriptable<double> shadow_displacement_x;///< Offset of the shadow in pixels, for a font size of 15
|
||||
Scriptable<double> shadow_displacement_y;///< Offset of the shadow in pixels, for a font size of 15
|
||||
Scriptable<double> shadow_blur; ///< Blur radius of the shadow in pixels, for a font size of 15
|
||||
Scriptable<Color> stroke_color; ///< Color for the stroke
|
||||
Scriptable<double> stroke_radius; ///< Thickness of the stroke in pixels, for a font size of 15
|
||||
Scriptable<double> stroke_blur; ///< Blur radius of the stroke in pixels, for a font size of 15
|
||||
SymbolFontP font; ///< The symbol font this is referencing, if it is loaded
|
||||
|
||||
/// Add a shadow under symbols?
|
||||
inline bool hasShadow() const {
|
||||
return (!almost_equal(shadow_blur(), 0.0) || !almost_equal(shadow_displacement_x(), 0.0) || !almost_equal(shadow_displacement_y(), 0.0)) && shadow_color().Alpha() != 0;
|
||||
}
|
||||
/// Add a stroke effect around symbols?
|
||||
inline bool hasStroke() const {
|
||||
return (!almost_equal(stroke_blur(), 0.0) || !almost_equal(stroke_radius(), 0.0)) && stroke_color().Alpha() != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
DECLARE_REFLECTION();
|
||||
|
||||
|
||||
+17
-1
@@ -53,11 +53,15 @@ void sharp_resample_and_clip(const Image& img_in, Image& img_out, wxRect rect, i
|
||||
* rect = rectangle to draw in (a rectangle somewhere around pos)
|
||||
* stretch = amount to stretch in the direction of the text after drawing
|
||||
*/
|
||||
void draw_resampled_text(DC& dc, const RealPoint& pos, const RealRect& rect, double stretch, Radians angle, Color color, const String& text, int blur_radius = 0, int repeat = 1);
|
||||
void draw_resampled_text(DC& dc, const String& text, const RealPoint& pos, const RealRect& rect, Radians angle, Color color, int blur_radius = 0, Color stroke_color = Color(0,0,0), int stroke_radius = 0, double stretch = 1.0);
|
||||
|
||||
// scaling factor to use when drawing resampled text
|
||||
extern const int text_scaling;
|
||||
|
||||
// Downsamples the red channel of the input image to the alpha channel of the output image
|
||||
// img_in must be text_scaling times as large as img_out
|
||||
void downsample_to_alpha(Bitmap& bmp_in, Image& img_out);
|
||||
|
||||
// ----------------------------------------------------------------------------- : Image rotation
|
||||
|
||||
/// Rotates an image counter clockwise
|
||||
@@ -92,6 +96,18 @@ void saturate(Image& image, double amount);
|
||||
/// Invert the colors in an image
|
||||
void invert(Image& img);
|
||||
|
||||
// Blur the alpha of a pixel
|
||||
Byte blur_pixel_alpha(Byte* in, int x, int y, int width, int height, int center_weight = 2);
|
||||
// Blur the alpha channel of an image
|
||||
void blur_image_alpha(Image& img, int center_weight = 2);
|
||||
|
||||
// Thicken the alpha of a pixel
|
||||
Byte thicken_pixel_alpha_3x3(std::vector<Byte> in, int i, int x, int y, int width, int height);
|
||||
Byte thicken_pixel_alpha_5x5(std::vector<Byte> in, int i, int x, int y, int width, int height);
|
||||
Byte thicken_pixel_alpha_7x7(std::vector<Byte> in, int i, int x, int y, int width, int height);
|
||||
// Thicken the alpha channel of an image
|
||||
void thicken_image_alpha(Image& img, int radius);
|
||||
|
||||
// ----------------------------------------------------------------------------- : Combining
|
||||
|
||||
/// Ways in which images can be combined, similair to what Photoshop supports
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <util/prec.hpp>
|
||||
#include <gfx/gfx.hpp>
|
||||
#include <util/error.hpp>
|
||||
#if defined(__WXMSW__) && wxUSE_WXDIB
|
||||
#include <wx/msw/dib.h>
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------- : Saturation
|
||||
|
||||
@@ -71,6 +74,294 @@ void invert(Image& img) {
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Blurring
|
||||
|
||||
Byte blur_pixel_alpha(Byte* in, int x, int y, int width, int height, int center_weight) {
|
||||
return ( center_weight * in[0] + // center
|
||||
(x == 0 ? in[0] : in[-1]) + // left
|
||||
(y == 0 ? in[0] : in[-width]) + // up
|
||||
(x == width - 1 ? in[0] : in[1]) + // right
|
||||
(y == height - 1 ? in[0] : in[width]) // down
|
||||
) / (4 + center_weight);
|
||||
}
|
||||
|
||||
void blur_image_alpha(Image& img, int center_weight) {
|
||||
if (!img.HasAlpha()) return;
|
||||
int width = img.GetWidth(), height = img.GetHeight();
|
||||
Byte* data = img.GetAlpha();
|
||||
for (int y = 0 ; y < height ; ++y) {
|
||||
for (int x = 0 ; x < width ; ++x) {
|
||||
*data = blur_pixel_alpha(data, x, y, width, height, center_weight);
|
||||
++data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Thickening
|
||||
|
||||
Byte thicken_pixel_alpha_7x7(std::vector<unsigned char> in, int i, int x, int y, int width, int height) {
|
||||
Byte result = in[i];
|
||||
Byte diag = in[i];
|
||||
Byte corner = in[i];
|
||||
if (x > 2) {
|
||||
if (y > 1) corner = max(corner, in[i -3 -2*width]);
|
||||
if (y > 0) diag = max(diag , in[i -3 -1*width]);
|
||||
result = max(result, in[i -3 ]);
|
||||
if (y < height - 1) diag = max(diag , in[i -3 +1*width]);
|
||||
if (y < height - 2) corner = max(corner, in[i -3 +2*width]);
|
||||
}
|
||||
if (x > 1) {
|
||||
if (y > 2) corner = max(corner, in[i -2 -3*width]);
|
||||
if (y > 1) result = max(result, in[i -2 -2*width]);
|
||||
if (y > 0) result = max(result, in[i -2 -1*width]);
|
||||
result = max(result, in[i -2 ]);
|
||||
if (y < height - 1) result = max(result, in[i -2 +1*width]);
|
||||
if (y < height - 2) result = max(result, in[i -2 +2*width]);
|
||||
if (y < height - 3) corner = max(corner, in[i -2 +3*width]);
|
||||
}
|
||||
if (x > 0) {
|
||||
if (y > 2) diag = max(diag , in[i -1 -3*width]);
|
||||
if (y > 1) result = max(result, in[i -1 -2*width]);
|
||||
if (y > 0) result = max(result, in[i -1 -1*width]);
|
||||
result = max(result, in[i -1 ]);
|
||||
if (y < height - 1) result = max(result, in[i -1 +1*width]);
|
||||
if (y < height - 2) result = max(result, in[i -1 +2*width]);
|
||||
if (y < height - 3) diag = max(diag , in[i -1 +3*width]);
|
||||
}
|
||||
|
||||
if (y > 2) result = max(result, in[i -3*width]);
|
||||
if (y > 1) result = max(result, in[i -2*width]);
|
||||
if (y > 0) result = max(result, in[i -1*width]);
|
||||
|
||||
if (y < height - 1) result = max(result, in[i +1*width]);
|
||||
if (y < height - 2) result = max(result, in[i +2*width]);
|
||||
if (y < height - 3) result = max(result, in[i +3*width]);
|
||||
|
||||
if (x < width - 1) {
|
||||
if (y > 2) diag = max(diag , in[i +1 -3*width]);
|
||||
if (y > 1) result = max(result, in[i +1 -2*width]);
|
||||
if (y > 0) result = max(result, in[i +1 -1*width]);
|
||||
result = max(result, in[i +1 ]);
|
||||
if (y < height - 1) result = max(result, in[i +1 +1*width]);
|
||||
if (y < height - 2) result = max(result, in[i +1 +2*width]);
|
||||
if (y < height - 3) diag = max(diag , in[i +1 +3*width]);
|
||||
}
|
||||
if (x < width - 2) {
|
||||
if (y > 2) corner = max(corner, in[i +2 -3*width]);
|
||||
if (y > 1) result = max(result, in[i +2 -2*width]);
|
||||
if (y > 0) result = max(result, in[i +2 -1*width]);
|
||||
result = max(result, in[i +2 ]);
|
||||
if (y < height - 1) result = max(result, in[i +2 +1*width]);
|
||||
if (y < height - 2) result = max(result, in[i +2 +2*width]);
|
||||
if (y < height - 3) corner = max(corner, in[i +2 +3*width]);
|
||||
}
|
||||
if (x < width - 3) {
|
||||
if (y > 1) corner = max(corner, in[i +3 -2*width]);
|
||||
if (y > 0) diag = max(diag , in[i +3 -1*width]);
|
||||
result = max(result, in[i +3 ]);
|
||||
if (y < height - 1) diag = max(diag , in[i +3 +1*width]);
|
||||
if (y < height - 2) corner = max(corner, in[i +3 +2*width]);
|
||||
}
|
||||
if (diag > result) result = (Byte)((4 * (int)diag + (int)result) / 5);
|
||||
if (corner > result) result = (Byte)((2 * (int)corner + 3 * (int)result) / 5);
|
||||
return result;
|
||||
}
|
||||
|
||||
Byte thicken_pixel_alpha_5x5(std::vector<unsigned char> in, int i, int x, int y, int width, int height) {
|
||||
Byte result = in[i];
|
||||
Byte diag = in[i];
|
||||
if (x > 1) {
|
||||
if (y > 0) diag = max(diag , in[i -2 -1*width]);
|
||||
result = max(result, in[i -2 ]);
|
||||
if (y < height - 1) diag = max(diag , in[i -2 +1*width]);
|
||||
}
|
||||
if (x > 0) {
|
||||
if (y > 1) diag = max(diag , in[i -1 -2*width]);
|
||||
if (y > 0) result = max(result, in[i -1 -1*width]);
|
||||
result = max(result, in[i -1 ]);
|
||||
if (y < height - 1) result = max(result, in[i -1 +1*width]);
|
||||
if (y < height - 2) diag = max(diag , in[i -1 +2*width]);
|
||||
}
|
||||
|
||||
if (y > 1) result = max(result, in[i -2*width]);
|
||||
if (y > 0) result = max(result, in[i -1*width]);
|
||||
|
||||
if (y < height - 1) result = max(result, in[i +1*width]);
|
||||
if (y < height - 2) result = max(result, in[i +2*width]);
|
||||
|
||||
if (x < width - 1) {
|
||||
if (y > 1) diag = max(diag , in[i +1 -2*width]);
|
||||
if (y > 0) result = max(result, in[i +1 -1*width]);
|
||||
result = max(result, in[i +1 ]);
|
||||
if (y < height - 1) result = max(result, in[i +1 +1*width]);
|
||||
if (y < height - 2) diag = max(diag , in[i +1 +2*width]);
|
||||
}
|
||||
if (x < width - 2) {
|
||||
if (y > 0) diag = max(diag , in[i +2 -1*width]);
|
||||
result = max(result, in[i +2 ]);
|
||||
if (y < height - 1) diag = max(diag , in[i +2 +1*width]);
|
||||
}
|
||||
if (diag > result) result = (Byte)((3 * (int)diag + (int)result) / 4);
|
||||
return result;
|
||||
}
|
||||
|
||||
Byte thicken_pixel_alpha_3x3(std::vector<unsigned char> in, int i, int x, int y, int width, int height) {
|
||||
Byte result = in[i];
|
||||
Byte diag = in[i];
|
||||
if (x > 0) {
|
||||
if (y > 0) diag = max(diag, in[i -1 -width]);
|
||||
result = max(result, in[i -1 ]);
|
||||
if (y < height - 1) diag = max(diag, in[i -1 +width]);
|
||||
}
|
||||
if (y > 0) result = max(result, in[i -width]);
|
||||
|
||||
if (y < height - 1) result = max(result, in[i +width]);
|
||||
if (x < width - 1) {
|
||||
if (y > 0) diag = max(diag, in[i +1 -width]);
|
||||
result = max(result, in[i +1 ]);
|
||||
if (y < height - 1) diag = max(diag, in[i +1 +width]);
|
||||
}
|
||||
if (diag > result) result = (Byte)((3*(int)diag + 2*(int)result) / 5);
|
||||
return result;
|
||||
}
|
||||
|
||||
void thicken_image_alpha(Image& img, int radius) {
|
||||
if (!img.HasAlpha()) return;
|
||||
int width = img.GetWidth(), height = img.GetHeight();
|
||||
Byte* data = img.GetAlpha();
|
||||
std::vector<Byte> copy(data, data + width * height);
|
||||
int i = 0;
|
||||
for (int y = 0 ; y < height ; ++y) {
|
||||
for (int x = 0 ; x < width ; ++x) {
|
||||
*data = radius == 1 ? thicken_pixel_alpha_3x3(copy, i, x, y, width, height) :
|
||||
radius == 2 ? thicken_pixel_alpha_5x5(copy, i, x, y, width, height) :
|
||||
thicken_pixel_alpha_7x7(copy, i, x, y, width, height) ;
|
||||
++data;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Resampled text
|
||||
|
||||
void downsample_to_alpha(Bitmap& bmp_in, Image& img_out) {
|
||||
Byte* temp = nullptr;
|
||||
#if defined(__WXMSW__) && wxUSE_WXDIB
|
||||
wxDIB img_in(bmp_in);
|
||||
if (!img_in.IsOk()) return;
|
||||
// if text_scaling = 4, then the line always is dword aligned, so we need no adjusting
|
||||
// we created a bitmap with depth 24, so that is what we should have here
|
||||
if (img_in.GetDepth() != 24) throw InternalError(_("DIB has wrong bit depth"));
|
||||
#else
|
||||
Image img_in = bmp_in.ConvertToImage();
|
||||
#endif
|
||||
Byte* in = img_in.GetData();
|
||||
Byte* out = img_in.GetData();
|
||||
// scale in the x direction, this overwrites parts of the input image
|
||||
if (img_in.GetWidth() == img_out.GetWidth() * text_scaling) {
|
||||
// no stretching
|
||||
int count = img_out.GetWidth() * img_in.GetHeight();
|
||||
for (int i = 0 ; i < count ; ++i) {
|
||||
int total = 0;
|
||||
for (int j = 0 ; j < text_scaling ; ++j) {
|
||||
total += in[3 * (j + text_scaling * i)];
|
||||
}
|
||||
out[i] = total / text_scaling;
|
||||
}
|
||||
} else {
|
||||
// resample to buffer
|
||||
temp = new Byte[img_out.GetWidth() * img_in.GetHeight()];
|
||||
out = temp;
|
||||
// custom stretch, see resample_image.cpp
|
||||
const int shift = 32-12-8; // => max size = 4096, max alpha = 255
|
||||
int w1 = img_in.GetWidth(), w2 = img_out.GetWidth(), h = img_in.GetHeight();
|
||||
int out_fact = (w2 << shift) / w1; // how much to output for 256 input = 1 pixel
|
||||
int out_rest = (w2 << shift) % w1;
|
||||
// make the image 'bolder' to compensate for compressing it
|
||||
int mul = 128 + min(256, 128*w1/(text_scaling*w2));
|
||||
for (int y = 0 ; y < h ; ++y) {
|
||||
int in_rem = out_fact + out_rest;
|
||||
for (int x = 0 ; x < w2 ; ++x) {
|
||||
int out_rem = 1 << shift;
|
||||
int tot = 0;
|
||||
while (out_rem >= in_rem) {
|
||||
// eat a whole input pixel
|
||||
tot += *in * in_rem;
|
||||
out_rem -= in_rem;
|
||||
in_rem = out_fact;
|
||||
in += 3;
|
||||
}
|
||||
if (out_rem > 0) {
|
||||
// eat a partial input pixel
|
||||
tot += *in * out_rem;
|
||||
in_rem -= out_rem;
|
||||
}
|
||||
// store
|
||||
*out = top(((tot >> shift) * mul) >> 8);
|
||||
out += 1;
|
||||
}
|
||||
}
|
||||
in = temp;
|
||||
}
|
||||
|
||||
// now scale in the y direction, and write to the output alpha
|
||||
img_out.InitAlpha();
|
||||
int line_size_in = img_out.GetWidth();
|
||||
#if defined(__WXMSW__) && wxUSE_WXDIB
|
||||
// DIBs are upside down
|
||||
out = img_out.GetAlpha() + (img_out.GetHeight() - 1) * line_size_in;
|
||||
int line_size_out = -line_size_in;
|
||||
#else
|
||||
out = img_out.GetAlpha();
|
||||
int line_size_out = line_size_in;
|
||||
#endif
|
||||
int h = img_out.GetHeight();
|
||||
if (img_in.GetHeight() == h * text_scaling) {
|
||||
// no stretching
|
||||
for (int y = 0 ; y < h ; ++y) {
|
||||
for (int x = 0 ; x < line_size_in ; ++x) {
|
||||
int total = 0;
|
||||
for (int j = 0 ; j < text_scaling ; ++j) {
|
||||
total += in[x + line_size_in * (j + text_scaling * y)];
|
||||
}
|
||||
out[x + line_size_out * y] = total / text_scaling;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const int shift = 32-12-8; // => max size = 4096, max alpha = 255
|
||||
int h1 = img_in.GetHeight(), w = img_out.GetWidth();
|
||||
int out_fact = (h << shift) / h1; // how much to output for 256 input = 1 pixel
|
||||
int out_rest = (h << shift) % h1;
|
||||
int mul = 128 + min(256, 128*h1/(text_scaling*h));
|
||||
for (int x = 0 ; x < w ; ++x) {
|
||||
int in_rem = out_fact + out_rest;
|
||||
for (int y = 0 ; y < h ; ++y) {
|
||||
int out_rem = 1 << shift;
|
||||
int tot = 0;
|
||||
while (out_rem >= in_rem) {
|
||||
// eat a whole input pixel
|
||||
tot += *in * in_rem;
|
||||
out_rem -= in_rem;
|
||||
in_rem = out_fact;
|
||||
in += line_size_in;
|
||||
}
|
||||
if (out_rem > 0) {
|
||||
// eat a partial input pixel
|
||||
tot += *in * out_rem;
|
||||
in_rem -= out_rem;
|
||||
}
|
||||
// store
|
||||
*out = top(((tot >> shift) * mul) >> 8);
|
||||
out += line_size_out;
|
||||
}
|
||||
in = in - h1 * line_size_in + 1;
|
||||
out = out - h * line_size_out + 1;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] temp;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Coloring symbol images
|
||||
|
||||
RGB recolor(RGB x, RGB cr, RGB cg, RGB cb, RGB cw) {
|
||||
|
||||
+53
-168
@@ -10,9 +10,6 @@
|
||||
#include <gfx/gfx.hpp>
|
||||
#include <util/error.hpp>
|
||||
#include <gui/util.hpp> // clearDC_black
|
||||
#if defined(__WXMSW__) && wxUSE_WXDIB
|
||||
#include <wx/msw/dib.h>
|
||||
#endif
|
||||
|
||||
void blur_image(const Image& img_in, Image& img_out);
|
||||
|
||||
@@ -21,189 +18,77 @@ void blur_image(const Image& img_in, Image& img_out);
|
||||
// scaling factor to use when drawing resampled text
|
||||
const int text_scaling = 4;
|
||||
|
||||
// Downsamples the red channel of the input image to the alpha channel of the output image
|
||||
// img_in must be text_scaling times as large as img_out
|
||||
void downsample_to_alpha(Bitmap& bmp_in, Image& img_out) {
|
||||
Byte* temp = nullptr;
|
||||
#if defined(__WXMSW__) && wxUSE_WXDIB
|
||||
wxDIB img_in(bmp_in);
|
||||
if (!img_in.IsOk()) return;
|
||||
// if text_scaling = 4, then the line always is dword aligned, so we need no adjusting
|
||||
// we created a bitmap with depth 24, so that is what we should have here
|
||||
if (img_in.GetDepth() != 24) throw InternalError(_("DIB has wrong bit depth"));
|
||||
#else
|
||||
Image img_in = bmp_in.ConvertToImage();
|
||||
#endif
|
||||
Byte* in = img_in.GetData();
|
||||
Byte* out = img_in.GetData();
|
||||
// scale in the x direction, this overwrites parts of the input image
|
||||
if (img_in.GetWidth() == img_out.GetWidth() * text_scaling) {
|
||||
// no stretching
|
||||
int count = img_out.GetWidth() * img_in.GetHeight();
|
||||
for (int i = 0 ; i < count ; ++i) {
|
||||
int total = 0;
|
||||
for (int j = 0 ; j < text_scaling ; ++j) {
|
||||
total += in[3 * (j + text_scaling * i)];
|
||||
}
|
||||
out[i] = total / text_scaling;
|
||||
}
|
||||
} else {
|
||||
// resample to buffer
|
||||
temp = new Byte[img_out.GetWidth() * img_in.GetHeight()];
|
||||
out = temp;
|
||||
// custom stretch, see resample_image.cpp
|
||||
const int shift = 32-12-8; // => max size = 4096, max alpha = 255
|
||||
int w1 = img_in.GetWidth(), w2 = img_out.GetWidth(), h = img_in.GetHeight();
|
||||
int out_fact = (w2 << shift) / w1; // how much to output for 256 input = 1 pixel
|
||||
int out_rest = (w2 << shift) % w1;
|
||||
// make the image 'bolder' to compensate for compressing it
|
||||
int mul = 128 + min(256, 128*w1/(text_scaling*w2));
|
||||
for (int y = 0 ; y < h ; ++y) {
|
||||
int in_rem = out_fact + out_rest;
|
||||
for (int x = 0 ; x < w2 ; ++x) {
|
||||
int out_rem = 1 << shift;
|
||||
int tot = 0;
|
||||
while (out_rem >= in_rem) {
|
||||
// eat a whole input pixel
|
||||
tot += *in * in_rem;
|
||||
out_rem -= in_rem;
|
||||
in_rem = out_fact;
|
||||
in += 3;
|
||||
}
|
||||
if (out_rem > 0) {
|
||||
// eat a partial input pixel
|
||||
tot += *in * out_rem;
|
||||
in_rem -= out_rem;
|
||||
}
|
||||
// store
|
||||
*out = top(((tot >> shift) * mul) >> 8);
|
||||
out += 1;
|
||||
}
|
||||
}
|
||||
in = temp;
|
||||
}
|
||||
|
||||
// now scale in the y direction, and write to the output alpha
|
||||
img_out.InitAlpha();
|
||||
int line_size_in = img_out.GetWidth();
|
||||
#if defined(__WXMSW__) && wxUSE_WXDIB
|
||||
// DIBs are upside down
|
||||
out = img_out.GetAlpha() + (img_out.GetHeight() - 1) * line_size_in;
|
||||
int line_size_out = -line_size_in;
|
||||
#else
|
||||
out = img_out.GetAlpha();
|
||||
int line_size_out = line_size_in;
|
||||
#endif
|
||||
int h = img_out.GetHeight();
|
||||
if (img_in.GetHeight() == h * text_scaling) {
|
||||
// no stretching
|
||||
for (int y = 0 ; y < h ; ++y) {
|
||||
for (int x = 0 ; x < line_size_in ; ++x) {
|
||||
int total = 0;
|
||||
for (int j = 0 ; j < text_scaling ; ++j) {
|
||||
total += in[x + line_size_in * (j + text_scaling * y)];
|
||||
}
|
||||
out[x + line_size_out * y] = total / text_scaling;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const int shift = 32-12-8; // => max size = 4096, max alpha = 255
|
||||
int h1 = img_in.GetHeight(), w = img_out.GetWidth();
|
||||
int out_fact = (h << shift) / h1; // how much to output for 256 input = 1 pixel
|
||||
int out_rest = (h << shift) % h1;
|
||||
int mul = 128 + min(256, 128*h1/(text_scaling*h));
|
||||
for (int x = 0 ; x < w ; ++x) {
|
||||
int in_rem = out_fact + out_rest;
|
||||
for (int y = 0 ; y < h ; ++y) {
|
||||
int out_rem = 1 << shift;
|
||||
int tot = 0;
|
||||
while (out_rem >= in_rem) {
|
||||
// eat a whole input pixel
|
||||
tot += *in * in_rem;
|
||||
out_rem -= in_rem;
|
||||
in_rem = out_fact;
|
||||
in += line_size_in;
|
||||
}
|
||||
if (out_rem > 0) {
|
||||
// eat a partial input pixel
|
||||
tot += *in * out_rem;
|
||||
in_rem -= out_rem;
|
||||
}
|
||||
// store
|
||||
*out = top(((tot >> shift) * mul) >> 8);
|
||||
out += line_size_out;
|
||||
}
|
||||
in = in - h1 * line_size_in + 1;
|
||||
out = out - h * line_size_out + 1;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] temp;
|
||||
}
|
||||
|
||||
// simple blur
|
||||
int blur_alpha_pixel(Byte* in, int x, int y, int width, int height) {
|
||||
return (2 * ( in[0]) + // center
|
||||
(x == 0 ? in[0] : in[-1]) + // left
|
||||
(y == 0 ? in[0] : in[-width]) + // up
|
||||
(x == width - 1 ? in[0] : in[1]) + // right
|
||||
(y == height - 1 ? in[0] : in[width]) // down
|
||||
) / 6;
|
||||
}
|
||||
|
||||
// TODO: move me?
|
||||
void blur_image_alpha(Image& img) {
|
||||
int width = img.GetWidth(), height = img.GetHeight();
|
||||
Byte* data = img.GetAlpha();
|
||||
for (int y = 0 ; y < height ; ++y) {
|
||||
for (int x = 0 ; x < width ; ++x) {
|
||||
*data = blur_alpha_pixel(data, x, y, width, height);
|
||||
++data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw text by first drawing it using a larger font and then downsampling it
|
||||
// optionally rotated by an angle
|
||||
void draw_resampled_text(DC& dc, const RealPoint& pos, const RealRect& rect, double stretch, Radians angle, Color color, const String& text, int blur_radius, int repeat) {
|
||||
void draw_resampled_text(DC& dc, const String& text, const RealPoint& pos, const RealRect& rect, Radians angle, Color color, int blur_radius, Color stroke_color, int stroke_radius, double stretch) {
|
||||
// transparent text can be ignored
|
||||
if (color.Alpha() == 0) return;
|
||||
// enlarge slightly; some fonts are larger then the GetTextExtent tells us (especially italic fonts)
|
||||
int w = static_cast<int>(rect.width) + 3 + 2 * blur_radius, h = static_cast<int>(rect.height) + 1 + 2 * blur_radius;
|
||||
int w = static_cast<int>(rect.width) + 4, h = static_cast<int>(rect.height) + 2;
|
||||
// determine sub-pixel position
|
||||
int xi = static_cast<int>(rect.x) - blur_radius / text_scaling,
|
||||
yi = static_cast<int>(rect.y) - blur_radius / text_scaling;
|
||||
int xi = static_cast<int>(rect.x),
|
||||
yi = static_cast<int>(rect.y);
|
||||
int xsub = static_cast<int>(text_scaling * (pos.x - xi)),
|
||||
ysub = static_cast<int>(text_scaling * (pos.y - yi));
|
||||
// draw text
|
||||
Bitmap buffer(w * text_scaling, h * text_scaling, 24); // should be initialized to black
|
||||
// draw text as mask (white text on black background)
|
||||
Bitmap buffer(w * text_scaling, h * text_scaling, 24);
|
||||
wxMemoryDC mdc;
|
||||
mdc.SelectObject(buffer);
|
||||
clearDC_black(mdc);
|
||||
// now draw the text
|
||||
mdc.SetFont(dc.GetFont());
|
||||
mdc.SetTextForeground(*wxWHITE);
|
||||
mdc.DrawRotatedText(text, xsub, ysub, rad_to_deg(angle));
|
||||
// get image
|
||||
mdc.SelectObject(wxNullBitmap);
|
||||
// step 2. sample down
|
||||
// downsample
|
||||
double ca = fabs(cos(angle)), sa = fabs(sin(angle));
|
||||
w += int(w * (stretch - 1) * ca); // GCC makes annoying conversion warnings if *= is used here.
|
||||
h += int(h * (stretch - 1) * sa);
|
||||
Image img_small(w, h, false);
|
||||
fill_image(img_small, color);
|
||||
downsample_to_alpha(buffer, img_small);
|
||||
// multiply alpha
|
||||
if (color.Alpha() != 255) {
|
||||
set_alpha(img_small, color.Alpha() / 255.);
|
||||
}
|
||||
// blur
|
||||
for (int i = 0 ; i < blur_radius ; ++i) {
|
||||
blur_image_alpha(img_small);
|
||||
}
|
||||
// step 3. draw to dc
|
||||
for (int i = 0 ; i < repeat ; ++i) {
|
||||
dc.DrawBitmap(img_small, xi, yi);
|
||||
Image img(w, h, false);
|
||||
downsample_to_alpha(buffer, img);
|
||||
// if there is no stroke effect, just add blur and draw
|
||||
if (stroke_radius == 0) {
|
||||
if (color.Alpha() != 255) {
|
||||
set_alpha(img, color.Alpha() / 255.);
|
||||
}
|
||||
if (blur_radius > 0) {
|
||||
Image s_img(w + 2*blur_radius, h + 2*blur_radius);
|
||||
set_alpha(s_img, 0);
|
||||
s_img.Paste(img, blur_radius, blur_radius, wxIMAGE_ALPHA_BLEND_COMPOSE);
|
||||
for (int i = 0 ; i < blur_radius ; ++i) {
|
||||
blur_image_alpha(s_img, 3);
|
||||
}
|
||||
fill_image(s_img, color);
|
||||
dc.DrawBitmap(s_img, xi-blur_radius, yi-blur_radius);
|
||||
}
|
||||
else {
|
||||
fill_image(img, color);
|
||||
dc.DrawBitmap(img, xi, yi);
|
||||
}
|
||||
}
|
||||
// otherwise add stroke effect
|
||||
else {
|
||||
int radius = blur_radius + stroke_radius;
|
||||
Image s_img(w + 2*radius, h + 2*radius);
|
||||
set_alpha(s_img, 0);
|
||||
s_img.Paste(img, radius, radius, wxIMAGE_ALPHA_BLEND_COMPOSE);
|
||||
for (int i = 0 ; i < stroke_radius ; ++i) {
|
||||
thicken_image_alpha(s_img, 1);
|
||||
}
|
||||
for (int i = 0 ; i < blur_radius ; ++i) {
|
||||
blur_image_alpha(s_img, 3);
|
||||
}
|
||||
if (stroke_color.Alpha() != 255) {
|
||||
set_alpha(s_img, stroke_color.Alpha() / 255.);
|
||||
}
|
||||
fill_image(s_img, stroke_color);
|
||||
if (stroke_color != color) {
|
||||
fill_image(img, color);
|
||||
if (color.Alpha() != 255) {
|
||||
set_alpha(img, color.Alpha() / 255.);
|
||||
}
|
||||
s_img.Paste(img, radius, radius, wxIMAGE_ALPHA_BLEND_COMPOSE);
|
||||
}
|
||||
dc.DrawBitmap(s_img, xi-radius, yi-radius);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ void FontTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, co
|
||||
margin = RealSize(1., 0);
|
||||
}
|
||||
dc.SetFont(*font, scale);
|
||||
dc.DrawTextWithShadow(text, *font, rect.position() + margin);
|
||||
dc.DrawTextWithShadowOrStroke(text, *font, rect.position() + margin);
|
||||
if (native_look) font->color = font_color;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
void SymbolTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end, bool native_look) const {
|
||||
if (!(what & DRAW_NORMAL)) return;
|
||||
if (font.font) {
|
||||
font.font->draw(dc, ctx, rect, font.size * scale, font.alignment, content.substr(start - this->start, end-start));
|
||||
font.font->draw(dc, ctx, rect, scale, font, content.substr(start - this->start, end-start));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ void draw_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& style,
|
||||
dc.SetFont(font, 1.0);
|
||||
RealSize size = dc.GetTextExtent(text);
|
||||
RealPoint text_pos = align_in_rect(text_align, size, dc.getInternalRect()) + RealSize(margin, 0);
|
||||
dc.DrawTextWithShadow(text, font, text_pos);
|
||||
dc.DrawTextWithShadowOrStroke(text, font, text_pos);
|
||||
if (viewer.nativeLook()) font.color = font_color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,15 +119,11 @@ Bitmap ImageValueViewer::imagePlaceholder(const Rotation& rot, UInt w, UInt h, c
|
||||
// only when in editor mode
|
||||
for (UInt size = 12 ; size > 2 ; --size) {
|
||||
dc.SetFont(wxFont(size, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
RealSize rs = dc.GetTextExtent(_("double click to load image"));
|
||||
RealSize rs = dc.GetTextExtent(_LABEL_("load image"));
|
||||
if (rs.width <= w - 10 && rs.height < h - 10) {
|
||||
// text fits
|
||||
RealPoint pos = align_in_rect(ALIGN_MIDDLE_CENTER, rs, rect);
|
||||
bool black_on_white = !default_image.Ok() || very_light(default_image);
|
||||
dc.SetTextForeground(black_on_white ? *wxWHITE : *wxBLACK);
|
||||
dc.DrawText(_("double click to load image"), pos, 2, 4); // blurred
|
||||
dc.SetTextForeground(black_on_white ? *wxBLACK : *wxWHITE);
|
||||
dc.DrawText(_("double click to load image"), pos);
|
||||
dc.DrawText(_LABEL_("load image"), pos, Color(255,255,255), 2, Color(0,0,0), 3); // stroked
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ void MultipleChoiceValueViewer::drawChoice(RotatedDC& dc, RealPoint& pos, const
|
||||
dc.SetFont(font,1);
|
||||
RealSize text_size = dc.GetTextExtent(text);
|
||||
RealPoint text_pos = align_in_rect(ALIGN_MIDDLE_LEFT, text_size, RealRect(pos.x + size.width + 1, pos.y, 0,size.height)) + margin;
|
||||
dc.DrawTextWithShadow(text, font, text_pos);
|
||||
dc.DrawTextWithShadowOrStroke(text, font, text_pos);
|
||||
size = add_horizontal(size, text_size);
|
||||
if (nativeLook()) font.color = font_color;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,6 @@ void PackageChoiceValueViewer::draw(RotatedDC& dc) {
|
||||
}
|
||||
dc.SetFont(font, 1.0);
|
||||
RealPoint pos = align_in_rect(ALIGN_MIDDLE_LEFT, RealSize(0, dc.GetCharHeight()), dc.getInternalRect()) + RealSize(17., 0) + margin;
|
||||
dc.DrawTextWithShadow(text, font, pos);
|
||||
dc.DrawTextWithShadowOrStroke(text, font, pos);
|
||||
if (nativeLook()) font.color = font_color;
|
||||
}
|
||||
|
||||
+14
-11
@@ -182,27 +182,27 @@ RotatedDC::RotatedDC(DC& dc, const Rotation& rotation, RenderQuality quality)
|
||||
|
||||
// ----------------------------------------------------------------------------- : RotatedDC : Drawing
|
||||
|
||||
void RotatedDC::DrawText(const String& text, const RealPoint& pos, int blur_radius, int boldness, double stretch_) {
|
||||
DrawText(text, pos, dc.GetTextForeground(), blur_radius, boldness, stretch_);
|
||||
void RotatedDC::DrawText(const String& text, const RealPoint& pos, int blur_radius, Color stroke_color, int stroke_radius, double stretch) {
|
||||
DrawText(text, pos, dc.GetTextForeground(), blur_radius, stroke_color, stroke_radius, stretch);
|
||||
}
|
||||
|
||||
void RotatedDC::DrawText(const String& text, const RealPoint& pos, Color color, int blur_radius, int boldness, double stretch_) {
|
||||
void RotatedDC::DrawText(const String& text, const RealPoint& pos, Color color, int blur_radius, Color stroke_color, int stroke_radius, double stretch) {
|
||||
if (text.empty()) return;
|
||||
if (color.Alpha() == 0) return;
|
||||
if (quality >= QUALITY_AA) {
|
||||
RealRect r(pos, GetTextExtent(text));
|
||||
RealRect r_ext = trRectToBB(r);
|
||||
RealPoint pos2 = tr(pos);
|
||||
stretch_ *= getStretch();
|
||||
if (fabs(stretch_ - 1) > 1e-6) {
|
||||
r.width *= stretch_;
|
||||
stretch *= getStretch();
|
||||
if (fabs(stretch - 1) > 1e-6) {
|
||||
r.width *= stretch;
|
||||
RealRect r_ext2 = trRectToBB(r);
|
||||
pos2.x += r_ext2.x - r_ext.x;
|
||||
pos2.y += r_ext2.y - r_ext.y;
|
||||
r_ext.x = r_ext2.x;
|
||||
r_ext.y = r_ext2.y;
|
||||
}
|
||||
draw_resampled_text(dc, pos2, r_ext, stretch_, angle, color, text, blur_radius, boldness);
|
||||
draw_resampled_text(dc, text, pos2, r_ext, angle, color, blur_radius, stroke_color, stroke_radius, stretch);
|
||||
} else if (quality >= QUALITY_SUB_PIXEL) {
|
||||
RealPoint p_ext = tr(pos)*text_scaling;
|
||||
double usx,usy;
|
||||
@@ -216,11 +216,14 @@ void RotatedDC::DrawText(const String& text, const RealPoint& pos, Color color,
|
||||
dc.SetTextForeground(color);
|
||||
dc.DrawRotatedText(text, (int) p_ext.x, (int) p_ext.y, rad_to_deg(angle));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RotatedDC::DrawTextWithShadow(const String& text, const Font& font, const RealPoint& pos, double scale, double stretch) {
|
||||
DrawText(text, pos + RealSize(font.shadow_displacement_x, font.shadow_displacement_y) * scale, font.shadow_color, font.shadow_blur * scale, 1, stretch);
|
||||
DrawText(text, pos, font.color, 0, 1, stretch);
|
||||
void RotatedDC::DrawTextWithShadowOrStroke(const String& text, const Font& font, const RealPoint& pos, double scale, double stretch) {
|
||||
double s_scale = scale * dc.GetFont().GetPointSize() / text_scaling / 15.;
|
||||
if (font.hasShadow() && !font.hasStroke()) {
|
||||
DrawText(text, pos + RealSize(font.shadow_displacement_x, font.shadow_displacement_y) * scale, font.shadow_color, lround(font.shadow_blur * s_scale), Color(0,0,0), 0, stretch);
|
||||
}
|
||||
DrawText(text, pos, font.color, lround(font.stroke_blur * s_scale), font.stroke_color, lround(font.stroke_radius * s_scale), stretch);
|
||||
}
|
||||
|
||||
void RotatedDC::DrawBitmap(const Bitmap& bitmap, const RealPoint& pos) {
|
||||
|
||||
@@ -157,10 +157,10 @@ public:
|
||||
// --------------------------------------------------- : Drawing
|
||||
|
||||
/// Draw text
|
||||
void DrawText (const String& text, const RealPoint& pos, int blur_radius = 0, int boldness = 1, double stretch = 1.0);
|
||||
void DrawText (const String& text, const RealPoint& pos, Color color, int blur_radius = 0, int boldness = 1, double stretch = 1.0);
|
||||
/// Draw text with the shadow and color settings of the given font
|
||||
void DrawTextWithShadow(const String& text, const Font& font, const RealPoint& pos, double scale = 1.0, double stretch = 1.0);
|
||||
void DrawText (const String& text, const RealPoint& pos, int blur_radius = 0, Color stroke_color = Color(0,0,0), int stroke_radius = 0, double stretch = 1.0);
|
||||
void DrawText (const String& text, const RealPoint& pos, Color color, int blur_radius = 0, Color stroke_color = Color(0,0,0), int stroke_radius = 0, double stretch = 1.0);
|
||||
/// Draw text with a shadow or stroke, and color settings of the given font
|
||||
void DrawTextWithShadowOrStroke(const String& text, const Font& font, const RealPoint& pos, double scale = 1.0, double stretch = 1.0);
|
||||
/// Draw abitmap, it must already be zoomed!
|
||||
void DrawBitmap(const Bitmap& bitmap, const RealPoint& pos);
|
||||
/// Draw an image using the given combining mode, the image must already be zoomed!
|
||||
|
||||
Reference in New Issue
Block a user