mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-13 14:07:01 -04:00
Change tabs to two spaces.
This commit is contained in:
+165
-165
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user