Change tabs to two spaces.

This commit is contained in:
Lymia Aluysia
2017-01-18 08:43:21 -06:00
parent d7f5f0dc3b
commit d2c635f739
329 changed files with 41307 additions and 41496 deletions
+165 -165
View File
@@ -14,16 +14,16 @@
// ----------------------------------------------------------------------------- : Evaluation
BezierCurve::BezierCurve(const Vector2D& p0, const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) {
// calculate coefficients
c = (p1 - p0) * 3.0;
// calculate coefficients
c = (p1 - p0) * 3.0;
b = (p2 - p1) * 3.0 - c;
a = (p3 - p0) - c - b;
d = p0;
}
BezierCurve::BezierCurve(const ControlPoint& p0, const ControlPoint& p3) {
// calculate coefficients
c = p0.delta_after * 3.0;
// calculate coefficients
c = p0.delta_after * 3.0;
b = (p3.pos + p3.delta_before - p0.pos - p0.delta_after) * 3.0 - c;
a = (p3.pos - p0.pos) - c - b;
d = p0.pos;
@@ -34,216 +34,216 @@ void deCasteljau(Vector2D a1, Vector2D a2, Vector2D a3, Vector2D a4,
Vector2D& b2, Vector2D& b3, Vector2D& b4c1,
Vector2D& c2, Vector2D& c3, double t)
{
b2 = a1 + (a2 - a1) * t;
Vector2D mid23 = a2 + (a3 - a2) * t;
c3 = a3 + (a4 - a3) * t;
b3 = b2 + (mid23 - b2) * t;
c2 = mid23 + (c3 - mid23) * t;
b4c1 = b3 + (c2 - b3) * t;
b2 = a1 + (a2 - a1) * t;
Vector2D mid23 = a2 + (a3 - a2) * t;
c3 = a3 + (a4 - a3) * t;
b3 = b2 + (mid23 - b2) * t;
c2 = mid23 + (c3 - mid23) * t;
b4c1 = b3 + (c2 - b3) * t;
}
void deCasteljau(ControlPoint& a, ControlPoint& b, double t, ControlPoint& mid) {
deCasteljau(a.pos, a.delta_after, b.delta_before, b.pos, t, mid);
deCasteljau(a.pos, a.delta_after, b.delta_before, b.pos, t, mid);
}
void deCasteljau(const Vector2D& a1, Vector2D& a21, Vector2D& a34, const Vector2D& a4, double t, ControlPoint& out) {
Vector2D half21 = a21 * t;
Vector2D half34 = a34 * (1-t);
Vector2D mid23 = (a1 + a21) * (1-t) + (a34 + a4) * t;
Vector2D mid23h21 = (a1 + half21) * (1-t) + mid23 * t;
Vector2D mid23h34 = (a4 + half34) * t + mid23 * (1-t);
out.pos = mid23h21 * (1-t) + mid23h34 * t;
out.delta_before = mid23h21 - out.pos;
out.delta_after = mid23h34 - out.pos;
a21 = half21;
a34 = half34;
Vector2D half21 = a21 * t;
Vector2D half34 = a34 * (1-t);
Vector2D mid23 = (a1 + a21) * (1-t) + (a34 + a4) * t;
Vector2D mid23h21 = (a1 + half21) * (1-t) + mid23 * t;
Vector2D mid23h34 = (a4 + half34) * t + mid23 * (1-t);
out.pos = mid23h21 * (1-t) + mid23h34 * t;
out.delta_before = mid23h21 - out.pos;
out.delta_after = mid23h34 - out.pos;
a21 = half21;
a34 = half34;
}
// ----------------------------------------------------------------------------- : Drawing
void curve_subdivide(const BezierCurve& c, const Vector2D& p0, const Vector2D& p1, double t0, double t1, const Vector2D& origin, const Matrix2D& m, vector<wxPoint>& out, UInt level) {
if (level <= 0) return;
double midtime = (t0+t1) * 0.5f;
Vector2D midpoint = c.pointAt(midtime);
Vector2D d0 = p0 - midpoint;
Vector2D d1 = midpoint - p1;
// Determine treshold for subdivision, greater angle -> subdivide
// greater size -> subdivide
double treshold = fabs( atan2(d0.x,d0.y) - atan2(d1.x,d1.y)) * (p0-p1).lengthSqr();
bool subdivide = treshold >= .0001;
// subdivide left
curve_subdivide(c, p0, midpoint, t0, midtime, origin, m, out, level - 1);
// add midpoint
if (subdivide) {
out.push_back(origin + midpoint * m);
}
// subdivide right
curve_subdivide(c, midpoint, p1, midtime, t1, origin, m, out, level - 1);
if (level <= 0) return;
double midtime = (t0+t1) * 0.5f;
Vector2D midpoint = c.pointAt(midtime);
Vector2D d0 = p0 - midpoint;
Vector2D d1 = midpoint - p1;
// Determine treshold for subdivision, greater angle -> subdivide
// greater size -> subdivide
double treshold = fabs( atan2(d0.x,d0.y) - atan2(d1.x,d1.y)) * (p0-p1).lengthSqr();
bool subdivide = treshold >= .0001;
// subdivide left
curve_subdivide(c, p0, midpoint, t0, midtime, origin, m, out, level - 1);
// add midpoint
if (subdivide) {
out.push_back(origin + midpoint * m);
}
// subdivide right
curve_subdivide(c, midpoint, p1, midtime, t1, origin, m, out, level - 1);
}
void segment_subdivide(const ControlPoint& p0, const ControlPoint& p1, const Vector2D& origin, const Matrix2D& m, vector<wxPoint>& out) {
assert(p0.segment_after == p1.segment_before);
// always the start
out.push_back(origin + p0.pos * m);
if (p0.segment_after == SEGMENT_CURVE) {
// need more points?
BezierCurve curve(p0,p1);
curve_subdivide(curve, p0.pos, p1.pos, 0, 1, origin, m, out, 5);
}
assert(p0.segment_after == p1.segment_before);
// always the start
out.push_back(origin + p0.pos * m);
if (p0.segment_after == SEGMENT_CURVE) {
// need more points?
BezierCurve curve(p0,p1);
curve_subdivide(curve, p0.pos, p1.pos, 0, 1, origin, m, out, 5);
}
}
// ----------------------------------------------------------------------------- : Bounds
Bounds segment_bounds(const Vector2D& origin, const Matrix2D& m, const ControlPoint& p1, const ControlPoint& p2) {
assert(p1.segment_after == p2.segment_before);
if (p1.segment_after == SEGMENT_LINE) {
return line_bounds (origin, m, p1.pos, p2.pos);
} else {
return bezier_bounds(origin, m, p1, p2);
}
assert(p1.segment_after == p2.segment_before);
if (p1.segment_after == SEGMENT_LINE) {
return line_bounds (origin, m, p1.pos, p2.pos);
} else {
return bezier_bounds(origin, m, p1, p2);
}
}
Bounds bezier_bounds(const Vector2D& origin, const Matrix2D& m, const ControlPoint& p1, const ControlPoint& p2) {
assert(p1.segment_after == SEGMENT_CURVE);
// Transform the control points
Vector2D r1 = origin + p1.pos * m;
Vector2D r2 = origin + (p1.pos + p1.delta_after) * m;
Vector2D r3 = origin + (p2.pos + p2.delta_before) * m;
Vector2D r4 = origin + p2.pos * m;
// First of all, the corners should be in the bounding box
Bounds bounds(r1);
bounds.update(r4);
// Solve the derivative of the bezier curve to find its extremes
// It's only a quadtratic equation :)
BezierCurve curve(r1,r2,r3,r4);
double roots[4];
UInt count;
count = solve_quadratic(3*curve.a.x, 2*curve.b.x, curve.c.x, roots);
count += solve_quadratic(3*curve.a.y, 2*curve.b.y, curve.c.y, roots + count);
// now check them for min/max
for (UInt i = 0 ; i < count ; ++i) {
double t = roots[i];
if (t >=0 && t <= 1) {
bounds.update(curve.pointAt(t));
}
}
return bounds;
assert(p1.segment_after == SEGMENT_CURVE);
// Transform the control points
Vector2D r1 = origin + p1.pos * m;
Vector2D r2 = origin + (p1.pos + p1.delta_after) * m;
Vector2D r3 = origin + (p2.pos + p2.delta_before) * m;
Vector2D r4 = origin + p2.pos * m;
// First of all, the corners should be in the bounding box
Bounds bounds(r1);
bounds.update(r4);
// Solve the derivative of the bezier curve to find its extremes
// It's only a quadtratic equation :)
BezierCurve curve(r1,r2,r3,r4);
double roots[4];
UInt count;
count = solve_quadratic(3*curve.a.x, 2*curve.b.x, curve.c.x, roots);
count += solve_quadratic(3*curve.a.y, 2*curve.b.y, curve.c.y, roots + count);
// now check them for min/max
for (UInt i = 0 ; i < count ; ++i) {
double t = roots[i];
if (t >=0 && t <= 1) {
bounds.update(curve.pointAt(t));
}
}
return bounds;
}
Bounds line_bounds(const Vector2D& origin, const Matrix2D& m, const Vector2D& p1, const Vector2D& p2) {
Bounds bounds(origin + p1 * m);
bounds.update(origin + p2 * m);
return bounds;
Bounds bounds(origin + p1 * m);
bounds.update(origin + p2 * m);
return bounds;
}
// ----------------------------------------------------------------------------- : Point tests
// Is a point inside a symbol shape?
bool point_in_shape(const Vector2D& pos, const SymbolShape& shape) {
// Step 1. compare bounding box of the part
if (!shape.bounds.contains(pos)) return false;
// Step 2. trace ray outward, count intersections
int count = 0;
size_t size = shape.points.size();
for(size_t i = 0 ; i < size ; ++i) {
ControlPointP p1 = shape.getPoint((int) i);
ControlPointP p2 = shape.getPoint((int) i + 1);
if (p1->segment_after == SEGMENT_LINE) {
count += intersect_line_ray (p1->pos, p2->pos, pos);
} else {
count += intersect_bezier_ray(*p1, *p2, pos);
}
}
return count & 1; // odd number of intersections
// Step 1. compare bounding box of the part
if (!shape.bounds.contains(pos)) return false;
// Step 2. trace ray outward, count intersections
int count = 0;
size_t size = shape.points.size();
for(size_t i = 0 ; i < size ; ++i) {
ControlPointP p1 = shape.getPoint((int) i);
ControlPointP p2 = shape.getPoint((int) i + 1);
if (p1->segment_after == SEGMENT_LINE) {
count += intersect_line_ray (p1->pos, p2->pos, pos);
} else {
count += intersect_bezier_ray(*p1, *p2, pos);
}
}
return count & 1; // odd number of intersections
}
// ----------------------------------------------------------------------------- : Finding points
bool pos_on_segment(const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut) {
if (p1.segment_after == SEGMENT_CURVE) {
return pos_on_bezier(pos, range, p1, p2, pOut, tOut);
} else {
return pos_on_line (pos, range, p1.pos, p2.pos, pOut, tOut);
}
if (p1.segment_after == SEGMENT_CURVE) {
return pos_on_bezier(pos, range, p1, p2, pOut, tOut);
} else {
return pos_on_line (pos, range, p1.pos, p2.pos, pOut, tOut);
}
}
bool pos_on_bezier(const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut) {
assert(p1.segment_after == SEGMENT_CURVE);
// Find intersections with the horizontal and vertical lines through p0
// theoretically we would need to check in all directions, but this covers enough
BezierCurve curve(p1, p2);
double roots[6];
UInt count;
count = solve_cubic(curve.a.y, curve.b.y, curve.c.y, curve.d.y - pos.y, roots);
count += solve_cubic(curve.a.x, curve.b.x, curve.c.x, curve.d.x - pos.x, roots + count); // append intersections
// take the best intersection point
double bestDistSqr = std::numeric_limits<double>::max(); //infinity
for(UInt i = 0 ; i < count ; ++i) {
double t = roots[i];
if (t >= 0 && t < 1) {
Vector2D pnt = curve.pointAt(t);
double distSqr = (pnt - pos).lengthSqr();
if (distSqr < bestDistSqr) {
bestDistSqr = distSqr;
pOut = pnt;
tOut = t;
}
}
}
return bestDistSqr <= range * range;
assert(p1.segment_after == SEGMENT_CURVE);
// Find intersections with the horizontal and vertical lines through p0
// theoretically we would need to check in all directions, but this covers enough
BezierCurve curve(p1, p2);
double roots[6];
UInt count;
count = solve_cubic(curve.a.y, curve.b.y, curve.c.y, curve.d.y - pos.y, roots);
count += solve_cubic(curve.a.x, curve.b.x, curve.c.x, curve.d.x - pos.x, roots + count); // append intersections
// take the best intersection point
double bestDistSqr = std::numeric_limits<double>::max(); //infinity
for(UInt i = 0 ; i < count ; ++i) {
double t = roots[i];
if (t >= 0 && t < 1) {
Vector2D pnt = curve.pointAt(t);
double distSqr = (pnt - pos).lengthSqr();
if (distSqr < bestDistSqr) {
bestDistSqr = distSqr;
pOut = pnt;
tOut = t;
}
}
}
return bestDistSqr <= range * range;
}
bool pos_on_line(const Vector2D& pos, double range, const Vector2D& p1, const Vector2D& p2, Vector2D& pOut, double& t) {
Vector2D p21 = p2 - p1;
double p21len = p21.lengthSqr();
if (p21len < 0.00001) return false; // line is too short
t = dot(p21, pos - p1) / p21len; // 'time' on line p1->p2
if (t < 0 || t > 1) return false; // outside segment
pOut = p1 + p21 * t; // point on line
Vector2D dist = pOut - pos; // distance to line
return dist.lengthSqr() <= range * range; // in range?
Vector2D p21 = p2 - p1;
double p21len = p21.lengthSqr();
if (p21len < 0.00001) return false; // line is too short
t = dot(p21, pos - p1) / p21len; // 'time' on line p1->p2
if (t < 0 || t > 1) return false; // outside segment
pOut = p1 + p21 * t; // point on line
Vector2D dist = pOut - pos; // distance to line
return dist.lengthSqr() <= range * range; // in range?
}
// ----------------------------------------------------------------------------- : Intersection
UInt intersect_bezier_ray(const ControlPoint& p1, const ControlPoint& p2, const Vector2D& pos) {
// Looking only at the y coordinate
// we can use the cubic formula to find roots, points where the horizontal line
// through pos intersects the (extended) curve
BezierCurve curve(p1,p2);
double roots[3];
UInt count = solve_cubic(curve.a.y, curve.b.y, curve.c.y, curve.d.y - pos.y, roots);
// now check if the solutions are left of pos.x
UInt solsInRange = 0;
for(UInt i = 0 ; i < count ; ++i) {
double t = roots[i];
if (t >= 0 && t < 1 && curve.pointAt(t).x < pos.x) {
solsInRange += 1;
}
}
return solsInRange;
// Looking only at the y coordinate
// we can use the cubic formula to find roots, points where the horizontal line
// through pos intersects the (extended) curve
BezierCurve curve(p1,p2);
double roots[3];
UInt count = solve_cubic(curve.a.y, curve.b.y, curve.c.y, curve.d.y - pos.y, roots);
// now check if the solutions are left of pos.x
UInt solsInRange = 0;
for(UInt i = 0 ; i < count ; ++i) {
double t = roots[i];
if (t >= 0 && t < 1 && curve.pointAt(t).x < pos.x) {
solsInRange += 1;
}
}
return solsInRange;
}
bool intersect_line_ray(const Vector2D& p1, const Vector2D& p2, const Vector2D& pos) {
// Vector2D intersection = p1 + t * (p2 - p1)
// intersection.y == pos.y
// == p1.y + t * (p2.y - p1.y)
// => t == (pos.y - p1.y) / (p2.y - p1.y)
// intersection.x == p1.x + t * (p2.x - p1.x)
// == p1.x + (pos.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y)
double dy = p2.y - p1.y;
if (fabs(dy) < 0.0000000001) {
// horizontal line
return (p1.x > pos.x || p2.x > pos.x) && // starts to the left of pos
fabs(p1.y - pos.y) < 0.0000000001; // same y as pos
} else {
double dx = p2.x - p1.x;
double t = (pos.y - p1.y) / dy;
if (t < 0.0 || t >= 1.0) return false;
double intersectX = p1.x + t * dx;
return intersectX <= pos.x; // intersection is left of pos
}
// Vector2D intersection = p1 + t * (p2 - p1)
// intersection.y == pos.y
// == p1.y + t * (p2.y - p1.y)
// => t == (pos.y - p1.y) / (p2.y - p1.y)
// intersection.x == p1.x + t * (p2.x - p1.x)
// == p1.x + (pos.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y)
double dy = p2.y - p1.y;
if (fabs(dy) < 0.0000000001) {
// horizontal line
return (p1.x > pos.x || p2.x > pos.x) && // starts to the left of pos
fabs(p1.y - pos.y) < 0.0000000001; // same y as pos
} else {
double dx = p2.x - p1.x;
double t = (pos.y - p1.y) / dy;
if (t < 0.0 || t >= 1.0) return false;
double intersectX = p1.x + t * dx;
return intersectX <= pos.x; // intersection is left of pos
}
}
+18 -18
View File
@@ -25,24 +25,24 @@ class Rotation;
/// A bezier curve for evaluation
class BezierCurve {
public:
/// coefficients of the equation (x,y) = at^3 + bt^2 + ct + d
Vector2D a, b, c, d;
/// Construct a bezier curve evaluator given the 4 handles
BezierCurve(const Vector2D& p0, const Vector2D& p1, const Vector2D& p2, const Vector2D& p3);
/// Construct a bezier curve evaluator given two ControlPoints at the ends
BezierCurve(const ControlPoint& p0, const ControlPoint& p3);
/// Return the point on this curve at time t in [0...1)
inline Vector2D pointAt(double t) const {
return d + (c + (b + a * t) * t) * t;
}
/// Return the tangent on this curve at time t in [0...1)
inline Vector2D tangentAt(double t) const {
return c + ((b * 2.) + (a * 3.) * t) * t;
}
/// coefficients of the equation (x,y) = at^3 + bt^2 + ct + d
Vector2D a, b, c, d;
/// Construct a bezier curve evaluator given the 4 handles
BezierCurve(const Vector2D& p0, const Vector2D& p1, const Vector2D& p2, const Vector2D& p3);
/// Construct a bezier curve evaluator given two ControlPoints at the ends
BezierCurve(const ControlPoint& p0, const ControlPoint& p3);
/// Return the point on this curve at time t in [0...1)
inline Vector2D pointAt(double t) const {
return d + (c + (b + a * t) * t) * t;
}
/// Return the tangent on this curve at time t in [0...1)
inline Vector2D tangentAt(double t) const {
return c + ((b * 2.) + (a * 3.) * t) * t;
}
};
/// Subdivide a curve from a to b, store the result in a control point
+84 -84
View File
@@ -16,104 +16,104 @@
template <typename T> inline T sqr(T x) { return x * x; }
void linear_blend(Image& img1, const Image& img2, double x1,double y1, double x2,double y2) {
int width = img1.GetWidth(), height = img1.GetHeight();
if (img2.GetWidth() != width || img2.GetHeight() != height) {
throw Error(_ERROR_("images used for blending must have the same size"));
}
const int fixed = 1<<16; // fixed point multiplier
// equation:
// x * xm + y * ym + d == fixed * f(x,y)
// xm and ym are multiples of delta x/y:
// xm = a w (x2-x1); ym = a h (y2-y1)
// known values
// f(x1*w, y1*h) = 0
// f(x2*w, y2*h) = 1
// filling in:
// x1 * w * a * w * (x2-x1) + y1 * h * a * h * (y2-y1) + d == 0
// x2 * w * a * w * (x2-x1) + y2 * h * a * h * (y2-y1) + d == fixed
// solving for a and d:
// (using dx = x1-x2, dy = y1-y2)
// a = fixed / (w^2 dx^2 + h^2 dy^2)
// d = a * (w^2 x1 dx + h^2 y1 dy)
if (x1==x2 && y1==y2) throw Error(_ERROR_("coordinates for blending overlap"));
double a = fixed / (sqr(width) * sqr(x1-x2) + sqr(height) * sqr(y1-y2));
int xm = to_int( (x2 - x1) * width * a );
int ym = to_int( (y2 - y1) * height * a );
int d = to_int( - (x1 * width * xm + y1 * height * ym) );
Byte *data1 = img1.GetData(), *data2 = img2.GetData();
// blend pixels
for (int y = 0 ; y < height ; ++y) {
for (int x = 0 ; x < width ; ++x) {
int mult = x * xm + y * ym + d;
if (mult < 0) mult = 0;
if (mult > fixed) mult = fixed;
data1[0] = data1[0] + mult * (data2[0] - data1[0]) / fixed;
data1[1] = data1[1] + mult * (data2[1] - data1[1]) / fixed;
data1[2] = data1[2] + mult * (data2[2] - data1[2]) / fixed;
data1 += 3;
data2 += 3;
}
}
int width = img1.GetWidth(), height = img1.GetHeight();
if (img2.GetWidth() != width || img2.GetHeight() != height) {
throw Error(_ERROR_("images used for blending must have the same size"));
}
const int fixed = 1<<16; // fixed point multiplier
// equation:
// x * xm + y * ym + d == fixed * f(x,y)
// xm and ym are multiples of delta x/y:
// xm = a w (x2-x1); ym = a h (y2-y1)
// known values
// f(x1*w, y1*h) = 0
// f(x2*w, y2*h) = 1
// filling in:
// x1 * w * a * w * (x2-x1) + y1 * h * a * h * (y2-y1) + d == 0
// x2 * w * a * w * (x2-x1) + y2 * h * a * h * (y2-y1) + d == fixed
// solving for a and d:
// (using dx = x1-x2, dy = y1-y2)
// a = fixed / (w^2 dx^2 + h^2 dy^2)
// d = a * (w^2 x1 dx + h^2 y1 dy)
if (x1==x2 && y1==y2) throw Error(_ERROR_("coordinates for blending overlap"));
double a = fixed / (sqr(width) * sqr(x1-x2) + sqr(height) * sqr(y1-y2));
int xm = to_int( (x2 - x1) * width * a );
int ym = to_int( (y2 - y1) * height * a );
int d = to_int( - (x1 * width * xm + y1 * height * ym) );
Byte *data1 = img1.GetData(), *data2 = img2.GetData();
// blend pixels
for (int y = 0 ; y < height ; ++y) {
for (int x = 0 ; x < width ; ++x) {
int mult = x * xm + y * ym + d;
if (mult < 0) mult = 0;
if (mult > fixed) mult = fixed;
data1[0] = data1[0] + mult * (data2[0] - data1[0]) / fixed;
data1[1] = data1[1] + mult * (data2[1] - data1[1]) / fixed;
data1[2] = data1[2] + mult * (data2[2] - data1[2]) / fixed;
data1 += 3;
data2 += 3;
}
}
}
// ----------------------------------------------------------------------------- : Mask Blend
void mask_blend(Image& img1, const Image& img2, const Image& mask) {
if (img2.GetWidth() != img1.GetWidth() || img2.GetHeight() != img1.GetHeight()
|| mask.GetWidth() != img1.GetWidth() || mask.GetHeight() != img1.GetHeight()) {
throw Error(_("Images used for blending must have the same size"));
}
UInt size = img1.GetWidth() * img1.GetHeight() * 3;
Byte *data1 = img1.GetData(), *data2 = img2.GetData(), *dataM = mask.GetData();
// for each subpixel...
for (UInt i = 0 ; i < size ; ++i) {
data1[i] = (data1[i] * dataM[i] + data2[i] * (255 - dataM[i])) / 255;
}
if (img2.GetWidth() != img1.GetWidth() || img2.GetHeight() != img1.GetHeight()
|| mask.GetWidth() != img1.GetWidth() || mask.GetHeight() != img1.GetHeight()) {
throw Error(_("Images used for blending must have the same size"));
}
UInt size = img1.GetWidth() * img1.GetHeight() * 3;
Byte *data1 = img1.GetData(), *data2 = img2.GetData(), *dataM = mask.GetData();
// for each subpixel...
for (UInt i = 0 ; i < size ; ++i) {
data1[i] = (data1[i] * dataM[i] + data2[i] * (255 - dataM[i])) / 255;
}
}
// ----------------------------------------------------------------------------- : Alpha
void set_alpha(Image& img, const Image& img_alpha) {
Image img_alpha_resampled = resample(img_alpha, img.GetWidth(), img.GetHeight());
if (!img.HasAlpha()) img.InitAlpha();
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;
}
Image img_alpha_resampled = resample(img_alpha, img.GetWidth(), img.GetHeight());
if (!img.HasAlpha()) img.InitAlpha();
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;
}
}
void set_alpha(Image& img, Byte* al, const wxSize& alpha_size) {
if (img.GetWidth() != alpha_size.GetWidth() || img.GetHeight() != alpha_size.GetHeight()) {
throw Error(_("Image must have same size as mask"));
}
if (!img.HasAlpha()) {
// copy
img.InitAlpha();
memcpy(img.GetAlpha(), al, img.GetWidth() * img.GetHeight());
} else{
// merge
Byte *im = img.GetAlpha();
size_t size = img.GetWidth() * img.GetHeight();
for (size_t i = 0 ; i < size ; ++i) {
im[i] = (im[i] * al[i]) / 255;
}
}
if (img.GetWidth() != alpha_size.GetWidth() || img.GetHeight() != alpha_size.GetHeight()) {
throw Error(_("Image must have same size as mask"));
}
if (!img.HasAlpha()) {
// copy
img.InitAlpha();
memcpy(img.GetAlpha(), al, img.GetWidth() * img.GetHeight());
} else{
// merge
Byte *im = img.GetAlpha();
size_t size = img.GetWidth() * img.GetHeight();
for (size_t i = 0 ; i < size ; ++i) {
im[i] = (im[i] * al[i]) / 255;
}
}
}
void set_alpha(Image& img, double alpha) {
Byte b_alpha = Byte(alpha * 255);
if (!img.HasAlpha()) {
img.InitAlpha();
memset(img.GetAlpha(), b_alpha, img.GetWidth() * img.GetHeight());
} else {
Byte *im = img.GetAlpha();
size_t size = img.GetWidth() * img.GetHeight();
for (size_t i = 0 ; i < size ; ++i) {
im[i] = (im[i] * b_alpha) / 255;
}
}
Byte b_alpha = Byte(alpha * 255);
if (!img.HasAlpha()) {
img.InitAlpha();
memset(img.GetAlpha(), b_alpha, img.GetWidth() * img.GetHeight());
} else {
Byte *im = img.GetAlpha();
size_t size = img.GetWidth() * img.GetHeight();
for (size_t i = 0 ; i < size ; ++i) {
im[i] = (im[i] * b_alpha) / 255;
}
}
}
+70 -70
View File
@@ -12,111 +12,111 @@
// ----------------------------------------------------------------------------- : Parsing etc.
template <> void Reader::handle(Color& col) {
col = parse_color(getValue());
if (!col.Ok()) col = *wxBLACK;
col = parse_color(getValue());
if (!col.Ok()) col = *wxBLACK;
}
template <> void Reader::handle(AColor& col) {
col = parse_acolor(getValue());
if (!col.Ok()) col = AColor(0,0,0,0);
col = parse_acolor(getValue());
if (!col.Ok()) col = AColor(0,0,0,0);
}
template <> void Writer::handle(const AColor& col) {
handle(format_acolor(col));
handle(format_acolor(col));
}
Color parse_color(const String& v) {
UInt r,g,b;
if (wxSscanf(v.c_str(),_("rgb(%u,%u,%u)"),&r,&g,&b)) {
return Color(r, g, b);
} else {
return Color(v);
}
UInt r,g,b;
if (wxSscanf(v.c_str(),_("rgb(%u,%u,%u)"),&r,&g,&b)) {
return Color(r, g, b);
} else {
return Color(v);
}
}
AColor parse_acolor(const String& v) {
UInt r,g,b,a;
if (wxSscanf(v.c_str(),_("rgb(%u,%u,%u)"),&r,&g,&b)) {
return AColor(r, g, b);
} else if (wxSscanf(v.c_str(),_("rgba(%u,%u,%u,%u)"),&r,&g,&b,&a)) {
return AColor(r, g, b, a);
} else if (v == _("transparent")) {
return AColor(0,0,0,0);
} else {
return Color(v);
}
UInt r,g,b,a;
if (wxSscanf(v.c_str(),_("rgb(%u,%u,%u)"),&r,&g,&b)) {
return AColor(r, g, b);
} else if (wxSscanf(v.c_str(),_("rgba(%u,%u,%u,%u)"),&r,&g,&b,&a)) {
return AColor(r, g, b, a);
} else if (v == _("transparent")) {
return AColor(0,0,0,0);
} else {
return Color(v);
}
}
String format_acolor(AColor col) {
if (col.alpha == 255) {
return String::Format(_("rgb(%u,%u,%u)"), col.Red(), col.Green(), col.Blue());
} else if (col.alpha == 0) {
return _("transparent");
} else {
return String::Format(_("rgba(%u,%u,%u,%u)"), col.Red(), col.Green(), col.Blue(), col.alpha);
}
if (col.alpha == 255) {
return String::Format(_("rgb(%u,%u,%u)"), col.Red(), col.Green(), col.Blue());
} else if (col.alpha == 0) {
return _("transparent");
} else {
return String::Format(_("rgba(%u,%u,%u,%u)"), col.Red(), col.Green(), col.Blue(), col.alpha);
}
}
// ----------------------------------------------------------------------------- : Color utility functions
Color lerp(const Color& a, const Color& b, double t) {
return Color(static_cast<int>( a.Red() + (b.Red() - a.Red() ) * t ),
static_cast<int>( a.Green() + (b.Green() - a.Green()) * t ),
static_cast<int>( a.Blue() + (b.Blue() - a.Blue() ) * t ));
return Color(static_cast<int>( a.Red() + (b.Red() - a.Red() ) * t ),
static_cast<int>( a.Green() + (b.Green() - a.Green()) * t ),
static_cast<int>( a.Blue() + (b.Blue() - a.Blue() ) * t ));
}
int hsl2rgbp(double t1, double t2, double t3) {
// adjust t3 to [0...1)
if (t3 < 0.0) t3 += 1;
else if (t3 > 1.0) t3 -= 1;
// determine color
if (6.0 * t3 < 1) return (int)(255 * (t1 + (t2-t1) * 6.0 * t3) );
if (2.0 * t3 < 1) return (int)(255 * (t2) );
if (3.0 * t3 < 2) return (int)(255 * (t1 + (t2-t1) * 6.0 * (2.0/3.0 - t3)) );
else return (int)(255 * (t1) );
// adjust t3 to [0...1)
if (t3 < 0.0) t3 += 1;
else if (t3 > 1.0) t3 -= 1;
// determine color
if (6.0 * t3 < 1) return (int)(255 * (t1 + (t2-t1) * 6.0 * t3) );
if (2.0 * t3 < 1) return (int)(255 * (t2) );
if (3.0 * t3 < 2) return (int)(255 * (t1 + (t2-t1) * 6.0 * (2.0/3.0 - t3)) );
else return (int)(255 * (t1) );
}
Color hsl2rgb(double h, double s, double l) {
double t2 = l < 0.5 ? l * (1.0 + s) :
l * (1.0 - s) + s;
double t1 = 2.0 * l - t2;
return Color(
hsl2rgbp(t1, t2, h + 1.0/3.0),
hsl2rgbp(t1, t2, h) ,
hsl2rgbp(t1, t2, h - 1.0/3.0)
);
double t2 = l < 0.5 ? l * (1.0 + s) :
l * (1.0 - s) + s;
double t1 = 2.0 * l - t2;
return Color(
hsl2rgbp(t1, t2, h + 1.0/3.0),
hsl2rgbp(t1, t2, h) ,
hsl2rgbp(t1, t2, h - 1.0/3.0)
);
}
Color darken(const Color& c) {
return Color(
c.Red() * 8 / 10,
c.Green() * 8 / 10,
c.Blue() * 8 / 10
);
return Color(
c.Red() * 8 / 10,
c.Green() * 8 / 10,
c.Blue() * 8 / 10
);
}
Color saturate(const Color& c, double amount) {
int r = c.Red(), g = c.Green(), b = c.Blue();
double l = (r + g + b) / 3;
return Color(
col(static_cast<int>( (r - amount * l) / (1 - amount) )),
col(static_cast<int>( (g - amount * l) / (1 - amount) )),
col(static_cast<int>( (b - amount * l) / (1 - amount) ))
);
int r = c.Red(), g = c.Green(), b = c.Blue();
double l = (r + g + b) / 3;
return Color(
col(static_cast<int>( (r - amount * l) / (1 - amount) )),
col(static_cast<int>( (g - amount * l) / (1 - amount) )),
col(static_cast<int>( (b - amount * l) / (1 - amount) ))
);
}
void fill_image(Image& image, RGB x) {
RGB* pos = (RGB*)image.GetData();
RGB* end = pos + image.GetWidth() * image.GetHeight();
if (x.r == x.g && x.r == x.b) {
// optimization: use memset
memset(pos, x.r, (end-pos) * sizeof(*pos));
} else {
// fill the image
while (pos != end) {
*pos++ = x;
}
}
RGB* pos = (RGB*)image.GetData();
RGB* end = pos + image.GetWidth() * image.GetHeight();
if (x.r == x.g && x.r == x.b) {
// optimization: use memset
memset(pos, x.r, (end-pos) * sizeof(*pos));
} else {
// fill the image
while (pos != end) {
*pos++ = x;
}
}
}
+37 -37
View File
@@ -21,15 +21,15 @@
/// Color with alpha channel
class AColor : public Color {
public:
Byte alpha; ///< The alpha value, in the range [0..255]
inline AColor() : alpha(0) {}
inline AColor(Byte r, Byte g, Byte b, Byte a = 255) : Color(r,g,b), alpha(a) {}
inline AColor(const Color& color, Byte a = 255) : Color(color), alpha(a) {}
inline bool operator == (const AColor& that) const {
return static_cast<const Color&>(*this) == static_cast<const Color&>(that) && alpha == that.alpha;
}
inline bool operator != (const AColor& that) const { return ! (*this == that); }
Byte alpha; ///< The alpha value, in the range [0..255]
inline AColor() : alpha(0) {}
inline AColor(Byte r, Byte g, Byte b, Byte a = 255) : Color(r,g,b), alpha(a) {}
inline AColor(const Color& color, Byte a = 255) : Color(color), alpha(a) {}
inline bool operator == (const AColor& that) const {
return static_cast<const Color&>(*this) == static_cast<const Color&>(that) && alpha == that.alpha;
}
inline bool operator != (const AColor& that) const { return ! (*this == that); }
};
// -----------------------------------------------------------------------------
@@ -41,42 +41,42 @@ class AColor : public Color {
// it is important to pack this into 3 bytes, so we can directly convert from wxImage data
#if defined(_MSC_VER)
#pragma pack(push, 1)
#define MAKE_PACKED
#pragma pack(push, 1)
#define MAKE_PACKED
#else
#define MAKE_PACKED __attribute__((__packed__))
#define MAKE_PACKED __attribute__((__packed__))
#endif
/// An RGB triplet, packed into 3 bytes
struct RGB {
Byte r,g,b;
RGB() {}
RGB(Byte x) : r(x), g(x), b(x) {}
RGB(Byte r, Byte g, Byte b) : r(r), g(g), b(b) {}
RGB(wxColour const& x) : r(x.Red()), g(x.Green()), b(x.Blue()) {}
inline int total() { return r+g+b; }
inline operator wxColour() const {
return wxColour(r,g,b);
}
inline bool operator == (RGB const& that) const {
return r == that.r && g == that.g && b == that.b;
}
inline bool operator < (RGB const& that) const {
if (r < that.r) return true;
if (r > that.r) return false;
if (g < that.g) return true;
if (g > that.g) return false;
return b < that.b;
}
Byte r,g,b;
RGB() {}
RGB(Byte x) : r(x), g(x), b(x) {}
RGB(Byte r, Byte g, Byte b) : r(r), g(g), b(b) {}
RGB(wxColour const& x) : r(x.Red()), g(x.Green()), b(x.Blue()) {}
inline int total() { return r+g+b; }
inline operator wxColour() const {
return wxColour(r,g,b);
}
inline bool operator == (RGB const& that) const {
return r == that.r && g == that.g && b == that.b;
}
inline bool operator < (RGB const& that) const {
if (r < that.r) return true;
if (r > that.r) return false;
if (g < that.g) return true;
if (g > that.g) return false;
return b < that.b;
}
} MAKE_PACKED;
#ifdef _MSC_VER
#pragma pack(pop)
#pragma pack(pop)
#endif
// ----------------------------------------------------------------------------- : Parsing
+112 -112
View File
@@ -16,74 +16,74 @@ using namespace std;
// ----------------------------------------------------------------------------- : Reflection for combining modes
IMPLEMENT_REFLECTION_ENUM(ImageCombine) {
VALUE_N("normal", COMBINE_NORMAL);
VALUE_N("add", COMBINE_ADD);
VALUE_N("subtract", COMBINE_SUBTRACT);
VALUE_N("stamp", COMBINE_STAMP);
VALUE_N("difference", COMBINE_DIFFERENCE);
VALUE_N("negation", COMBINE_NEGATION);
VALUE_N("multiply", COMBINE_MULTIPLY);
VALUE_N("darken", COMBINE_DARKEN);
VALUE_N("lighten", COMBINE_LIGHTEN);
VALUE_N("color dodge", COMBINE_COLOR_DODGE);
VALUE_N("color burn", COMBINE_COLOR_BURN);
VALUE_N("screen", COMBINE_SCREEN);
VALUE_N("overlay", COMBINE_OVERLAY);
VALUE_N("hard light", COMBINE_HARD_LIGHT);
VALUE_N("soft light", COMBINE_SOFT_LIGHT);
VALUE_N("reflect", COMBINE_REFLECT);
VALUE_N("glow", COMBINE_GLOW);
VALUE_N("freeze", COMBINE_FREEZE);
VALUE_N("heat", COMBINE_HEAT);
VALUE_N("and", COMBINE_AND);
VALUE_N("or", COMBINE_OR);
VALUE_N("xor", COMBINE_XOR);
VALUE_N("shadow", COMBINE_SHADOW);
VALUE_N("symmetric overlay",COMBINE_SYMMETRIC_OVERLAY);
VALUE_N("normal", COMBINE_NORMAL);
VALUE_N("add", COMBINE_ADD);
VALUE_N("subtract", COMBINE_SUBTRACT);
VALUE_N("stamp", COMBINE_STAMP);
VALUE_N("difference", COMBINE_DIFFERENCE);
VALUE_N("negation", COMBINE_NEGATION);
VALUE_N("multiply", COMBINE_MULTIPLY);
VALUE_N("darken", COMBINE_DARKEN);
VALUE_N("lighten", COMBINE_LIGHTEN);
VALUE_N("color dodge", COMBINE_COLOR_DODGE);
VALUE_N("color burn", COMBINE_COLOR_BURN);
VALUE_N("screen", COMBINE_SCREEN);
VALUE_N("overlay", COMBINE_OVERLAY);
VALUE_N("hard light", COMBINE_HARD_LIGHT);
VALUE_N("soft light", COMBINE_SOFT_LIGHT);
VALUE_N("reflect", COMBINE_REFLECT);
VALUE_N("glow", COMBINE_GLOW);
VALUE_N("freeze", COMBINE_FREEZE);
VALUE_N("heat", COMBINE_HEAT);
VALUE_N("and", COMBINE_AND);
VALUE_N("or", COMBINE_OR);
VALUE_N("xor", COMBINE_XOR);
VALUE_N("shadow", COMBINE_SHADOW);
VALUE_N("symmetric overlay",COMBINE_SYMMETRIC_OVERLAY);
}
// ----------------------------------------------------------------------------- : Combining functions
// Functor for combining functions for a given combining type
template <ImageCombine combine> struct Combine {
static inline int f(int a, int b);
static inline int f(int a, int b);
};
// Give a combining function for enum value 'combine'
#define COMBINE_FUN(combine,fun) \
template <> int Combine<combine>::f(int a, int b) { return fun; }
#define COMBINE_FUN(combine,fun) \
template <> int Combine<combine>::f(int a, int b) { return fun; }
// Based on
// http://www.pegtop.net/delphi/articles/blendmodes/
COMBINE_FUN(COMBINE_NORMAL, b )
COMBINE_FUN(COMBINE_ADD, top(a + b) )
COMBINE_FUN(COMBINE_SUBTRACT, bot(a - b) )
COMBINE_FUN(COMBINE_STAMP, col(a - 2 * b + 256) )
COMBINE_FUN(COMBINE_DIFFERENCE, abs(a - b) )
COMBINE_FUN(COMBINE_NEGATION, 255 - abs(255 - a - b) )
COMBINE_FUN(COMBINE_MULTIPLY, (a * b) / 255 )
COMBINE_FUN(COMBINE_DARKEN, min(a, b) )
COMBINE_FUN(COMBINE_LIGHTEN, max(a, b) )
COMBINE_FUN(COMBINE_COLOR_DODGE,b == 255 ? 255 : top(a * 255 / (255 - b)) )
COMBINE_FUN(COMBINE_COLOR_BURN, b == 0 ? 0 : bot(255 - (255-a) * 255 / b) )
COMBINE_FUN(COMBINE_SCREEN, 255 - (((255 - a) * (255 - b)) / 255) )
COMBINE_FUN(COMBINE_OVERLAY, a < 128
? (a * b) >> 7
: 255 - (((255 - a) * (255 - b)) >> 7) )
COMBINE_FUN(COMBINE_HARD_LIGHT, b < 128
? (a * b) >> 7
: 255 - (((255 - a) * (255 - b)) >> 7) )
COMBINE_FUN(COMBINE_SOFT_LIGHT, b)
COMBINE_FUN(COMBINE_REFLECT, b == 255 ? 255 : top(a * a / (255 - b)) )
COMBINE_FUN(COMBINE_GLOW, a == 255 ? 255 : top(b * b / (255 - a)) )
COMBINE_FUN(COMBINE_FREEZE, b == 0 ? 0 : bot(255 - (255 - a) * (255 - a) / b) )
COMBINE_FUN(COMBINE_HEAT, a == 0 ? 0 : bot(255 - (255 - b) * (255 - b) / a) )
COMBINE_FUN(COMBINE_AND, a & b )
COMBINE_FUN(COMBINE_OR, a | b )
COMBINE_FUN(COMBINE_XOR, a ^ b )
COMBINE_FUN(COMBINE_SHADOW, (b * a * a) / (255 * 255) )
COMBINE_FUN(COMBINE_SYMMETRIC_OVERLAY, (Combine<COMBINE_OVERLAY>::f(a,b) + Combine<COMBINE_OVERLAY>::f(b,a)) / 2 )
COMBINE_FUN(COMBINE_NORMAL, b )
COMBINE_FUN(COMBINE_ADD, top(a + b) )
COMBINE_FUN(COMBINE_SUBTRACT, bot(a - b) )
COMBINE_FUN(COMBINE_STAMP, col(a - 2 * b + 256) )
COMBINE_FUN(COMBINE_DIFFERENCE, abs(a - b) )
COMBINE_FUN(COMBINE_NEGATION, 255 - abs(255 - a - b) )
COMBINE_FUN(COMBINE_MULTIPLY, (a * b) / 255 )
COMBINE_FUN(COMBINE_DARKEN, min(a, b) )
COMBINE_FUN(COMBINE_LIGHTEN, max(a, b) )
COMBINE_FUN(COMBINE_COLOR_DODGE,b == 255 ? 255 : top(a * 255 / (255 - b)) )
COMBINE_FUN(COMBINE_COLOR_BURN, b == 0 ? 0 : bot(255 - (255-a) * 255 / b) )
COMBINE_FUN(COMBINE_SCREEN, 255 - (((255 - a) * (255 - b)) / 255) )
COMBINE_FUN(COMBINE_OVERLAY, a < 128
? (a * b) >> 7
: 255 - (((255 - a) * (255 - b)) >> 7) )
COMBINE_FUN(COMBINE_HARD_LIGHT, b < 128
? (a * b) >> 7
: 255 - (((255 - a) * (255 - b)) >> 7) )
COMBINE_FUN(COMBINE_SOFT_LIGHT, b)
COMBINE_FUN(COMBINE_REFLECT, b == 255 ? 255 : top(a * a / (255 - b)) )
COMBINE_FUN(COMBINE_GLOW, a == 255 ? 255 : top(b * b / (255 - a)) )
COMBINE_FUN(COMBINE_FREEZE, b == 0 ? 0 : bot(255 - (255 - a) * (255 - a) / b) )
COMBINE_FUN(COMBINE_HEAT, a == 0 ? 0 : bot(255 - (255 - b) * (255 - b) / a) )
COMBINE_FUN(COMBINE_AND, a & b )
COMBINE_FUN(COMBINE_OR, a | b )
COMBINE_FUN(COMBINE_XOR, a ^ b )
COMBINE_FUN(COMBINE_SHADOW, (b * a * a) / (255 * 255) )
COMBINE_FUN(COMBINE_SYMMETRIC_OVERLAY, (Combine<COMBINE_OVERLAY>::f(a,b) + Combine<COMBINE_OVERLAY>::f(b,a)) / 2 )
// ----------------------------------------------------------------------------- : Combining
@@ -91,67 +91,67 @@ COMBINE_FUN(COMBINE_SYMMETRIC_OVERLAY, (Combine<COMBINE_OVERLAY>::f(a,b) + Combi
/// The results are stored in the image A.
template <ImageCombine combine>
void combine_image_do(Image& a, Image b) {
UInt size = a.GetWidth() * a.GetHeight() * 3;
Byte *dataA = a.GetData(), *dataB = b.GetData();
// for each pixel: apply function
for (UInt i = 0 ; i < size ; ++i) {
dataA[i] = Combine<combine>::f(dataA[i], dataB[i]);
}
UInt size = a.GetWidth() * a.GetHeight() * 3;
Byte *dataA = a.GetData(), *dataB = b.GetData();
// for each pixel: apply function
for (UInt i = 0 ; i < size ; ++i) {
dataA[i] = Combine<combine>::f(dataA[i], dataB[i]);
}
}
void combine_image(Image& a, const Image& b, ImageCombine combine) {
// Images must have same size
assert(a.GetWidth() == b.GetWidth());
assert(a.GetHeight() == b.GetHeight());
// Copy alpha channel?
if (b.HasAlpha()) {
if (!a.HasAlpha()) a.InitAlpha();
memcpy(a.GetAlpha(), b.GetAlpha(), a.GetWidth() * a.GetHeight());
}
// Combine image data, by dispatching to combineImageDo
switch(combine) {
#define DISPATCH(comb) case comb: combine_image_do<comb>(a,b); return
case COMBINE_DEFAULT:
case COMBINE_NORMAL: a = b; return; // no need to do a per pixel operation
DISPATCH(COMBINE_ADD);
DISPATCH(COMBINE_SUBTRACT);
DISPATCH(COMBINE_STAMP);
DISPATCH(COMBINE_DIFFERENCE);
DISPATCH(COMBINE_NEGATION);
DISPATCH(COMBINE_MULTIPLY);
DISPATCH(COMBINE_DARKEN);
DISPATCH(COMBINE_LIGHTEN);
DISPATCH(COMBINE_COLOR_DODGE);
DISPATCH(COMBINE_COLOR_BURN);
DISPATCH(COMBINE_SCREEN);
DISPATCH(COMBINE_OVERLAY);
DISPATCH(COMBINE_HARD_LIGHT);
DISPATCH(COMBINE_SOFT_LIGHT);
DISPATCH(COMBINE_REFLECT);
DISPATCH(COMBINE_GLOW);
DISPATCH(COMBINE_FREEZE);
DISPATCH(COMBINE_HEAT);
DISPATCH(COMBINE_AND);
DISPATCH(COMBINE_OR);
DISPATCH(COMBINE_XOR);
DISPATCH(COMBINE_SHADOW);
DISPATCH(COMBINE_SYMMETRIC_OVERLAY);
}
// Images must have same size
assert(a.GetWidth() == b.GetWidth());
assert(a.GetHeight() == b.GetHeight());
// Copy alpha channel?
if (b.HasAlpha()) {
if (!a.HasAlpha()) a.InitAlpha();
memcpy(a.GetAlpha(), b.GetAlpha(), a.GetWidth() * a.GetHeight());
}
// Combine image data, by dispatching to combineImageDo
switch(combine) {
#define DISPATCH(comb) case comb: combine_image_do<comb>(a,b); return
case COMBINE_DEFAULT:
case COMBINE_NORMAL: a = b; return; // no need to do a per pixel operation
DISPATCH(COMBINE_ADD);
DISPATCH(COMBINE_SUBTRACT);
DISPATCH(COMBINE_STAMP);
DISPATCH(COMBINE_DIFFERENCE);
DISPATCH(COMBINE_NEGATION);
DISPATCH(COMBINE_MULTIPLY);
DISPATCH(COMBINE_DARKEN);
DISPATCH(COMBINE_LIGHTEN);
DISPATCH(COMBINE_COLOR_DODGE);
DISPATCH(COMBINE_COLOR_BURN);
DISPATCH(COMBINE_SCREEN);
DISPATCH(COMBINE_OVERLAY);
DISPATCH(COMBINE_HARD_LIGHT);
DISPATCH(COMBINE_SOFT_LIGHT);
DISPATCH(COMBINE_REFLECT);
DISPATCH(COMBINE_GLOW);
DISPATCH(COMBINE_FREEZE);
DISPATCH(COMBINE_HEAT);
DISPATCH(COMBINE_AND);
DISPATCH(COMBINE_OR);
DISPATCH(COMBINE_XOR);
DISPATCH(COMBINE_SHADOW);
DISPATCH(COMBINE_SYMMETRIC_OVERLAY);
}
}
void draw_combine_image(DC& dc, UInt x, UInt y, const Image& img, ImageCombine combine) {
if (combine <= COMBINE_NORMAL) {
dc.DrawBitmap(img, x, y);
} else {
// Capture the current image in the target rectangle
Bitmap sourceB(img.GetWidth(), img.GetHeight());
wxMemoryDC sourceDC;
sourceDC.SelectObject(sourceB);
sourceDC.Blit(0, 0, img.GetWidth(), img.GetHeight(), &dc, x, y);
sourceDC.SelectObject(wxNullBitmap);
Image source = sourceB.ConvertToImage();
// Combine and draw
combine_image(source, img, combine);
dc.DrawBitmap(source, x, y);
}
if (combine <= COMBINE_NORMAL) {
dc.DrawBitmap(img, x, y);
} else {
// Capture the current image in the target rectangle
Bitmap sourceB(img.GetWidth(), img.GetHeight());
wxMemoryDC sourceDC;
sourceDC.SelectObject(sourceB);
sourceDC.Blit(0, 0, img.GetWidth(), img.GetHeight(), &dc, x, y);
sourceDC.SelectObject(wxNullBitmap);
Image source = sourceB.ConvertToImage();
// Combine and draw
combine_image(source, img, combine);
dc.DrawBitmap(source, x, y);
}
}
+320 -320
View File
@@ -20,299 +20,299 @@
ScriptType GeneratedImage::type() const { return SCRIPT_IMAGE; }
String GeneratedImage::typeName() const { return _TYPE_("image"); }
GeneratedImageP GeneratedImage::toImage(const ScriptValueP& thisP) const {
return static_pointer_cast<GeneratedImage>(thisP);
return static_pointer_cast<GeneratedImage>(thisP);
}
Image GeneratedImage::generateConform(const Options& options) const {
return conform_image(generate(options),options);
return conform_image(generate(options),options);
}
Image conform_image(const Image& img, const GeneratedImage::Options& options) {
Image image = img;
// resize?
int iw = image.GetWidth(), ih = image.GetHeight();
if ((iw == options.width && ih == options.height) || (options.width == 0 && options.height == 0)) {
// zoom?
if (options.zoom != 1.0) {
image = resample(image, int(iw * options.zoom), int(ih * options.zoom));
} else {
// already the right size
}
} else if (options.height == 0) {
// width is given, determine height
int h = options.width * ih / iw;
image = resample(image, options.width, h);
} else if (options.width == 0) {
// height is given, determine width
int w = options.height * iw / ih;
image = resample(image, w, options.height);
} else if (options.preserve_aspect == ASPECT_FIT) {
// determine actual size of resulting image
int w, h;
if (iw * options.height > ih * options.width) { // too much height requested
w = options.width;
h = options.width * ih / iw;
} else {
w = options.height * iw / ih;
h = options.height;
}
image = resample(image, w, h);
} else {
if (options.preserve_aspect == ASPECT_BORDER && (options.width < options.height * 3) && (options.height < options.width * 3)) {
// preserve the aspect ratio if there is not too much difference
image = resample_preserve_aspect(image, options.width, options.height);
} else {
image = resample(image, options.width, options.height);
}
}
// saturate?
if (options.saturate) {
saturate(image, .1);
}
options.width = image.GetWidth();
options.height = image.GetHeight();
// rotate?
if (options.angle != 0) {
image = rotate_image(image, options.angle);
}
return image;
Image image = img;
// resize?
int iw = image.GetWidth(), ih = image.GetHeight();
if ((iw == options.width && ih == options.height) || (options.width == 0 && options.height == 0)) {
// zoom?
if (options.zoom != 1.0) {
image = resample(image, int(iw * options.zoom), int(ih * options.zoom));
} else {
// already the right size
}
} else if (options.height == 0) {
// width is given, determine height
int h = options.width * ih / iw;
image = resample(image, options.width, h);
} else if (options.width == 0) {
// height is given, determine width
int w = options.height * iw / ih;
image = resample(image, w, options.height);
} else if (options.preserve_aspect == ASPECT_FIT) {
// determine actual size of resulting image
int w, h;
if (iw * options.height > ih * options.width) { // too much height requested
w = options.width;
h = options.width * ih / iw;
} else {
w = options.height * iw / ih;
h = options.height;
}
image = resample(image, w, h);
} else {
if (options.preserve_aspect == ASPECT_BORDER && (options.width < options.height * 3) && (options.height < options.width * 3)) {
// preserve the aspect ratio if there is not too much difference
image = resample_preserve_aspect(image, options.width, options.height);
} else {
image = resample(image, options.width, options.height);
}
}
// saturate?
if (options.saturate) {
saturate(image, .1);
}
options.width = image.GetWidth();
options.height = image.GetHeight();
// rotate?
if (options.angle != 0) {
image = rotate_image(image, options.angle);
}
return image;
}
// ----------------------------------------------------------------------------- : BlankImage
Image BlankImage::generate(const Options& opt) const {
int w = max(1, opt.width >= 0 ? opt.width : opt.height);
int h = max(1, opt.height >= 0 ? opt.height : opt.width);
Image img(w, h);
img.InitAlpha();
memset(img.GetAlpha(), 0, w * h);
return img;
int w = max(1, opt.width >= 0 ? opt.width : opt.height);
int h = max(1, opt.height >= 0 ? opt.height : opt.width);
Image img(w, h);
img.InitAlpha();
memset(img.GetAlpha(), 0, w * h);
return img;
}
bool BlankImage::operator == (const GeneratedImage& that) const {
const BlankImage* that2 = dynamic_cast<const BlankImage*>(&that);
return that2;
const BlankImage* that2 = dynamic_cast<const BlankImage*>(&that);
return that2;
}
// ----------------------------------------------------------------------------- : LinearBlendImage
Image LinearBlendImage::generate(const Options& opt) const {
Image img = image1->generate(opt);
linear_blend(img, image2->generate(opt), x1, y1, x2, y2);
return img;
Image img = image1->generate(opt);
linear_blend(img, image2->generate(opt), x1, y1, x2, y2);
return img;
}
ImageCombine LinearBlendImage::combine() const {
return image1->combine();
return image1->combine();
}
bool LinearBlendImage::operator == (const GeneratedImage& that) const {
const LinearBlendImage* that2 = dynamic_cast<const LinearBlendImage*>(&that);
return that2 && *image1 == *that2->image1
&& *image2 == *that2->image2
&& x1 == that2->x1 && y1 == that2->y1
&& x2 == that2->x2 && y2 == that2->y2;
const LinearBlendImage* that2 = dynamic_cast<const LinearBlendImage*>(&that);
return that2 && *image1 == *that2->image1
&& *image2 == *that2->image2
&& x1 == that2->x1 && y1 == that2->y1
&& x2 == that2->x2 && y2 == that2->y2;
}
// ----------------------------------------------------------------------------- : MaskedBlendImage
Image MaskedBlendImage::generate(const Options& opt) const {
Image img = light->generate(opt);
mask_blend(img, dark->generate(opt), mask->generate(opt));
return img;
Image img = light->generate(opt);
mask_blend(img, dark->generate(opt), mask->generate(opt));
return img;
}
ImageCombine MaskedBlendImage::combine() const {
return light->combine();
return light->combine();
}
bool MaskedBlendImage::operator == (const GeneratedImage& that) const {
const MaskedBlendImage* that2 = dynamic_cast<const MaskedBlendImage*>(&that);
return that2 && *light == *that2->light
&& *dark == *that2->dark
&& *mask == *that2->mask;
const MaskedBlendImage* that2 = dynamic_cast<const MaskedBlendImage*>(&that);
return that2 && *light == *that2->light
&& *dark == *that2->dark
&& *mask == *that2->mask;
}
// ----------------------------------------------------------------------------- : CombineBlendImage
Image CombineBlendImage::generate(const Options& opt) const {
Image img = image1->generate(opt);
combine_image(img, image2->generate(opt), image_combine);
return img;
Image img = image1->generate(opt);
combine_image(img, image2->generate(opt), image_combine);
return img;
}
ImageCombine CombineBlendImage::combine() const {
return image1->combine();
return image1->combine();
}
bool CombineBlendImage::operator == (const GeneratedImage& that) const {
const CombineBlendImage* that2 = dynamic_cast<const CombineBlendImage*>(&that);
return that2 && *image1 == *that2->image1
&& *image2 == *that2->image2
&& image_combine == that2->image_combine;
const CombineBlendImage* that2 = dynamic_cast<const CombineBlendImage*>(&that);
return that2 && *image1 == *that2->image1
&& *image2 == *that2->image2
&& image_combine == that2->image_combine;
}
// ----------------------------------------------------------------------------- : SetMaskImage
Image SetMaskImage::generate(const Options& opt) const {
Image img = image->generate(opt);
set_alpha(img, mask->generate(opt));
return img;
Image img = image->generate(opt);
set_alpha(img, mask->generate(opt));
return img;
}
bool SetMaskImage::operator == (const GeneratedImage& that) const {
const SetMaskImage* that2 = dynamic_cast<const SetMaskImage*>(&that);
return that2 && *image == *that2->image
&& *mask == *that2->mask;
const SetMaskImage* that2 = dynamic_cast<const SetMaskImage*>(&that);
return that2 && *image == *that2->image
&& *mask == *that2->mask;
}
Image SetAlphaImage::generate(const Options& opt) const {
Image img = image->generate(opt);
set_alpha(img, alpha);
return img;
Image img = image->generate(opt);
set_alpha(img, alpha);
return img;
}
bool SetAlphaImage::operator == (const GeneratedImage& that) const {
const SetAlphaImage* that2 = dynamic_cast<const SetAlphaImage*>(&that);
return that2 && *image == *that2->image
&& alpha == that2->alpha;
const SetAlphaImage* that2 = dynamic_cast<const SetAlphaImage*>(&that);
return that2 && *image == *that2->image
&& alpha == that2->alpha;
}
// ----------------------------------------------------------------------------- : SetCombineImage
Image SetCombineImage::generate(const Options& opt) const {
return image->generate(opt);
return image->generate(opt);
}
ImageCombine SetCombineImage::combine() const {
return image_combine;
return image_combine;
}
bool SetCombineImage::operator == (const GeneratedImage& that) const {
const SetCombineImage* that2 = dynamic_cast<const SetCombineImage*>(&that);
return that2 && *image == *that2->image
&& image_combine == that2->image_combine;
const SetCombineImage* that2 = dynamic_cast<const SetCombineImage*>(&that);
return that2 && *image == *that2->image
&& image_combine == that2->image_combine;
}
// ----------------------------------------------------------------------------- : SaturateImage
Image SaturateImage::generate(const Options& opt) const {
Image img = image->generate(opt);
saturate(img, amount);
return img;
Image img = image->generate(opt);
saturate(img, amount);
return img;
}
bool SaturateImage::operator == (const GeneratedImage& that) const {
const SaturateImage* that2 = dynamic_cast<const SaturateImage*>(&that);
return that2 && *image == *that2->image
&& amount == that2->amount;
const SaturateImage* that2 = dynamic_cast<const SaturateImage*>(&that);
return that2 && *image == *that2->image
&& amount == that2->amount;
}
// ----------------------------------------------------------------------------- : InvertImage
Image InvertImage::generate(const Options& opt) const {
Image img = image->generate(opt);
invert(img);
return img;
Image img = image->generate(opt);
invert(img);
return img;
}
bool InvertImage::operator == (const GeneratedImage& that) const {
const InvertImage* that2 = dynamic_cast<const InvertImage*>(&that);
return that2 && *image == *that2->image;
const InvertImage* that2 = dynamic_cast<const InvertImage*>(&that);
return that2 && *image == *that2->image;
}
// ----------------------------------------------------------------------------- : RecolorImage
Image RecolorImage::generate(const Options& opt) const {
Image img = image->generate(opt);
recolor(img, color);
return img;
Image img = image->generate(opt);
recolor(img, color);
return img;
}
bool RecolorImage::operator == (const GeneratedImage& that) const {
const RecolorImage* that2 = dynamic_cast<const RecolorImage*>(&that);
return that2 && *image == *that2->image
&& color == that2->color;
const RecolorImage* that2 = dynamic_cast<const RecolorImage*>(&that);
return that2 && *image == *that2->image
&& color == that2->color;
}
Image RecolorImage2::generate(const Options& opt) const {
Image img = image->generate(opt);
recolor(img, red,green,blue,white);
return img;
Image img = image->generate(opt);
recolor(img, red,green,blue,white);
return img;
}
bool RecolorImage2::operator == (const GeneratedImage& that) const {
const RecolorImage2* that2 = dynamic_cast<const RecolorImage2*>(&that);
return that2 && *image == *that2->image
&& red == that2->red
&& green == that2->green
&& blue == that2->blue
&& white == that2->white;
const RecolorImage2* that2 = dynamic_cast<const RecolorImage2*>(&that);
return that2 && *image == *that2->image
&& red == that2->red
&& green == that2->green
&& blue == that2->blue
&& white == that2->white;
}
// ----------------------------------------------------------------------------- : FlipImage
Image FlipImageHorizontal::generate(const Options& opt) const {
Image img = image->generate(opt);
return flip_image_horizontal(img);
Image img = image->generate(opt);
return flip_image_horizontal(img);
}
bool FlipImageHorizontal::operator == (const GeneratedImage& that) const {
const FlipImageHorizontal* that2 = dynamic_cast<const FlipImageHorizontal*>(&that);
return that2 && *image == *that2->image;
const FlipImageHorizontal* that2 = dynamic_cast<const FlipImageHorizontal*>(&that);
return that2 && *image == *that2->image;
}
Image FlipImageVertical::generate(const Options& opt) const {
Image img = image->generate(opt);
return flip_image_vertical(img);
Image img = image->generate(opt);
return flip_image_vertical(img);
}
bool FlipImageVertical::operator == (const GeneratedImage& that) const {
const FlipImageVertical* that2 = dynamic_cast<const FlipImageVertical*>(&that);
return that2 && *image == *that2->image;
const FlipImageVertical* that2 = dynamic_cast<const FlipImageVertical*>(&that);
return that2 && *image == *that2->image;
}
Image RotateImage::generate(const Options& opt) const {
Image img = image->generate(opt);
return rotate_image(img,angle);
Image img = image->generate(opt);
return rotate_image(img,angle);
}
bool RotateImage::operator == (const GeneratedImage& that) const {
const RotateImage* that2 = dynamic_cast<const RotateImage*>(&that);
return that2 && *image == *that2->image
&& angle == that2->angle;
const RotateImage* that2 = dynamic_cast<const RotateImage*>(&that);
return that2 && *image == *that2->image
&& angle == that2->angle;
}
// ----------------------------------------------------------------------------- : EnlargeImage
Image EnlargeImage::generate(const Options& opt) const {
// generate 'sub' image
Options sub_opt
( int(opt.width * (border_size < 0.5 ? 1 - 2 * border_size : 0))
, int(opt.height * (border_size < 0.5 ? 1 - 2 * border_size : 0))
, opt.package
, opt.local_package
, opt.preserve_aspect);
Image img = image->generate(sub_opt);
// size of generated image
int w = img.GetWidth(), h = img.GetHeight(); // original image size
int dw = int(w * border_size), dh = int(h * border_size); // delta
int w2 = w + dw + dw, h2 = h + dh + dh; // new image size
Image larger(w2,h2);
larger.InitAlpha();
memset(larger.GetAlpha(),0,w2*h2); // blank
// copy to sub-part of larger image
Byte* data1 = img.GetData(), *data2 = larger.GetData();
for (int y = 0 ; y < h ; ++y) {
memcpy(data2 + 3*(dw + (y+dh)*w2), data1 + 3*y*w, 3*w); // copy a line
}
if (img.HasAlpha()) {
data1 = img.GetAlpha(), data2 = larger.GetAlpha();
for (int y = 0 ; y < h ; ++y) {
memcpy(data2 + dw + (y+dh)*w2, data1 + y*w, w); // copy a line
}
}
// done
return larger;
// generate 'sub' image
Options sub_opt
( int(opt.width * (border_size < 0.5 ? 1 - 2 * border_size : 0))
, int(opt.height * (border_size < 0.5 ? 1 - 2 * border_size : 0))
, opt.package
, opt.local_package
, opt.preserve_aspect);
Image img = image->generate(sub_opt);
// size of generated image
int w = img.GetWidth(), h = img.GetHeight(); // original image size
int dw = int(w * border_size), dh = int(h * border_size); // delta
int w2 = w + dw + dw, h2 = h + dh + dh; // new image size
Image larger(w2,h2);
larger.InitAlpha();
memset(larger.GetAlpha(),0,w2*h2); // blank
// copy to sub-part of larger image
Byte* data1 = img.GetData(), *data2 = larger.GetData();
for (int y = 0 ; y < h ; ++y) {
memcpy(data2 + 3*(dw + (y+dh)*w2), data1 + 3*y*w, 3*w); // copy a line
}
if (img.HasAlpha()) {
data1 = img.GetAlpha(), data2 = larger.GetAlpha();
for (int y = 0 ; y < h ; ++y) {
memcpy(data2 + dw + (y+dh)*w2, data1 + y*w, w); // copy a line
}
}
// done
return larger;
}
bool EnlargeImage::operator == (const GeneratedImage& that) const {
const EnlargeImage* that2 = dynamic_cast<const EnlargeImage*>(&that);
return that2 && *image == *that2->image
&& border_size == that2->border_size;
const EnlargeImage* that2 = dynamic_cast<const EnlargeImage*>(&that);
return that2 && *image == *that2->image
&& border_size == that2->border_size;
}
// ----------------------------------------------------------------------------- : CropImage
Image CropImage::generate(const Options& opt) const {
return image->generate(opt).Size(wxSize((int)width, (int)height), wxPoint(-(int)offset_x, -(int)offset_y));
return image->generate(opt).Size(wxSize((int)width, (int)height), wxPoint(-(int)offset_x, -(int)offset_y));
}
bool CropImage::operator == (const GeneratedImage& that) const {
const CropImage* that2 = dynamic_cast<const CropImage*>(&that);
return that2 && *image == *that2->image
&& width == that2->width && height == that2->height
&& offset_x == that2->offset_x && offset_y == that2->offset_y;
const CropImage* that2 = dynamic_cast<const CropImage*>(&that);
return that2 && *image == *that2->image
&& width == that2->width && height == that2->height
&& offset_x == that2->offset_x && offset_y == that2->offset_y;
}
// ----------------------------------------------------------------------------- : DropShadowImage
@@ -320,190 +320,190 @@ bool CropImage::operator == (const GeneratedImage& that) const {
/// Preform a gaussian blur, from the image in of w*h bytes to out
/** out is scaled some scaling, this is the return value */
UInt gaussian_blur(Byte* in, UInt* out, int w, int h, double radius) {
// blur horizontally
UInt* blur_x = new UInt[w*h]; // scaled by total_x, so in [0..255*total_x]
memset(blur_x, 0, w*h*sizeof(UInt));
UInt total_x = 0;
{
double sigma = radius * w;
double mult = (1 << 8) / (sqrt(2 * M_PI) * sigma);
double sigsqr2 = 1 / (2 * sigma * sigma);
int range = min(w, (int)(3*sigma));
for (int d = -range ; d <= range ; ++d) {
UInt factor = (int)( mult * exp(-d * d * sigsqr2) );
total_x += factor;
if (factor > 0) {
int x_start = max(0, -d), x_end = min(w, w-d);
for (int y = 0 ; y < h ; ++y) {
for (int x = x_start ; x < x_end ; ++x) {
blur_x[x + y*w] += in[x + d + y*w] * factor;
}
}
}
}
}
// blur vertically
memset(out, 0, w*h*sizeof(UInt));
UInt total_y = 0;
{
double sigma = radius * h;
double mult = (1 << 8) / (sqrt(2 * M_PI) * sigma);
double sigsqr2 = 1 / (2 * sigma * sigma);
int range = min(h, (int)(3*sigma));
for (int d = -range ; d <= range ; ++d) {
UInt factor = (UInt)( mult * exp(-d * d * sigsqr2) );
total_y += factor;
if (factor > 0) {
int y_start = max(0, -d), y_end = min(h, h-d);
for (int y = y_start ; y < y_end ; ++y) {
for (int x = 0 ; x < w ; ++x) {
out[x + y*w] += blur_x[x + (d + y)*w] * factor;
}
}
}
}
}
delete[] blur_x;
return total_x * total_y;
// blur horizontally
UInt* blur_x = new UInt[w*h]; // scaled by total_x, so in [0..255*total_x]
memset(blur_x, 0, w*h*sizeof(UInt));
UInt total_x = 0;
{
double sigma = radius * w;
double mult = (1 << 8) / (sqrt(2 * M_PI) * sigma);
double sigsqr2 = 1 / (2 * sigma * sigma);
int range = min(w, (int)(3*sigma));
for (int d = -range ; d <= range ; ++d) {
UInt factor = (int)( mult * exp(-d * d * sigsqr2) );
total_x += factor;
if (factor > 0) {
int x_start = max(0, -d), x_end = min(w, w-d);
for (int y = 0 ; y < h ; ++y) {
for (int x = x_start ; x < x_end ; ++x) {
blur_x[x + y*w] += in[x + d + y*w] * factor;
}
}
}
}
}
// blur vertically
memset(out, 0, w*h*sizeof(UInt));
UInt total_y = 0;
{
double sigma = radius * h;
double mult = (1 << 8) / (sqrt(2 * M_PI) * sigma);
double sigsqr2 = 1 / (2 * sigma * sigma);
int range = min(h, (int)(3*sigma));
for (int d = -range ; d <= range ; ++d) {
UInt factor = (UInt)( mult * exp(-d * d * sigsqr2) );
total_y += factor;
if (factor > 0) {
int y_start = max(0, -d), y_end = min(h, h-d);
for (int y = y_start ; y < y_end ; ++y) {
for (int x = 0 ; x < w ; ++x) {
out[x + y*w] += blur_x[x + (d + y)*w] * factor;
}
}
}
}
}
delete[] blur_x;
return total_x * total_y;
}
Image DropShadowImage::generate(const Options& opt) const {
// sub image
Image img = image->generate(opt);
if (!img.HasAlpha()) {
// no alpha, there is nothing we can do
return img;
}
int w = img.GetWidth(), h = img.GetHeight();
Byte* alpha = img.GetAlpha();
// blur
UInt* shadow = new UInt[w*h];
UInt total = 255 * gaussian_blur(alpha, shadow, w, h, shadow_blur_radius);
// combine
Byte* data = img.GetData();
int dw = int(w * offset_x), dh = int(h * offset_y);
int x_start = max(0, dw), y_start = max(0, dh);
int x_end = min(w, w+dw), y_end = min(h, h+dh);
int delta = dw + w * dh;
int sa = (int)(shadow_alpha * (1 << 16));
for (int y = y_start ; y < y_end ; ++y) {
for (int x = x_start ; x < x_end ; ++x) {
int p = x + y * w; // pixel we are working on
int a = alpha[p];
int shad = ((((255 - a)*sa)>>16) * shadow[p - delta]) / total; // amount of shadow to add
int factor = max(1, a + shad); // divide by this
data[3 * p ] = (a * data[3 * p ] + shad * shadow_color.Red() ) / factor;
data[3 * p + 1] = (a * data[3 * p + 1] + shad * shadow_color.Green()) / factor;
data[3 * p + 2] = (a * data[3 * p + 2] + shad * shadow_color.Blue() ) / factor;
alpha[p] = a + shad;
}
}
//memset(data,0,3*w*h);
// cleanup
delete[] shadow;
return img;
// sub image
Image img = image->generate(opt);
if (!img.HasAlpha()) {
// no alpha, there is nothing we can do
return img;
}
int w = img.GetWidth(), h = img.GetHeight();
Byte* alpha = img.GetAlpha();
// blur
UInt* shadow = new UInt[w*h];
UInt total = 255 * gaussian_blur(alpha, shadow, w, h, shadow_blur_radius);
// combine
Byte* data = img.GetData();
int dw = int(w * offset_x), dh = int(h * offset_y);
int x_start = max(0, dw), y_start = max(0, dh);
int x_end = min(w, w+dw), y_end = min(h, h+dh);
int delta = dw + w * dh;
int sa = (int)(shadow_alpha * (1 << 16));
for (int y = y_start ; y < y_end ; ++y) {
for (int x = x_start ; x < x_end ; ++x) {
int p = x + y * w; // pixel we are working on
int a = alpha[p];
int shad = ((((255 - a)*sa)>>16) * shadow[p - delta]) / total; // amount of shadow to add
int factor = max(1, a + shad); // divide by this
data[3 * p ] = (a * data[3 * p ] + shad * shadow_color.Red() ) / factor;
data[3 * p + 1] = (a * data[3 * p + 1] + shad * shadow_color.Green()) / factor;
data[3 * p + 2] = (a * data[3 * p + 2] + shad * shadow_color.Blue() ) / factor;
alpha[p] = a + shad;
}
}
//memset(data,0,3*w*h);
// cleanup
delete[] shadow;
return img;
}
bool DropShadowImage::operator == (const GeneratedImage& that) const {
const DropShadowImage* that2 = dynamic_cast<const DropShadowImage*>(&that);
return that2 && *image == *that2->image
&& offset_x == that2->offset_x && offset_y == that2->offset_y
&& shadow_alpha == that2->shadow_alpha && shadow_blur_radius == that2->shadow_blur_radius
&& shadow_color == that2->shadow_color;
const DropShadowImage* that2 = dynamic_cast<const DropShadowImage*>(&that);
return that2 && *image == *that2->image
&& offset_x == that2->offset_x && offset_y == that2->offset_y
&& shadow_alpha == that2->shadow_alpha && shadow_blur_radius == that2->shadow_blur_radius
&& shadow_color == that2->shadow_color;
}
// ----------------------------------------------------------------------------- : PackagedImage
Image PackagedImage::generate(const Options& opt) const {
// TODO : use opt.width and opt.height?
// open file from package
if (!opt.package) throw ScriptError(_("Can only load images in a context where an image is expected"));
InputStreamP file = opt.package->openIn(filename);
Image img;
if (img.LoadFile(*file)) {
if (img.HasMask()) img.InitAlpha(); // we can't handle masks
return img;
} else {
throw ScriptError(_("Unable to load image '") + filename + _("' from '" + opt.package->name() + _("'")));
}
// TODO : use opt.width and opt.height?
// open file from package
if (!opt.package) throw ScriptError(_("Can only load images in a context where an image is expected"));
InputStreamP file = opt.package->openIn(filename);
Image img;
if (img.LoadFile(*file)) {
if (img.HasMask()) img.InitAlpha(); // we can't handle masks
return img;
} else {
throw ScriptError(_("Unable to load image '") + filename + _("' from '" + opt.package->name() + _("'")));
}
}
bool PackagedImage::operator == (const GeneratedImage& that) const {
const PackagedImage* that2 = dynamic_cast<const PackagedImage*>(&that);
return that2 && filename == that2->filename;
const PackagedImage* that2 = dynamic_cast<const PackagedImage*>(&that);
return that2 && filename == that2->filename;
}
// ----------------------------------------------------------------------------- : BuiltInImage
Image BuiltInImage::generate(const Options& opt) const {
// TODO : use opt.width and opt.height?
Image img = load_resource_image(name);
if (!img.Ok()) {
throw ScriptError(_("There is no built in image '") + name + _("'"));
}
return img;
// TODO : use opt.width and opt.height?
Image img = load_resource_image(name);
if (!img.Ok()) {
throw ScriptError(_("There is no built in image '") + name + _("'"));
}
return img;
}
bool BuiltInImage::operator == (const GeneratedImage& that) const {
const BuiltInImage* that2 = dynamic_cast<const BuiltInImage*>(&that);
return that2 && name == that2->name;
const BuiltInImage* that2 = dynamic_cast<const BuiltInImage*>(&that);
return that2 && name == that2->name;
}
// ----------------------------------------------------------------------------- : SymbolToImage
SymbolToImage::SymbolToImage(bool is_local, const String& filename, Age age, const SymbolVariationP& variation)
: is_local(is_local), filename(filename), age(age), variation(variation)
: is_local(is_local), filename(filename), age(age), variation(variation)
{}
SymbolToImage::~SymbolToImage() {}
Image SymbolToImage::generate(const Options& opt) const {
// TODO : use opt.width and opt.height?
Package* package = is_local ? opt.local_package : opt.package;
if (!package) throw ScriptError(_("Can only load images in a context where an image is expected"));
SymbolP the_symbol;
if (filename.empty()) {
the_symbol = default_symbol();
} else {
the_symbol = package->readFile<SymbolP>(filename);
}
int size = max(100, 3*max(opt.width,opt.height));
if (opt.width <= 1 || opt.height <= 1) {
return render_symbol(the_symbol, *variation->filter, variation->border_radius, size, size);
} else {
int width = size * opt.width / max(opt.width,opt.height);
int height = size * opt.height / max(opt.width,opt.height);
return render_symbol(the_symbol, *variation->filter, variation->border_radius, width, height, false, true);
}
// TODO : use opt.width and opt.height?
Package* package = is_local ? opt.local_package : opt.package;
if (!package) throw ScriptError(_("Can only load images in a context where an image is expected"));
SymbolP the_symbol;
if (filename.empty()) {
the_symbol = default_symbol();
} else {
the_symbol = package->readFile<SymbolP>(filename);
}
int size = max(100, 3*max(opt.width,opt.height));
if (opt.width <= 1 || opt.height <= 1) {
return render_symbol(the_symbol, *variation->filter, variation->border_radius, size, size);
} else {
int width = size * opt.width / max(opt.width,opt.height);
int height = size * opt.height / max(opt.width,opt.height);
return render_symbol(the_symbol, *variation->filter, variation->border_radius, width, height, false, true);
}
}
bool SymbolToImage::operator == (const GeneratedImage& that) const {
const SymbolToImage* that2 = dynamic_cast<const SymbolToImage*>(&that);
return that2 && is_local == that2->is_local
&& filename == that2->filename
&& age == that2->age
&& (variation == that2->variation ||
*variation == *that2->variation // custom variation
);
const SymbolToImage* that2 = dynamic_cast<const SymbolToImage*>(&that);
return that2 && is_local == that2->is_local
&& filename == that2->filename
&& age == that2->age
&& (variation == that2->variation ||
*variation == *that2->variation // custom variation
);
}
// ----------------------------------------------------------------------------- : ImageValueToImage
ImageValueToImage::ImageValueToImage(const String& filename, Age age)
: filename(filename), age(age)
: filename(filename), age(age)
{}
ImageValueToImage::~ImageValueToImage() {}
Image ImageValueToImage::generate(const Options& opt) const {
// TODO : use opt.width and opt.height?
if (!opt.local_package) throw ScriptError(_("Can only load images in a context where an image is expected"));
Image image;
if (!filename.empty()) {
InputStreamP image_file = opt.local_package->openIn(filename);
image.LoadFile(*image_file);
}
if (!image.Ok()) {
image = Image(max(1,opt.width), max(1,opt.height));
}
return image;
// TODO : use opt.width and opt.height?
if (!opt.local_package) throw ScriptError(_("Can only load images in a context where an image is expected"));
Image image;
if (!filename.empty()) {
InputStreamP image_file = opt.local_package->openIn(filename);
image.LoadFile(*image_file);
}
if (!image.Ok()) {
image = Image(max(1,opt.width), max(1,opt.height));
}
return image;
}
bool ImageValueToImage::operator == (const GeneratedImage& that) const {
const ImageValueToImage* that2 = dynamic_cast<const ImageValueToImage*>(&that);
return that2 && filename == that2->filename
&& age == that2->age;
const ImageValueToImage* that2 = dynamic_cast<const ImageValueToImage*>(&that);
return that2 && filename == that2->filename
&& age == that2->age;
}
+193 -193
View File
@@ -25,44 +25,44 @@ class Package;
*/
class GeneratedImage : public ScriptValue {
public:
/// Options for generating the image
struct Options {
Options(int width = 0, int height = 0, Package* package = nullptr, Package* local_package = nullptr, PreserveAspect preserve_aspect = ASPECT_STRETCH, bool saturate = false)
: width(width), height(height), zoom(1.0), angle(0)
, preserve_aspect(preserve_aspect), saturate(saturate)
, package(package), local_package(local_package)
{}
mutable int width, height; ///< Width to force the image to, or 0 to keep the width of the input
///< In that case, width and height will be later set to the actual size
double zoom; ///< Zoom factor to use, when width=height=0
Radians angle; ///< Angle to rotate image by afterwards
PreserveAspect preserve_aspect;
bool saturate;
Package* package; ///< Package to load images from
Package* local_package; ///< Package to load symbols and ImageValue images from
};
/// Generate the image, and conform to the options
Image generateConform(const Options&) const;
/// Generate the image
virtual Image generate(const Options&) const = 0;
/// How must the image be combined with the background?
virtual ImageCombine combine() const { return COMBINE_DEFAULT; }
/// Equality should mean that every pixel in the generated images is the same if the same options are used
virtual bool operator == (const GeneratedImage& that) const = 0;
inline bool operator != (const GeneratedImage& that) const { return !(*this == that); }
/// Can this image be generated safely from another thread?
virtual bool threadSafe() const { return true; }
/// Is this image specific to the set (the local_package)?
virtual bool local() const { return false; }
/// Is this image blank?
virtual bool isBlank() const { return false; }
virtual ScriptType type() const;
virtual String typeName() const;
virtual GeneratedImageP toImage(const ScriptValueP& thisP) const;
/// Options for generating the image
struct Options {
Options(int width = 0, int height = 0, Package* package = nullptr, Package* local_package = nullptr, PreserveAspect preserve_aspect = ASPECT_STRETCH, bool saturate = false)
: width(width), height(height), zoom(1.0), angle(0)
, preserve_aspect(preserve_aspect), saturate(saturate)
, package(package), local_package(local_package)
{}
mutable int width, height; ///< Width to force the image to, or 0 to keep the width of the input
///< In that case, width and height will be later set to the actual size
double zoom; ///< Zoom factor to use, when width=height=0
Radians angle; ///< Angle to rotate image by afterwards
PreserveAspect preserve_aspect;
bool saturate;
Package* package; ///< Package to load images from
Package* local_package; ///< Package to load symbols and ImageValue images from
};
/// Generate the image, and conform to the options
Image generateConform(const Options&) const;
/// Generate the image
virtual Image generate(const Options&) const = 0;
/// How must the image be combined with the background?
virtual ImageCombine combine() const { return COMBINE_DEFAULT; }
/// Equality should mean that every pixel in the generated images is the same if the same options are used
virtual bool operator == (const GeneratedImage& that) const = 0;
inline bool operator != (const GeneratedImage& that) const { return !(*this == that); }
/// Can this image be generated safely from another thread?
virtual bool threadSafe() const { return true; }
/// Is this image specific to the set (the local_package)?
virtual bool local() const { return false; }
/// Is this image blank?
virtual bool isBlank() const { return false; }
virtual ScriptType type() const;
virtual String typeName() const;
virtual GeneratedImageP toImage(const ScriptValueP& thisP) const;
};
/// Resize an image to conform to the options
@@ -73,13 +73,13 @@ Image conform_image(const Image&, const GeneratedImage::Options&);
/// Apply some filter to a single image
class SimpleFilterImage : public GeneratedImage {
public:
inline SimpleFilterImage(const GeneratedImageP& image)
: image(image)
{}
virtual ImageCombine combine() const { return image->combine(); }
virtual bool local() const { return image->local(); }
inline SimpleFilterImage(const GeneratedImageP& image)
: image(image)
{}
virtual ImageCombine combine() const { return image->combine(); }
virtual bool local() const { return image->local(); }
protected:
GeneratedImageP image;
GeneratedImageP image;
};
// ----------------------------------------------------------------------------- : BlankImage
@@ -87,14 +87,14 @@ class SimpleFilterImage : public GeneratedImage {
/// An image generator that returns a blank image
class BlankImage : public GeneratedImage {
public:
virtual Image generate(const Options&) const;
virtual bool operator == (const GeneratedImage& that) const;
virtual bool isBlank() const { return true; }
// Why is this not thread safe? What is GTK smoking?
#ifdef __WXGTK__
virtual bool threadSafe() const { return false; }
#endif
virtual Image generate(const Options&) const;
virtual bool operator == (const GeneratedImage& that) const;
virtual bool isBlank() const { return true; }
// Why is this not thread safe? What is GTK smoking?
#ifdef __WXGTK__
virtual bool threadSafe() const { return false; }
#endif
};
// ----------------------------------------------------------------------------- : LinearBlendImage
@@ -102,16 +102,16 @@ class BlankImage : public GeneratedImage {
/// An image generator that linearly blends two other images
class LinearBlendImage : public GeneratedImage {
public:
inline LinearBlendImage(const GeneratedImageP& image1, const GeneratedImageP& image2, double x1, double y1, double x2, double y2)
: image1(image1), image2(image2), x1(x1), y1(y1), x2(x2), y2(y2)
{}
virtual Image generate(const Options& opt) const;
virtual ImageCombine combine() const;
virtual bool operator == (const GeneratedImage& that) const;
virtual bool local() const { return image1->local() && image2->local(); }
inline LinearBlendImage(const GeneratedImageP& image1, const GeneratedImageP& image2, double x1, double y1, double x2, double y2)
: image1(image1), image2(image2), x1(x1), y1(y1), x2(x2), y2(y2)
{}
virtual Image generate(const Options& opt) const;
virtual ImageCombine combine() const;
virtual bool operator == (const GeneratedImage& that) const;
virtual bool local() const { return image1->local() && image2->local(); }
private:
GeneratedImageP image1, image2;
double x1, y1, x2, y2;
GeneratedImageP image1, image2;
double x1, y1, x2, y2;
};
// ----------------------------------------------------------------------------- : MaskedBlendImage
@@ -119,15 +119,15 @@ class LinearBlendImage : public GeneratedImage {
/// An image generator that blends two other images using a third as a mask
class MaskedBlendImage : public GeneratedImage {
public:
inline MaskedBlendImage(const GeneratedImageP& light, const GeneratedImageP& dark, const GeneratedImageP& mask)
: light(light), dark(dark), mask(mask)
{}
virtual Image generate(const Options& opt) const;
virtual ImageCombine combine() const;
virtual bool operator == (const GeneratedImage& that) const;
virtual bool local() const { return light->local() && dark->local() && mask->local(); }
inline MaskedBlendImage(const GeneratedImageP& light, const GeneratedImageP& dark, const GeneratedImageP& mask)
: light(light), dark(dark), mask(mask)
{}
virtual Image generate(const Options& opt) const;
virtual ImageCombine combine() const;
virtual bool operator == (const GeneratedImage& that) const;
virtual bool local() const { return light->local() && dark->local() && mask->local(); }
private:
GeneratedImageP light, dark, mask;
GeneratedImageP light, dark, mask;
};
// ----------------------------------------------------------------------------- : CombineBlendImage
@@ -135,16 +135,16 @@ class MaskedBlendImage : public GeneratedImage {
/// An image generator that blends two other images using an ImageCombine function
class CombineBlendImage : public GeneratedImage {
public:
inline CombineBlendImage(const GeneratedImageP& image1, const GeneratedImageP& image2, ImageCombine image_combine)
: image1(image1), image2(image2), image_combine(image_combine)
{}
virtual Image generate(const Options& opt) const;
virtual ImageCombine combine() const;
virtual bool operator == (const GeneratedImage& that) const;
virtual bool local() const { return image1->local() && image2->local(); }
inline CombineBlendImage(const GeneratedImageP& image1, const GeneratedImageP& image2, ImageCombine image_combine)
: image1(image1), image2(image2), image_combine(image_combine)
{}
virtual Image generate(const Options& opt) const;
virtual ImageCombine combine() const;
virtual bool operator == (const GeneratedImage& that) const;
virtual bool local() const { return image1->local() && image2->local(); }
private:
GeneratedImageP image1, image2;
ImageCombine image_combine;
GeneratedImageP image1, image2;
ImageCombine image_combine;
};
// ----------------------------------------------------------------------------- : SetMaskImage
@@ -152,25 +152,25 @@ class CombineBlendImage : public GeneratedImage {
/// Change the alpha channel of an image
class SetMaskImage : public SimpleFilterImage {
public:
inline SetMaskImage(const GeneratedImageP& image, const GeneratedImageP& mask)
: SimpleFilterImage(image), mask(mask)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
inline SetMaskImage(const GeneratedImageP& image, const GeneratedImageP& mask)
: SimpleFilterImage(image), mask(mask)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
private:
GeneratedImageP mask;
GeneratedImageP mask;
};
/// Change the alpha channel of an image
class SetAlphaImage : public SimpleFilterImage {
public:
inline SetAlphaImage(const GeneratedImageP& image, double alpha)
: SimpleFilterImage(image), alpha(alpha)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
inline SetAlphaImage(const GeneratedImageP& image, double alpha)
: SimpleFilterImage(image), alpha(alpha)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
private:
double alpha;
double alpha;
};
// ----------------------------------------------------------------------------- : SetCombineImage
@@ -178,14 +178,14 @@ class SetAlphaImage : public SimpleFilterImage {
/// Change the combine mode
class SetCombineImage : public SimpleFilterImage {
public:
inline SetCombineImage(const GeneratedImageP& image, ImageCombine image_combine)
: SimpleFilterImage(image), image_combine(image_combine)
{}
virtual Image generate(const Options& opt) const;
virtual ImageCombine combine() const;
virtual bool operator == (const GeneratedImage& that) const;
inline SetCombineImage(const GeneratedImageP& image, ImageCombine image_combine)
: SimpleFilterImage(image), image_combine(image_combine)
{}
virtual Image generate(const Options& opt) const;
virtual ImageCombine combine() const;
virtual bool operator == (const GeneratedImage& that) const;
private:
ImageCombine image_combine;
ImageCombine image_combine;
};
// ----------------------------------------------------------------------------- : SaturateImage
@@ -193,13 +193,13 @@ class SetCombineImage : public SimpleFilterImage {
/// Saturate/desaturate an image
class SaturateImage : public SimpleFilterImage {
public:
inline SaturateImage(const GeneratedImageP& image, double amount)
: SimpleFilterImage(image), amount(amount)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
inline SaturateImage(const GeneratedImageP& image, double amount)
: SimpleFilterImage(image), amount(amount)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
private:
double amount;
double amount;
};
// ----------------------------------------------------------------------------- : InvertImage
@@ -207,11 +207,11 @@ class SaturateImage : public SimpleFilterImage {
/// Invert an image
class InvertImage : public SimpleFilterImage {
public:
inline InvertImage(const GeneratedImageP& image)
: SimpleFilterImage(image)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
inline InvertImage(const GeneratedImageP& image)
: SimpleFilterImage(image)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
};
// ----------------------------------------------------------------------------- : RecolorImage
@@ -219,24 +219,24 @@ class InvertImage : public SimpleFilterImage {
/// Recolor an image
class RecolorImage : public SimpleFilterImage {
public:
inline RecolorImage(const GeneratedImageP& image, Color color)
: SimpleFilterImage(image), color(color)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
inline RecolorImage(const GeneratedImageP& image, Color color)
: SimpleFilterImage(image), color(color)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
private:
Color color;
Color color;
};
/// Recolor an image, with custom colors
class RecolorImage2 : public SimpleFilterImage {
public:
inline RecolorImage2(const GeneratedImageP& image, Color red, Color green, Color blue, Color white)
: SimpleFilterImage(image), red(red), green(green), blue(blue), white(white)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
inline RecolorImage2(const GeneratedImageP& image, Color red, Color green, Color blue, Color white)
: SimpleFilterImage(image), red(red), green(green), blue(blue), white(white)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
private:
Color red,green,blue,white;
Color red,green,blue,white;
};
// ----------------------------------------------------------------------------- : FlipImage
@@ -244,33 +244,33 @@ class RecolorImage2 : public SimpleFilterImage {
/// Flip an image horizontally
class FlipImageHorizontal : public SimpleFilterImage {
public:
inline FlipImageHorizontal(const GeneratedImageP& image)
: SimpleFilterImage(image)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
inline FlipImageHorizontal(const GeneratedImageP& image)
: SimpleFilterImage(image)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
};
/// Flip an image vertically
class FlipImageVertical : public SimpleFilterImage {
public:
inline FlipImageVertical(const GeneratedImageP& image)
: SimpleFilterImage(image)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
inline FlipImageVertical(const GeneratedImageP& image)
: SimpleFilterImage(image)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
};
/// Rotate an image
class RotateImage : public SimpleFilterImage {
public:
inline RotateImage(const GeneratedImageP& image, Radians angle)
: SimpleFilterImage(image), angle(angle)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
inline RotateImage(const GeneratedImageP& image, Radians angle)
: SimpleFilterImage(image), angle(angle)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
private:
Radians angle;
Radians angle;
};
// ----------------------------------------------------------------------------- : EnlargeImage
@@ -278,13 +278,13 @@ class RotateImage : public SimpleFilterImage {
/// Enlarge an image by adding a border around it
class EnlargeImage : public SimpleFilterImage {
public:
inline EnlargeImage(const GeneratedImageP& image, double border_size)
: SimpleFilterImage(image), border_size(fabs(border_size))
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
inline EnlargeImage(const GeneratedImageP& image, double border_size)
: SimpleFilterImage(image), border_size(fabs(border_size))
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
private:
double border_size;
double border_size;
};
// ----------------------------------------------------------------------------- : CropImage
@@ -292,14 +292,14 @@ class EnlargeImage : public SimpleFilterImage {
/// Crop an image at a certain point, to a certain size
class CropImage : public SimpleFilterImage {
public:
inline CropImage(const GeneratedImageP& image, double width, double height, double offset_x, double offset_y)
: SimpleFilterImage(image), width(width), height(height), offset_x(offset_x), offset_y(offset_y)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
inline CropImage(const GeneratedImageP& image, double width, double height, double offset_x, double offset_y)
: SimpleFilterImage(image), width(width), height(height), offset_x(offset_x), offset_y(offset_y)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
private:
double width, height;
double offset_x, offset_y;
double width, height;
double offset_x, offset_y;
};
// ----------------------------------------------------------------------------- : DropShadowImage
@@ -307,17 +307,17 @@ class CropImage : public SimpleFilterImage {
/// Add a drop shadow to an image
class DropShadowImage : public SimpleFilterImage {
public:
inline DropShadowImage(const GeneratedImageP& image, double offset_x, double offset_y, double shadow_alpha, double shadow_blur_radius, Color shadow_color)
: SimpleFilterImage(image), offset_x(offset_x), offset_y(offset_y)
, shadow_alpha(shadow_alpha), shadow_blur_radius(shadow_blur_radius), shadow_color(shadow_color)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
inline DropShadowImage(const GeneratedImageP& image, double offset_x, double offset_y, double shadow_alpha, double shadow_blur_radius, Color shadow_color)
: SimpleFilterImage(image), offset_x(offset_x), offset_y(offset_y)
, shadow_alpha(shadow_alpha), shadow_blur_radius(shadow_blur_radius), shadow_color(shadow_color)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
private:
double offset_x, offset_y;
double shadow_alpha;
double shadow_blur_radius;
Color shadow_color;
double offset_x, offset_y;
double shadow_alpha;
double shadow_blur_radius;
Color shadow_color;
};
// ----------------------------------------------------------------------------- : PackagedImage
@@ -325,13 +325,13 @@ class DropShadowImage : public SimpleFilterImage {
/// Load an image from a file in a package
class PackagedImage : public GeneratedImage {
public:
inline PackagedImage(const String& filename)
: filename(filename)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
inline PackagedImage(const String& filename)
: filename(filename)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
private:
String filename;
String filename;
};
// ----------------------------------------------------------------------------- : BuiltInImage
@@ -339,13 +339,13 @@ class PackagedImage : public GeneratedImage {
/// Return a built in image
class BuiltInImage : public GeneratedImage {
public:
inline BuiltInImage(const String& name)
: name(name)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
inline BuiltInImage(const String& name)
: name(name)
{}
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
private:
String name;
String name;
};
// ----------------------------------------------------------------------------- : SymbolToImage
@@ -353,21 +353,21 @@ class BuiltInImage : public GeneratedImage {
/// Use a symbol as an image
class SymbolToImage : public GeneratedImage {
public:
SymbolToImage(bool is_local, const String& filename, Age age, const SymbolVariationP& variation);
~SymbolToImage();
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
virtual bool local() const { return is_local; }
#ifdef __WXGTK__
virtual bool threadSafe() const { return false; }
#endif
SymbolToImage(bool is_local, const String& filename, Age age, const SymbolVariationP& variation);
~SymbolToImage();
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
virtual bool local() const { return is_local; }
#ifdef __WXGTK__
virtual bool threadSafe() const { return false; }
#endif
private:
SymbolToImage(const SymbolToImage&); // copy ctor
bool is_local; ///< Use local package?
String filename;
Age age; ///< Age the symbol was last updated
SymbolVariationP variation;
SymbolToImage(const SymbolToImage&); // copy ctor
bool is_local; ///< Use local package?
String filename;
Age age; ///< Age the symbol was last updated
SymbolVariationP variation;
};
// ----------------------------------------------------------------------------- : ImageValueToImage
@@ -375,15 +375,15 @@ class SymbolToImage : public GeneratedImage {
/// Use an image from an ImageValue as an image
class ImageValueToImage : public GeneratedImage {
public:
ImageValueToImage(const String& filename, Age age);
~ImageValueToImage();
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
virtual bool local() const { return true; }
ImageValueToImage(const String& filename, Age age);
~ImageValueToImage();
virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const;
virtual bool local() const { return true; }
private:
ImageValueToImage(const ImageValueToImage&); // copy ctor
String filename;
Age age; ///< Age the symbol was last updated
ImageValueToImage(const ImageValueToImage&); // copy ctor
String filename;
Age age; ///< Age the symbol was last updated
};
// ----------------------------------------------------------------------------- : EOF
+70 -70
View File
@@ -31,9 +31,9 @@ void resample_and_clip(const Image& img_in, Image& img_out, wxRect rect);
/// How to preserve the aspect ratio of an image when rescaling
enum PreserveAspect
{ ASPECT_STRETCH ///< don't preserve
, ASPECT_BORDER ///< put borders around the image to make it the right shape
, ASPECT_FIT ///< generate a smaller image if needed
{ ASPECT_STRETCH ///< don't preserve
, ASPECT_BORDER ///< put borders around the image to make it the right shape
, ASPECT_FIT ///< generate a smaller image if needed
};
/// Resample an image, but preserve the aspect ratio by adding a transparent border around the output if needed.
@@ -96,32 +96,32 @@ void invert(Image& img);
/// Ways in which images can be combined, similair to what Photoshop supports
enum ImageCombine
{ COMBINE_DEFAULT // normal combine, but with a low priority, i.e. "apply default instead of add" == "add"
// it is not representable in scripting/files, so should only be used internally
, COMBINE_NORMAL
, COMBINE_ADD
, COMBINE_SUBTRACT
, COMBINE_STAMP
, COMBINE_DIFFERENCE
, COMBINE_NEGATION
, COMBINE_MULTIPLY
, COMBINE_DARKEN
, COMBINE_LIGHTEN
, COMBINE_COLOR_DODGE
, COMBINE_COLOR_BURN
, COMBINE_SCREEN
, COMBINE_OVERLAY
, COMBINE_HARD_LIGHT
, COMBINE_SOFT_LIGHT
, COMBINE_REFLECT
, COMBINE_GLOW
, COMBINE_FREEZE
, COMBINE_HEAT
, COMBINE_AND
, COMBINE_OR
, COMBINE_XOR
, COMBINE_SHADOW
, COMBINE_SYMMETRIC_OVERLAY
{ COMBINE_DEFAULT // normal combine, but with a low priority, i.e. "apply default instead of add" == "add"
// it is not representable in scripting/files, so should only be used internally
, COMBINE_NORMAL
, COMBINE_ADD
, COMBINE_SUBTRACT
, COMBINE_STAMP
, COMBINE_DIFFERENCE
, COMBINE_NEGATION
, COMBINE_MULTIPLY
, COMBINE_DARKEN
, COMBINE_LIGHTEN
, COMBINE_COLOR_DODGE
, COMBINE_COLOR_BURN
, COMBINE_SCREEN
, COMBINE_OVERLAY
, COMBINE_HARD_LIGHT
, COMBINE_SOFT_LIGHT
, COMBINE_REFLECT
, COMBINE_GLOW
, COMBINE_FREEZE
, COMBINE_HEAT
, COMBINE_AND
, COMBINE_OR
, COMBINE_XOR
, COMBINE_SHADOW
, COMBINE_SYMMETRIC_OVERLAY
};
/// Combine image b onto image a using some combining function.
@@ -147,48 +147,48 @@ void set_alpha(Image& img, double alpha);
*/
class AlphaMask : public IntrusivePtrBase<AlphaMask> {
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
void setAlpha(Bitmap& b) const;
/// Is the given location opaque (not fully transparent)? when the mask were stretched to size
bool isOpaque(const RealPoint& p, const RealSize& size) const;
bool isOpaque(int x, int y) const;
/// Determine a convex hull polygon *around* the mask
void convexHull(vector<wxPoint>& points) const;
/// 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, const RealSize& size) const;
/// Returns the end of a row, when the mask were stretched to size
double rowRight(double y, const RealSize& size) const;
/// Does this mask have the given size?
inline bool hasSize(const wxSize& compare_size) const { return size == compare_size; }
/// Is the mask loaded?
inline bool isLoaded() const { return alpha; }
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
void setAlpha(Bitmap& b) const;
/// Is the given location opaque (not fully transparent)? when the mask were stretched to size
bool isOpaque(const RealPoint& p, const RealSize& size) const;
bool isOpaque(int x, int y) const;
/// Determine a convex hull polygon *around* the mask
void convexHull(vector<wxPoint>& points) const;
/// 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, const RealSize& size) const;
/// Returns the end of a row, when the mask were stretched to size
double rowRight(double y, const RealSize& size) const;
/// Does this mask have the given size?
inline bool hasSize(const wxSize& compare_size) const { return size == compare_size; }
/// Is the mask loaded?
inline bool isLoaded() const { return alpha; }
private:
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;
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
+76 -76
View File
@@ -13,100 +13,100 @@
// ----------------------------------------------------------------------------- : Saturation
void saturate(Image& image, double amount) {
Byte* pix = image.GetData();
Byte* end = pix + image.GetWidth() * image.GetHeight() * 3;
// the formula for saturation is
// rgb' = (rgb - amount * avg) / (1 - amount)
// if amount >= 1 then this is some kind of inversion
// if amount > 0 then use formula directly
// if amount < 0 then de-saturate instead:
// rgb = rgb' + -amount*avg - -amount*rgb'
// = rgb' * (1 - -amount) + -amount*avg
// if amount < -1 then we are left with just the average
int factor = int(256 * amount);
if (factor == 0) {
return; // nothing to do
} else if (factor == 256) {
// super crazy saturation: division by zero
// if we take infty to be 255, then it is a >avg test
while (pix != end) {
int r = pix[0], g = pix[1], b = pix[2];
pix[0] = r+r > g+b ? 255 : 0;
pix[1] = g+g > b+r ? 255 : 0;
pix[2] = b+b > r+g ? 255 : 0;
pix += 3;
}
} else if (factor > 0) {
int div = 768 - 3 * factor;
assert(div > 0);
while (pix != end) {
int r = pix[0], g = pix[1], b = pix[2];
int avg = factor*(r+g+b);
pix[0] = col((768*r - avg) / div);
pix[1] = col((768*g - avg) / div);
pix[2] = col((768*b - avg) / div);
pix += 3;
}
} else {
int factor1 = -factor;
int factor2 = 768 - 3*factor1;
while (pix != end) {
int r = pix[0], g = pix[1], b = pix[2];
int avg = factor1*(r+g+b);
pix[0] = (factor2*r + avg) / 768;
pix[1] = (factor2*g + avg) / 768;
pix[2] = (factor2*b + avg) / 768;
pix += 3;
}
}
Byte* pix = image.GetData();
Byte* end = pix + image.GetWidth() * image.GetHeight() * 3;
// the formula for saturation is
// rgb' = (rgb - amount * avg) / (1 - amount)
// if amount >= 1 then this is some kind of inversion
// if amount > 0 then use formula directly
// if amount < 0 then de-saturate instead:
// rgb = rgb' + -amount*avg - -amount*rgb'
// = rgb' * (1 - -amount) + -amount*avg
// if amount < -1 then we are left with just the average
int factor = int(256 * amount);
if (factor == 0) {
return; // nothing to do
} else if (factor == 256) {
// super crazy saturation: division by zero
// if we take infty to be 255, then it is a >avg test
while (pix != end) {
int r = pix[0], g = pix[1], b = pix[2];
pix[0] = r+r > g+b ? 255 : 0;
pix[1] = g+g > b+r ? 255 : 0;
pix[2] = b+b > r+g ? 255 : 0;
pix += 3;
}
} else if (factor > 0) {
int div = 768 - 3 * factor;
assert(div > 0);
while (pix != end) {
int r = pix[0], g = pix[1], b = pix[2];
int avg = factor*(r+g+b);
pix[0] = col((768*r - avg) / div);
pix[1] = col((768*g - avg) / div);
pix[2] = col((768*b - avg) / div);
pix += 3;
}
} else {
int factor1 = -factor;
int factor2 = 768 - 3*factor1;
while (pix != end) {
int r = pix[0], g = pix[1], b = pix[2];
int avg = factor1*(r+g+b);
pix[0] = (factor2*r + avg) / 768;
pix[1] = (factor2*g + avg) / 768;
pix[2] = (factor2*b + avg) / 768;
pix += 3;
}
}
}
// ----------------------------------------------------------------------------- : Color inversion
void invert(Image& img) {
Byte* data = img.GetData();
int n = 3 * img.GetWidth() * img.GetHeight();
for (int i = 0 ; i < n ; ++i) {
data[i] = 255 - data[i];
}
Byte* data = img.GetData();
int n = 3 * img.GetWidth() * img.GetHeight();
for (int i = 0 ; i < n ; ++i) {
data[i] = 255 - data[i];
}
}
// ----------------------------------------------------------------------------- : Coloring symbol images
RGB recolor(RGB x, RGB cr, RGB cg, RGB cb, RGB cw) {
int lo = min(x.r,min(x.g,x.b));
// amount of each
int nr = x.r - lo;
int ng = x.g - lo;
int nb = x.b - lo;
int nw = lo;
// We should have that nr+ng+bw+nw < 255,
// otherwise the input is not a mixture of red/green/blue/white.
// Just to be sure, divide by the sum instead of 255
int total = max(255, nr+ng+nb+nw);
return RGB(
static_cast<Byte>( (nr * cr.r + ng * cg.r + nb * cb.r + nw * cw.r) / total ),
static_cast<Byte>( (nr * cr.g + ng * cg.g + nb * cb.g + nw * cw.g) / total ),
static_cast<Byte>( (nr * cr.b + ng * cg.b + nb * cb.b + nw * cw.b) / total )
);
int lo = min(x.r,min(x.g,x.b));
// amount of each
int nr = x.r - lo;
int ng = x.g - lo;
int nb = x.b - lo;
int nw = lo;
// We should have that nr+ng+bw+nw < 255,
// otherwise the input is not a mixture of red/green/blue/white.
// Just to be sure, divide by the sum instead of 255
int total = max(255, nr+ng+nb+nw);
return RGB(
static_cast<Byte>( (nr * cr.r + ng * cg.r + nb * cb.r + nw * cw.r) / total ),
static_cast<Byte>( (nr * cr.g + ng * cg.g + nb * cb.g + nw * cw.g) / total ),
static_cast<Byte>( (nr * cr.b + ng * cg.b + nb * cb.b + nw * cw.b) / total )
);
}
void recolor(Image& img, RGB cr, RGB cg, RGB cb, RGB cw) {
RGB* data = (RGB*)img.GetData();
int n = img.GetWidth() * img.GetHeight();
for (int i = 0 ; i < n ; ++i) {
data[i] = recolor(data[i], cr, cg, cb, cw);
}
RGB* data = (RGB*)img.GetData();
int n = img.GetWidth() * img.GetHeight();
for (int i = 0 ; i < n ; ++i) {
data[i] = recolor(data[i], cr, cg, cb, cw);
}
}
Byte to_grayscale(RGB x) {
return (Byte)((6969 * x.r + 23434 * x.g + 2365 * x.b) / 32768); // from libpng
return (Byte)((6969 * x.r + 23434 * x.g + 2365 * x.b) / 32768); // from libpng
}
void recolor(Image& img, RGB cr) {
RGB black(0,0,0), white(255,255,255);
bool dark = to_grayscale(cr) < 100;
recolor(img, cr, dark ? black : white, dark ? white : black, white);
RGB black(0,0,0), white(255,255,255);
bool dark = to_grayscale(cr) < 100;
recolor(img, cr, dark ? black : white, dark ? white : black, white);
}
+115 -115
View File
@@ -14,164 +14,164 @@
AlphaMask::AlphaMask() : alpha(nullptr), lefts(nullptr), rights(nullptr) {}
AlphaMask::AlphaMask(const Image& img) : alpha(nullptr), lefts(nullptr), rights(nullptr) {
load(img);
load(img);
}
AlphaMask::~AlphaMask() {
clear();
clear();
}
void AlphaMask::clear() {
delete[] alpha; alpha = nullptr;
delete[] lefts; lefts = nullptr;
delete[] rights; rights = nullptr;
delete[] alpha; alpha = nullptr;
delete[] lefts; lefts = nullptr;
delete[] rights; rights = nullptr;
}
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
Byte* from = img.GetData(), *to = alpha;
for (size_t i = 0 ; i < n ; ++i) {
to[i] = from[3*i];
}
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
Byte* from = img.GetData(), *to = alpha;
for (size_t i = 0 ; i < n ; ++i) {
to[i] = from[3*i];
}
}
void AlphaMask::setAlpha(Image& img) const {
if (!alpha) return;
set_alpha(img, alpha, size);
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);
if (!alpha) return;
Image img = bmp.ConvertToImage();
setAlpha(img);
bmp = Bitmap(img);
}
bool AlphaMask::isOpaque(int x, int y) const {
if (x < 0 || y < 0 || x >= size.x || y >= size.y) return false;
if (alpha) {
return alpha[x + y * size.x] >= 20;
} else {
return true;
}
if (x < 0 || y < 0 || x >= size.x || y >= size.y) return false;
if (alpha) {
return alpha[x + y * size.x] >= 20;
} else {
return true;
}
}
bool AlphaMask::isOpaque(const RealPoint& p, const RealSize& resize) const {
if (p.x < 0 || p.y < 0 || p.x >= resize.width || p.y >= resize.height) return false;
if (alpha) {
int x = (int)(p.x * size.x / resize.width);
int y = (int)(p.y * size.y / resize.height);
return alpha[x + y * size.x] >= 20;
} else {
return true;
}
if (p.x < 0 || p.y < 0 || p.x >= resize.width || p.y >= resize.height) return false;
if (alpha) {
int x = (int)(p.x * size.x / resize.width);
int y = (int)(p.y * size.y / resize.height);
return alpha[x + y * size.x] >= 20;
} else {
return true;
}
}
/// Do the points form a (counter??)clockwise angle?
bool convex(const wxPoint& p, const wxPoint& q, const wxPoint& r) {
return p.y*q.x - p.x*q.y - p.y*r.x + q.y*r.x + p.x*r.y - q.x*r.y > 0;
return p.y*q.x - p.x*q.y - p.y*r.x + q.y*r.x + p.x*r.y - q.x*r.y > 0;
}
void make_convex(vector<wxPoint>& points) {
while (points.size() > 2 &&
!convex(points[points.size() - 3]
,points[points.size() - 2]
,points[points.size() - 1])) {
points.erase(points.end() - 2);
}
while (points.size() > 2 &&
!convex(points[points.size() - 3]
,points[points.size() - 2]
,points[points.size() - 1])) {
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);
points.push_back(wxPoint(x,y));
make_convex(points);
}
void AlphaMask::convexHull(vector<wxPoint>& points) const {
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) {
if (alpha[x + y * size.x] >= 20) {
// opaque pixel
miny = min(miny,y);
maxy = y;
if (y == miny) {
add_convex_point(points, x-1, y-1);
}
add_convex_point(points, x-1, y);
lastx = x;
break;
}
}
}
if (maxy == -1) return; // No image
add_convex_point(points, lastx-1, maxy+1);
// Right side, bottom to top
for (int y = maxy ; y >= miny ; --y) {
for (int x = size.x - 1 ; x >= 0 ; --x) {
if (alpha[x + y * size.x] >= 20) {
// opaque pixel
if (y == maxy) {
add_convex_point(points, x+1, y+1);
}
add_convex_point(points, x+1, y);
lastx = x;
break;
}
}
}
add_convex_point(points, lastx+1, miny-1);
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) {
if (alpha[x + y * size.x] >= 20) {
// opaque pixel
miny = min(miny,y);
maxy = y;
if (y == miny) {
add_convex_point(points, x-1, y-1);
}
add_convex_point(points, x-1, y);
lastx = x;
break;
}
}
}
if (maxy == -1) return; // No image
add_convex_point(points, lastx-1, maxy+1);
// Right side, bottom to top
for (int y = maxy ; y >= miny ; --y) {
for (int x = size.x - 1 ; x >= 0 ; --x) {
if (alpha[x + y * size.x] >= 20) {
// opaque pixel
if (y == maxy) {
add_convex_point(points, x+1, y+1);
}
add_convex_point(points, x+1, y);
lastx = x;
break;
}
}
}
add_convex_point(points, lastx+1, miny-1);
}
Image AlphaMask::colorImage(const Color& color) const {
Image image(size.x, size.y);
fill_image(image, color);
setAlpha(image);
return image;
Image image(size.x, size.y);
fill_image(image, color);
setAlpha(image);
return image;
}
// ----------------------------------------------------------------------------- : 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
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] >= 128) { // white enough
rights[y] = x;
if (x < lefts[y]) lefts[y] = x;
}
}
}
if (lefts || !alpha) return;
lefts = new int[size.y];
rights = new int[size.y];
// for each row: determine left and rightmost white pixel
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] >= 128) { // white enough
rights[y] = x;
if (x < lefts[y]) lefts[y] = x;
}
}
}
}
double AlphaMask::rowLeft (double y, const RealSize& resize) const {
loadRowSizes();
if (!lefts || y < 0 || y >= resize.height) {
// no mask, or outside it
return 0;
}
return lefts[(int)(y * size.y / resize.height)] * resize.width / size.x;
loadRowSizes();
if (!lefts || y < 0 || y >= resize.height) {
// no mask, or outside it
return 0;
}
return lefts[(int)(y * size.y / resize.height)] * resize.width / size.x;
}
double AlphaMask::rowRight(double y, const RealSize& resize) const {
loadRowSizes();
if (!rights || y < 0 || y >= resize.height) {
// no mask, or outside it
return resize.width;
}
return rights[(int)(y * size.y / resize.height)] * resize.width / size.x;
loadRowSizes();
if (!rights || y < 0 || y >= resize.height) {
// no mask, or outside it
return resize.width;
}
return rights[(int)(y * size.y / resize.height)] * resize.width / size.x;
}
+59 -59
View File
@@ -13,37 +13,37 @@
// ----------------------------------------------------------------------------- : Solving
UInt solve_linear(double a, double b, double* root) {
if (a == 0) {
if (b == 0) {
root[0] = 0;
return 1;
} else {
return 0;
}
} else {
root[0] = -b / a;
return 1;
}
if (a == 0) {
if (b == 0) {
root[0] = 0;
return 1;
} else {
return 0;
}
} else {
root[0] = -b / a;
return 1;
}
}
UInt solve_quadratic(double a, double b, double c, double* roots) {
if (a == 0) {
return solve_linear(b, c, roots);
} else {
double d = b*b - 4*a*c;
if (d < 0) return 0;
roots[0] = (-b - sqrt(d)) / (2*a);
roots[1] = (-b + sqrt(d)) / (2*a);
return 2;
}
if (a == 0) {
return solve_linear(b, c, roots);
} else {
double d = b*b - 4*a*c;
if (d < 0) return 0;
roots[0] = (-b - sqrt(d)) / (2*a);
roots[1] = (-b + sqrt(d)) / (2*a);
return 2;
}
}
UInt solve_cubic(double a, double b, double c, double d, double* roots) {
if (a == 0) {
return solve_quadratic(b, c, d, roots);
} else {
return solve_cubic(b/a, c/a, d/a, roots);
}
if (a == 0) {
return solve_quadratic(b, c, d, roots);
} else {
return solve_cubic(b/a, c/a, d/a, roots);
}
}
// cubic root
@@ -51,38 +51,38 @@ template <typename T>
inline T curt(T x) { return pow(x, 1.0 / 3); }
UInt solve_cubic(double a, double b, double c, double* roots) {
double p = b - a*a / 3;
double q = c + (2 * a*a*a - 9 * a * b) / 27;
if (p == 0 && q == 0) {
roots[0] = -a / 3;
return 1;
}
complex<double> u;
if (q > 0) {
u = curt(q/2 + sqrt(complex<double>(q*q / 4 + p*p*p / 27)));
} else {
u = curt(q/2 - sqrt(complex<double>(q*q / 4 + p*p*p / 27)));
}
// now for the complex part
// rot1(1, 0)
complex<double> rot2(-0.5, sqrt(3.0) / 2);
complex<double> rot3(-0.5, -sqrt(3.0) / 2);
complex<double> x1 = p / (3.0 * u) - u - a / 3.0;
complex<double> x2 = p / (3.0 * u * rot2) - u * rot2 - a / 3.0;
complex<double> x3 = p / (3.0 * u * rot3) - u * rot3 - a / 3.0;
// check if the solutions are real
UInt count = 0;
if (abs(x1.imag()) < 0.00001) {
roots[count] = x1.real();
count += 1;
}
if (abs(x2.imag()) < 0.00001) {
roots[count] = x2.real();
count += 1;
}
if (abs(x3.imag()) < 0.00001) {
roots[count] = x3.real();
count += 1;
}
return count;
double p = b - a*a / 3;
double q = c + (2 * a*a*a - 9 * a * b) / 27;
if (p == 0 && q == 0) {
roots[0] = -a / 3;
return 1;
}
complex<double> u;
if (q > 0) {
u = curt(q/2 + sqrt(complex<double>(q*q / 4 + p*p*p / 27)));
} else {
u = curt(q/2 - sqrt(complex<double>(q*q / 4 + p*p*p / 27)));
}
// now for the complex part
// rot1(1, 0)
complex<double> rot2(-0.5, sqrt(3.0) / 2);
complex<double> rot3(-0.5, -sqrt(3.0) / 2);
complex<double> x1 = p / (3.0 * u) - u - a / 3.0;
complex<double> x2 = p / (3.0 * u * rot2) - u * rot2 - a / 3.0;
complex<double> x3 = p / (3.0 * u * rot3) - u * rot3 - a / 3.0;
// check if the solutions are real
UInt count = 0;
if (abs(x1.imag()) < 0.00001) {
roots[count] = x1.real();
count += 1;
}
if (abs(x2.imag()) < 0.00001) {
roots[count] = x2.real();
count += 1;
}
if (abs(x3.imag()) < 0.00001) {
roots[count] = x3.real();
count += 1;
}
return count;
}
+213 -213
View File
@@ -30,82 +30,82 @@ void resample_pass(const Image& img_in, Image& img_out, int offset_in, int offse
int length_in, int delta_in, int length_out, int delta_out,
int lines, int line_delta_in, int line_delta_out)
{
bool alpha = img_in.HasAlpha();
if (alpha && !img_out.HasAlpha()) img_out.InitAlpha();
int out_fact = (length_out << shift) / length_in; // how much to output for 256 input = 1 pixel
int out_rest = (length_out << shift) % length_in;
// for each line
for (int l = 0 ; l < lines ; ++l) {
Byte* in = img_in .GetData() + 3 * (offset_in + l * line_delta_in);
Byte* out = img_out.GetData() + 3 * (offset_out + l * line_delta_out);
UInt in_rem = out_fact + out_rest; // remaining to input from the current input pixel
if (alpha) {
Byte* in_a = img_in .GetAlpha() + (offset_in + l * line_delta_in);
Byte* out_a = img_out.GetAlpha() + (offset_out + l * line_delta_out);
for (int x = 0 ; x < length_out ; ++x) {
UInt out_rem = 1 << shift;
UInt totR = 0, totG = 0, totB = 0, totA = 0;
while (out_rem >= in_rem) {
// eat a whole input pixel
totR += in[0] * in_rem * in_a[0]; // multiply by alpha
totG += in[1] * in_rem * in_a[0];
totB += in[2] * in_rem * in_a[0];
totA += in_a[0] * in_rem;
out_rem -= in_rem;
in_rem = out_fact;
in += 3*delta_in; in_a += delta_in;
}
if (out_rem > 0) {
// eat a partial input pixel
totR += in[0] * out_rem * in_a[0];
totG += in[1] * out_rem * in_a[0];
totB += in[2] * out_rem * in_a[0];
totA += in_a[0] * out_rem;
in_rem -= out_rem;
}
// store
if (totA) {
out[0] = totR / totA;
out[1] = totG / totA;
out[2] = totB / totA;
out_a[0] = totA >> shift;
} else {
out[0] = out[1] = out[2] = out_a[0] = 0; // div by 0 is bad
}
out += 3*delta_out; out_a += delta_out;
}
} else {
// no alpha
for (int x = 0 ; x < length_out ; ++x) {
UInt out_rem = 1 << shift;
UInt totR = 0, totG = 0, totB = 0;
while (out_rem >= in_rem) {
// eat a whole input pixel
totR += in[0] * in_rem;
totG += in[1] * in_rem;
totB += in[2] * in_rem;
out_rem -= in_rem;
in_rem = out_fact;
in += 3*delta_in;
}
if (out_rem > 0) {
// eat a partial input pixel
totR += in[0] * out_rem;
totG += in[1] * out_rem;
totB += in[2] * out_rem;
in_rem -= out_rem;
}
// store
out[0] = totR >> shift;
out[1] = totG >> shift;
out[2] = totB >> shift;
out += 3*delta_out;
}
}
}
bool alpha = img_in.HasAlpha();
if (alpha && !img_out.HasAlpha()) img_out.InitAlpha();
int out_fact = (length_out << shift) / length_in; // how much to output for 256 input = 1 pixel
int out_rest = (length_out << shift) % length_in;
// for each line
for (int l = 0 ; l < lines ; ++l) {
Byte* in = img_in .GetData() + 3 * (offset_in + l * line_delta_in);
Byte* out = img_out.GetData() + 3 * (offset_out + l * line_delta_out);
UInt in_rem = out_fact + out_rest; // remaining to input from the current input pixel
if (alpha) {
Byte* in_a = img_in .GetAlpha() + (offset_in + l * line_delta_in);
Byte* out_a = img_out.GetAlpha() + (offset_out + l * line_delta_out);
for (int x = 0 ; x < length_out ; ++x) {
UInt out_rem = 1 << shift;
UInt totR = 0, totG = 0, totB = 0, totA = 0;
while (out_rem >= in_rem) {
// eat a whole input pixel
totR += in[0] * in_rem * in_a[0]; // multiply by alpha
totG += in[1] * in_rem * in_a[0];
totB += in[2] * in_rem * in_a[0];
totA += in_a[0] * in_rem;
out_rem -= in_rem;
in_rem = out_fact;
in += 3*delta_in; in_a += delta_in;
}
if (out_rem > 0) {
// eat a partial input pixel
totR += in[0] * out_rem * in_a[0];
totG += in[1] * out_rem * in_a[0];
totB += in[2] * out_rem * in_a[0];
totA += in_a[0] * out_rem;
in_rem -= out_rem;
}
// store
if (totA) {
out[0] = totR / totA;
out[1] = totG / totA;
out[2] = totB / totA;
out_a[0] = totA >> shift;
} else {
out[0] = out[1] = out[2] = out_a[0] = 0; // div by 0 is bad
}
out += 3*delta_out; out_a += delta_out;
}
} else {
// no alpha
for (int x = 0 ; x < length_out ; ++x) {
UInt out_rem = 1 << shift;
UInt totR = 0, totG = 0, totB = 0;
while (out_rem >= in_rem) {
// eat a whole input pixel
totR += in[0] * in_rem;
totG += in[1] * in_rem;
totB += in[2] * in_rem;
out_rem -= in_rem;
in_rem = out_fact;
in += 3*delta_in;
}
if (out_rem > 0) {
// eat a partial input pixel
totR += in[0] * out_rem;
totG += in[1] * out_rem;
totB += in[2] * out_rem;
in_rem -= out_rem;
}
// store
out[0] = totR >> shift;
out[1] = totG >> shift;
out[2] = totB >> shift;
out += 3*delta_out;
}
}
}
}
// ----------------------------------------------------------------------------- : Resample
@@ -123,33 +123,33 @@ void resample_pass(const Image& img_in, Image& img_out, int offset_in, int offse
* Uses fixed point numbers
*/
void resample(const Image& img_in, Image& img_out) {
resample_and_clip(img_in, img_out, wxRect(0, 0, img_in.GetWidth(), img_in.GetHeight()));
resample_and_clip(img_in, img_out, wxRect(0, 0, img_in.GetWidth(), img_in.GetHeight()));
}
Image resample(const Image& img_in, int width, int height) {
if (img_in.GetWidth() == width && img_in.GetHeight() == height) {
return img_in; // already the right size
} else {
Image img_out(width,height,false);
resample(img_in, img_out);
return img_out;
}
if (img_in.GetWidth() == width && img_in.GetHeight() == height) {
return img_in; // already the right size
} else {
Image img_out(width,height,false);
resample(img_in, img_out);
return img_out;
}
}
void resample_and_clip(const Image& img_in, Image& img_out, wxRect rect) {
// mask to alpha
if (img_in.HasMask() && !img_in.HasAlpha()) {
const_cast<Image&>(img_in).InitAlpha();
}
// starting position in data
int offset_in = (rect.x + img_in.GetWidth() * rect.y);
if (img_out.GetHeight() == rect.height) {
// no resizing vertically
resample_pass(img_in, img_out, offset_in, 0, rect.width, 1, img_out .GetWidth(), 1, rect .GetHeight(), img_in.GetWidth(), img_out .GetWidth());
} else {
Image img_temp(img_out.GetWidth(), rect.height, false);
resample_pass(img_in, img_temp, offset_in, 0, rect.width, 1, img_temp.GetWidth(), 1, rect .GetHeight(), img_in.GetWidth(), img_temp.GetWidth());
resample_pass(img_temp, img_out, 0, 0, rect.height, img_temp.GetWidth(), img_out .GetHeight(), img_temp.GetWidth(), img_temp.GetWidth(), 1, 1);
}
// mask to alpha
if (img_in.HasMask() && !img_in.HasAlpha()) {
const_cast<Image&>(img_in).InitAlpha();
}
// starting position in data
int offset_in = (rect.x + img_in.GetWidth() * rect.y);
if (img_out.GetHeight() == rect.height) {
// no resizing vertically
resample_pass(img_in, img_out, offset_in, 0, rect.width, 1, img_out .GetWidth(), 1, rect .GetHeight(), img_in.GetWidth(), img_out .GetWidth());
} else {
Image img_temp(img_out.GetWidth(), rect.height, false);
resample_pass(img_in, img_temp, offset_in, 0, rect.width, 1, img_temp.GetWidth(), 1, rect .GetHeight(), img_in.GetWidth(), img_temp.GetWidth());
resample_pass(img_temp, img_out, 0, 0, rect.height, img_temp.GetWidth(), img_out .GetHeight(), img_temp.GetWidth(), img_temp.GetWidth(), 1, 1);
}
}
@@ -157,37 +157,37 @@ void resample_and_clip(const Image& img_in, Image& img_out, wxRect rect) {
// fill an image with 100% transparent
void fill_transparent(Image& img) {
if (!img.HasAlpha()) img.InitAlpha();
memset(img.GetAlpha(), 0, img.GetWidth() * img.GetHeight());
if (!img.HasAlpha()) img.InitAlpha();
memset(img.GetAlpha(), 0, img.GetWidth() * img.GetHeight());
}
void resample_preserve_aspect(const Image& img_in, Image& img_out) {
int rheight = img_in.GetHeight() * img_out.GetWidth() / img_in.GetWidth();
int rwidth = img_in.GetWidth() * img_out.GetHeight() / img_in.GetHeight();
// actual size of output
if (rheight < img_out.GetHeight()) rwidth = img_out.GetWidth();
else if (rwidth < img_out.GetWidth()) rheight = img_out.GetHeight();
else {rwidth = img_out.GetWidth(); rheight = img_out.GetHeight();}
int dx = (img_out.GetWidth() - rwidth) / 2;
int dy = (img_out.GetHeight() - rheight) / 2;
// transparent background
fill_transparent(img_out);
// resample
int offset_out = dx + img_out.GetWidth() * dy;
Image img_temp(rwidth, img_in.GetHeight(), false);
img_temp.InitAlpha();
resample_pass(img_in, img_temp, 0, 0, img_in.GetWidth(), 1, rwidth, 1, img_in.GetHeight(), img_in.GetWidth(), img_temp.GetWidth());
resample_pass(img_temp, img_out, 0, offset_out, img_in.GetHeight(), img_temp.GetWidth(), rheight, img_out.GetWidth(), rwidth, 1, 1);
int rheight = img_in.GetHeight() * img_out.GetWidth() / img_in.GetWidth();
int rwidth = img_in.GetWidth() * img_out.GetHeight() / img_in.GetHeight();
// actual size of output
if (rheight < img_out.GetHeight()) rwidth = img_out.GetWidth();
else if (rwidth < img_out.GetWidth()) rheight = img_out.GetHeight();
else {rwidth = img_out.GetWidth(); rheight = img_out.GetHeight();}
int dx = (img_out.GetWidth() - rwidth) / 2;
int dy = (img_out.GetHeight() - rheight) / 2;
// transparent background
fill_transparent(img_out);
// resample
int offset_out = dx + img_out.GetWidth() * dy;
Image img_temp(rwidth, img_in.GetHeight(), false);
img_temp.InitAlpha();
resample_pass(img_in, img_temp, 0, 0, img_in.GetWidth(), 1, rwidth, 1, img_in.GetHeight(), img_in.GetWidth(), img_temp.GetWidth());
resample_pass(img_temp, img_out, 0, offset_out, img_in.GetHeight(), img_temp.GetWidth(), rheight, img_out.GetWidth(), rwidth, 1, 1);
}
Image resample_preserve_aspect(const Image& img_in, int width, int height) {
if (img_in.GetWidth() == width && img_in.GetHeight() == height) {
return img_in; // already the right size
} else {
Image img_out(width,height,false);
resample_preserve_aspect(img_in, img_out);
return img_out;
}
if (img_in.GetWidth() == width && img_in.GetHeight() == height) {
return img_in; // already the right size
} else {
Image img_out(width,height,false);
resample_preserve_aspect(img_in, img_out);
return img_out;
}
}
// ----------------------------------------------------------------------------- : Sharpening
@@ -195,102 +195,102 @@ Image resample_preserve_aspect(const Image& img_in, int width, int height) {
void sharp_downsample(const Image& img_in, Image& img_out, int amount);
void sharp_resample(const Image& img_in, Image& img_out, int amount) {
sharp_resample_and_clip(img_in, img_out, wxRect(0, 0, img_in.GetWidth(), img_in.GetHeight()), amount);
sharp_resample_and_clip(img_in, img_out, wxRect(0, 0, img_in.GetWidth(), img_in.GetHeight()), amount);
}
void sharp_resample_and_clip(const Image& img_in, Image& img_out, wxRect rect, int amount) {
Image img_larger(img_out.GetWidth() * 2, img_out.GetHeight() * 2, false);
resample_and_clip(img_in, img_larger, rect);
sharp_downsample(img_larger, img_out, amount);
Image img_larger(img_out.GetWidth() * 2, img_out.GetHeight() * 2, false);
resample_and_clip(img_in, img_larger, rect);
sharp_downsample(img_larger, img_out, amount);
}
// Downsample an image to create a sharp result by applying a sharpening filter
// img_in must be twice as large as img_out
void sharp_downsample(const Image& img_in, Image& img_out, int amount) {
assert(img_in.GetWidth() == img_out.GetWidth() * 2);
assert(img_in.GetHeight() == img_out.GetHeight() * 2);
int width = img_out.GetWidth(), height = img_out.GetHeight();
int line = width * 6;
int center_weight = 201;
int border_weight = amount;
assert(4 * center_weight - 8 * border_weight > 0);
Byte *in = img_in.GetData(), *out = img_out.GetData();
Byte *al = nullptr, *outa = nullptr;
if (img_in.HasAlpha()) {
img_out.InitAlpha();
al = img_in.GetAlpha();
outa = img_out.GetAlpha();
}
for (int y = 0 ; y < height ; ++y) {
for (int x = 0 ; x < width ; ++x) {
// Filter using a kernel of the form
/* -1 -1
* -1 c c -1
* -1 c c -1
* -1 -1
* But when we are near the edge ignore the pixel
*/
// when there is alpha, all weights are multiplied by 4*255
int center_alpha = al ? al[0] + al[1] + al[width*2] + al[width*2+1] : 1;
int weight = center_weight * center_alpha * 4;
int sumR = center_weight * center_alpha * (in[0] + in[3] + in[line+0] + in[line+3]);
int sumG = center_weight * center_alpha * (in[1] + in[4] + in[line+1] + in[line+4]);
int sumB = center_weight * center_alpha * (in[2] + in[5] + in[line+2] + in[line+5]);
// edges
if (x != 0) {
int a = al ? border_weight * min(2 * (al[-1] + al[width*2-1]), center_alpha)
: border_weight;
sumR -= a * (in[-3] + in[line-3]);
sumG -= a * (in[-2] + in[line-2]);
sumB -= a * (in[-1] + in[line-1]);
weight -= a * 2;
}
if (x+1 != width) {
int a = al ? border_weight * min(2 * (al[2] + al[width*2+2]), center_alpha)
: border_weight;
sumR -= a * (in[6] + in[line+6]);
sumG -= a * (in[7] + in[line+7]);
sumB -= a * (in[8] + in[line+8]);
weight -= a * 2;
}
if (y != 0) {
int a = al ? border_weight * min(2 * (al[-width*2] + al[-width*2+1]), center_alpha)
: border_weight;
sumR -= a * (in[-line+0] + in[-line+3]);
sumG -= a * (in[-line+1] + in[-line+4]);
sumB -= a * (in[-line+2] + in[-line+5]);
weight -= a * 2;
}
if (y+1 != height) {
int a = al ? border_weight * min(2 * (al[width*2*2] + al[width*2*2+1]), center_alpha)
: border_weight;
sumR -= a * (in[line*2+0] + in[line*2+3]);
sumG -= a * (in[line*2+1] + in[line*2+4]);
sumB -= a * (in[line*2+2] + in[line*2+5]);
weight -= a * 2;
}
// And then avarage the result into a single pixel (downsample by factor 2 in both dimensions)
if (weight > 0) {
out[0] = col( sumR / weight );
out[1] = col( sumG / weight );
out[2] = col( sumB / weight );
} else {
out[0] = out[1] = out[2] = 0;
}
if (al) {
outa[0] = center_alpha / 4;
outa += 1;
al += 2;
}
// next pixel
in += 6;
out += 3;
}
// skip a line
in += line;
if (al) al += width*2;
}
assert(img_in.GetWidth() == img_out.GetWidth() * 2);
assert(img_in.GetHeight() == img_out.GetHeight() * 2);
int width = img_out.GetWidth(), height = img_out.GetHeight();
int line = width * 6;
int center_weight = 201;
int border_weight = amount;
assert(4 * center_weight - 8 * border_weight > 0);
Byte *in = img_in.GetData(), *out = img_out.GetData();
Byte *al = nullptr, *outa = nullptr;
if (img_in.HasAlpha()) {
img_out.InitAlpha();
al = img_in.GetAlpha();
outa = img_out.GetAlpha();
}
for (int y = 0 ; y < height ; ++y) {
for (int x = 0 ; x < width ; ++x) {
// Filter using a kernel of the form
/* -1 -1
* -1 c c -1
* -1 c c -1
* -1 -1
* But when we are near the edge ignore the pixel
*/
// when there is alpha, all weights are multiplied by 4*255
int center_alpha = al ? al[0] + al[1] + al[width*2] + al[width*2+1] : 1;
int weight = center_weight * center_alpha * 4;
int sumR = center_weight * center_alpha * (in[0] + in[3] + in[line+0] + in[line+3]);
int sumG = center_weight * center_alpha * (in[1] + in[4] + in[line+1] + in[line+4]);
int sumB = center_weight * center_alpha * (in[2] + in[5] + in[line+2] + in[line+5]);
// edges
if (x != 0) {
int a = al ? border_weight * min(2 * (al[-1] + al[width*2-1]), center_alpha)
: border_weight;
sumR -= a * (in[-3] + in[line-3]);
sumG -= a * (in[-2] + in[line-2]);
sumB -= a * (in[-1] + in[line-1]);
weight -= a * 2;
}
if (x+1 != width) {
int a = al ? border_weight * min(2 * (al[2] + al[width*2+2]), center_alpha)
: border_weight;
sumR -= a * (in[6] + in[line+6]);
sumG -= a * (in[7] + in[line+7]);
sumB -= a * (in[8] + in[line+8]);
weight -= a * 2;
}
if (y != 0) {
int a = al ? border_weight * min(2 * (al[-width*2] + al[-width*2+1]), center_alpha)
: border_weight;
sumR -= a * (in[-line+0] + in[-line+3]);
sumG -= a * (in[-line+1] + in[-line+4]);
sumB -= a * (in[-line+2] + in[-line+5]);
weight -= a * 2;
}
if (y+1 != height) {
int a = al ? border_weight * min(2 * (al[width*2*2] + al[width*2*2+1]), center_alpha)
: border_weight;
sumR -= a * (in[line*2+0] + in[line*2+3]);
sumG -= a * (in[line*2+1] + in[line*2+4]);
sumB -= a * (in[line*2+2] + in[line*2+5]);
weight -= a * 2;
}
// And then avarage the result into a single pixel (downsample by factor 2 in both dimensions)
if (weight > 0) {
out[0] = col( sumR / weight );
out[1] = col( sumG / weight );
out[2] = col( sumB / weight );
} else {
out[0] = out[1] = out[2] = 0;
}
if (al) {
outa[0] = center_alpha / 4;
outa += 1;
al += 2;
}
// next pixel
in += 6;
out += 3;
}
// skip a line
in += line;
if (al) al += width*2;
}
}
+169 -169
View File
@@ -11,7 +11,7 @@
#include <util/error.hpp>
#include <gui/util.hpp> // clearDC_black
#if defined(__WXMSW__) && wxUSE_WXDIB
#include <wx/msw/dib.h>
#include <wx/msw/dib.h>
#endif
void blur_image(const Image& img_in, Image& img_out);
@@ -24,186 +24,186 @@ 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;
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;
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;
}
}
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, AColor color, const String& text, int blur_radius, int repeat) {
// 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;
// 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 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
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
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);
}
// 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;
// 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 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
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
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);
}
}
+86 -86
View File
@@ -15,84 +15,84 @@
// 'Rotater' is a function object that knows how to 'rotate' a pixel coordinate
template <class Rotater>
Image rotate_image_impl(Image img) {
UInt width = img.GetWidth(), height = img.GetHeight();
// initialize the return image
Image ret;
Rotater::init(ret, width, height);
Byte* in = img.GetData(), *out = ret.GetData();
// rotate each pixel
for (UInt y = 0 ; y < height ; ++y) {
for (UInt x = 0 ; x < width ; ++x) {
memcpy(out + 3 * Rotater::offset(x, y, width, height), in, 3);
in += 3;
}
}
// don't forget alpha
if (img.HasAlpha()) {
ret.InitAlpha();
in = img.GetAlpha();
out = ret.GetAlpha();
for (UInt y = 0 ; y < height ; ++y) {
for (UInt x = 0 ; x < width ; ++x) {
out[Rotater::offset(x, y, width, height)] = *in;
in += 1;
}
}
}
// ret is rotated image
return ret;
UInt width = img.GetWidth(), height = img.GetHeight();
// initialize the return image
Image ret;
Rotater::init(ret, width, height);
Byte* in = img.GetData(), *out = ret.GetData();
// rotate each pixel
for (UInt y = 0 ; y < height ; ++y) {
for (UInt x = 0 ; x < width ; ++x) {
memcpy(out + 3 * Rotater::offset(x, y, width, height), in, 3);
in += 3;
}
}
// don't forget alpha
if (img.HasAlpha()) {
ret.InitAlpha();
in = img.GetAlpha();
out = ret.GetAlpha();
for (UInt y = 0 ; y < height ; ++y) {
for (UInt x = 0 ; x < width ; ++x) {
out[Rotater::offset(x, y, width, height)] = *in;
in += 1;
}
}
}
// ret is rotated image
return ret;
}
// ----------------------------------------------------------------------------- : Rotations
// Function object to handle rotation
struct Rotate90deg {
/// Init a rotated image, where the source is w * h pixels
inline static void init(Image& img, UInt w, UInt h) {
img.Create(h, w, false);
}
/// Offset in the target data, x, y, w, h are SOURCE coordintes
inline static int offset(UInt x, UInt y, UInt w, UInt h) {
int mx = y;
int my = w - x - 1;
return h * my + mx; // note: h, since that is the width of the target image
}
/// Init a rotated image, where the source is w * h pixels
inline static void init(Image& img, UInt w, UInt h) {
img.Create(h, w, false);
}
/// Offset in the target data, x, y, w, h are SOURCE coordintes
inline static int offset(UInt x, UInt y, UInt w, UInt h) {
int mx = y;
int my = w - x - 1;
return h * my + mx; // note: h, since that is the width of the target image
}
};
struct Rotate180deg {
inline static void init(Image& img, UInt w, UInt h) {
img.Create(w, h, false);
}
inline static int offset(UInt x, UInt y, UInt w, UInt h) {
UInt mx = w - x - 1;
UInt my = h - y - 1;
return w * my + mx;
}
inline static void init(Image& img, UInt w, UInt h) {
img.Create(w, h, false);
}
inline static int offset(UInt x, UInt y, UInt w, UInt h) {
UInt mx = w - x - 1;
UInt my = h - y - 1;
return w * my + mx;
}
};
struct Rotate270deg {
inline static void init(Image& img, UInt w, UInt h) {
img.Create(h, w, false);
}
inline static int offset(UInt x, UInt y, UInt w, UInt h) {
UInt mx = h - y - 1;
UInt my = x;
return h * my + mx;
}
inline static void init(Image& img, UInt w, UInt h) {
img.Create(h, w, false);
}
inline static int offset(UInt x, UInt y, UInt w, UInt h) {
UInt mx = h - y - 1;
UInt my = x;
return h * my + mx;
}
};
// ----------------------------------------------------------------------------- : Interface
Image rotate_image(const Image& image, Radians angle) {
double a = constrain_radians(angle);
if (is_rad0(a)) return image;
if (is_rad90(a)) return rotate_image_impl<Rotate90deg> (image);
if (is_rad180(a)) return rotate_image_impl<Rotate180deg>(image);
if (is_rad270(a)) return rotate_image_impl<Rotate270deg>(image);
else {
if (!image.HasAlpha()) const_cast<Image&>(image).InitAlpha();
return image.Rotate(angle, wxPoint(0,0));
}
double a = constrain_radians(angle);
if (is_rad0(a)) return image;
if (is_rad90(a)) return rotate_image_impl<Rotate90deg> (image);
if (is_rad180(a)) return rotate_image_impl<Rotate180deg>(image);
if (is_rad270(a)) return rotate_image_impl<Rotate270deg>(image);
else {
if (!image.HasAlpha()) const_cast<Image&>(image).InitAlpha();
return image.Rotate(angle, wxPoint(0,0));
}
}
@@ -100,37 +100,37 @@ Image rotate_image(const Image& image, Radians angle) {
// reverse a list of n chunks of size 'step'
void do_flip(Byte const* in, Byte* out, int step, int n) {
for (int i = 0, j = n-1 ; i < n ; ++i, --j) {
memcpy(&out[i*step], &in[j*step], step);
}
for (int i = 0, j = n-1 ; i < n ; ++i, --j) {
memcpy(&out[i*step], &in[j*step], step);
}
}
void do_flip(Byte const* in, Byte* out, int step1, int n1, int n2) {
int step2 = step1 * n1;
for (int i = 0 ; i < n2 ; ++i) {
do_flip(in,out,step1,n1);
in += step2;
out += step2;
}
int step2 = step1 * n1;
for (int i = 0 ; i < n2 ; ++i) {
do_flip(in,out,step1,n1);
in += step2;
out += step2;
}
}
Image flip_image_horizontal(Image const& img) {
int w = img.GetWidth(), h= img.GetHeight();
Image out(w,h,false);
do_flip(img.GetData(), out.GetData(), 3, w, h);
if (img.HasAlpha()) {
out.InitAlpha();
do_flip(img.GetAlpha(), out.GetAlpha(), 1, w, h);
}
return out;
int w = img.GetWidth(), h= img.GetHeight();
Image out(w,h,false);
do_flip(img.GetData(), out.GetData(), 3, w, h);
if (img.HasAlpha()) {
out.InitAlpha();
do_flip(img.GetAlpha(), out.GetAlpha(), 1, w, h);
}
return out;
}
Image flip_image_vertical(Image const& img) {
int w = img.GetWidth(), h= img.GetHeight();
Image out(w,h,false);
do_flip(img.GetData(), out.GetData(), 3 * w, h);
if (img.HasAlpha()) {
out.InitAlpha();
do_flip(img.GetAlpha(), out.GetAlpha(), 1 * w, h);
}
return out;
int w = img.GetWidth(), h= img.GetHeight();
Image out(w,h,false);
do_flip(img.GetData(), out.GetData(), 3 * w, h);
if (img.HasAlpha()) {
out.InitAlpha();
do_flip(img.GetAlpha(), out.GetAlpha(), 1 * w, h);
}
return out;
}