initial checkin of C++ port (in progress)

git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@2 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
twanvl
2006-10-01 14:08:07 +00:00
parent f5c0071da6
commit ddfb1a5089
56 changed files with 8781 additions and 0 deletions
+252
View File
@@ -0,0 +1,252 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gfx/bezier.hpp>
#include <gfx/polynomial.hpp>
// ----------------------------------------------------------------------------- : Evaluation
BezierCurve::BezierCurve(const Vector2D& p0, const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) {
// 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.deltaAfter * 3.0;
b = (p3.pos + p3.deltaBefore - p0.pos - p0.deltaAfter) * 3.0 - c;
a = (p3.pos - p0.pos) - c - b;
d = p0.pos;
}
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;
}
void deCasteljau(ControlPoint& a, ControlPoint& b, double t, ControlPoint& mid) {
deCasteljau(a.pos, a.deltaAfter, b.deltaBefore, 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.deltaBefore = mid23h21 - out.pos;
out.deltaAfter = mid23h34 - out.pos;
a21 = half21;
a34 = half34;
}
// ----------------------------------------------------------------------------- : Drawing
void curveSubdivide(const BezierCurve& c, const Vector2D& p0, const Vector2D& p1, double t0, double t1, const Rotation& rot, 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
curveSubdivide(c, p0, midpoint, t0, midtime, rot, out, level - 1);
// add midpoint
if (subdivide) {
out.push_back(rot.tr(midpoint));
}
// subdivide right
curveSubdivide(c, midpoint, p1, midtime, t1, rot, out, level - 1);
}
void segmentSubdivide(const ControlPoint& p0, const ControlPoint& p1, const Rotation& rot, vector<wxPoint>& out) {
assert(p0.segmentAfter == p1.segmentBefore);
// always the start
out.push_back(rot.tr(p0.pos));
if (p0.segmentAfter == SEGMENT_CURVE) {
// need more points?
BezierCurve curve(p0,p1);
curveSubdivide(curve, p0.pos, p1.pos, 0, 1, rot, out, 5);
}
}
// ----------------------------------------------------------------------------- : Bounds
void segmentBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max) {
assert(p1.segmentAfter == p2.segmentBefore);
if (p1.segmentAfter == SEGMENT_LINE) {
lineBounds (p1.pos, p2.pos, min, max);
} else {
bezierBounds(p1, p2, min, max);
}
}
void bezierBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max) {
assert(p1.segmentAfter == SEGMENT_CURVE);
// First of all, the corners should be in the bounding box
pointBounds(p1.pos, min, max);
pointBounds(p2.pos, min, max);
// Solve the derivative of the bezier curve to find its extremes
// It's only a quadtratic equation :)
BezierCurve curve(p1,p2);
double roots[4];
UInt count;
count = solveQuadratic(3*curve.a.x, 2*curve.b.x, curve.c.x, roots);
count += solveQuadratic(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) {
pointBounds(curve.pointAt(t), min, max);
}
}
}
void lineBounds(const Vector2D& p1, const Vector2D& p2, Vector2D& min, Vector2D& max) {
pointBounds(p1, min, max);
pointBounds(p2, min, max);
}
void pointBounds(const Vector2D& p, Vector2D& min, Vector2D& max) {
min = piecewise_min(min, p);
max = piecewise_max(max, p);
}
// Is a point inside the bounds <min...max>?
bool pointInBounds(const Vector2D& p, const Vector2D& min, const Vector2D& max) {
return p.x >= min.x && p.y >= min.y &&
p.x <= max.x && p.y <= max.y;
}
// ----------------------------------------------------------------------------- : Point tests
// As a point inside a symbol part?
bool pointInPart(const Vector2D& pos, const SymbolPart& part) {
// Step 1. compare bounding box of the part
if (!pointInBounds(pos, part.minPos, part.maxPos)) return false;
// Step 2. trace ray outward, count intersections
int count = 0;
size_t size = part.points.size();
for(size_t i = 0 ; i < size ; ++i) {
ControlPointP p1 = part.getPoint((int) i);
ControlPointP p2 = part.getPoint((int) i + 1);
if (p1->segmentAfter == SEGMENT_LINE) {
count += intersectLineRay (p1->pos, p2->pos, pos);
} else {
count += intersectBezierRay(*p1, *p2, pos);
}
}
return count & 1; // odd number of intersections
}
// ----------------------------------------------------------------------------- : Finding points
bool posOnSegment(const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut) {
if (p1.segmentAfter == SEGMENT_CURVE) {
return posOnBezier(pos, range, p1, p2, pOut, tOut);
} else {
return posOnLine (pos, range, p1.pos, p2.pos, pOut, tOut);
}
}
bool posOnBezier(const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut) {
assert(p1.segmentAfter == 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 = solveCubic(curve.a.y, curve.b.y, curve.c.y, curve.d.y - pos.y, roots);
count += solveCubic(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 posOnLine(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 = p21.dot(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 intersectBezierRay(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 = solveCubic(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 intersectLineRay(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
}
}
+130
View File
@@ -0,0 +1,130 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GFX_BEZIER
#define HEADER_GFX_BEZIER
/** @file gfx/bezier.hpp
*
* Bezier curve and line related functions
*/
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/rotation.hpp>
#include <data/symbol.hpp>
// ----------------------------------------------------------------------------- : Evaluation
/// 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;
}
};
/// Subdivide a curve from a to b, store the result in a control point
/** Also modifies the handles of the points to accomodate the inserted point
* Direct version, using input curve a1,a2,a3,a4 and output curves a1,b2,b3,b4 and c1,c2,c3,a4
*/
void deCasteljau(Vector2D a1, Vector2D a2, Vector2D a3, Vector2D a4,
Vector2D& b2, Vector2D& b3, Vector2D& b4c1,
Vector2D& c2, Vector2D& c3, double t);
/// Subdivide a curve from a to b at time t
/** Stores the point at time t in mid, updates the handles of a and b!
*/
void deCasteljau(ControlPoint& a, ControlPoint& b, double t, ControlPoint& mid);
/// Subdivide a curve from a to b, store the result in a control point
/** Also modifies the handles of the points to accomodate the inserted point!
*/
void deCasteljau(const Vector2D& a1, Vector2D& a21, Vector2D& a34, const Vector2D& a4, double t, ControlPoint& out);
// ----------------------------------------------------------------------------- : Drawing
/// Devide a segment into a number of straight lines for display purposes
/** Adds the resulting corner points of those lines to out, the last point is not added.
* All points are converted to display coordinates using rot.tr
*/
void segmentSubdivide(const ControlPoint& p0, const ControlPoint& p1, const Rotation& rot, vector<wxPoint>& out);
// ----------------------------------------------------------------------------- : Bounds
/// Find a bounding box that fits a segment (either a line or a bezier curve) between p1 and p2.
/** stores the results in min and max.
* min is only changed if the minimum is smaller then the current value in min,
* max only if the maximum is larger.
*/
void segmentBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max);
/// Find a bounding box that fits a curve between p1 and p2, stores the results in min and max.
/** min is only changed if the minimum is smaller then the current value in min,
* max only if the maximum is larger
*/
void bezierBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max);
/// Find a bounding box that fits around p1 and p2, stores the result in min and max
/** min is only changed if the minimum is smaller then the current value in min,
* max only if the maximum is larger
*/
void lineBounds(const Vector2D& p1, const Vector2D& p2, Vector2D& min, Vector2D& max);
/// Find a bounding 'box' that fits around a single point
/** min is only changed if the minimum is smaller then the current value in min,
* max only if the maximum is larger
*/
void pointBounds(const Vector2D& p, Vector2D& min, Vector2D& max);
// ----------------------------------------------------------------------------- : Point tests
/// Is a point inside the given symbol part?
bool pointInPart(const Vector2D& p, const SymbolPart& part);
// ----------------------------------------------------------------------------- : Finding points
/// Finds the position of p0 on the line between p1 and p2, returns true if the point is on (or near) the line
/// the line between p1 and p2 can also be a bezier curve
/** Returns the time on the segment in tOut, and the point on the segment in pOut
*/
bool posOnSegment(const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut);
/// Finds the position of p0 on the line between p1 and p2, returns true if the point is on (or near)
/// the bezier curve between p1 and p2
bool posOnBezier (const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut);
/// Finds the position of p0 on the line p1-p2, returns true if the point is withing range of the line
/// if that is the case then (x,y) = p1 + (p2-p1) * out
bool posOnLine (const Vector2D& pos, double range, const Vector2D& p1, const Vector2D& p2, Vector2D& pOut, double& tOut);
// ----------------------------------------------------------------------------- : Intersection
/// Counts the number of intersections between the ray/halfline from (-inf, pos.y) to pos
/// and the bezier curve between p1 and p2.
UInt intersectBezierRay(const ControlPoint& p1, const ControlPoint& p2, const Vector2D& pos);
// Does the line between p1 and p2 intersect the ray (half line) from (-inf, pos.y) to pos?
bool intersectLineRay(const Vector2D& p1, const Vector2D& p2, const Vector2D& pos);
// ----------------------------------------------------------------------------- : EOF
#endif
+139
View File
@@ -0,0 +1,139 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include "../util/prec.hpp"
#include "../util/reflect.hpp"
#include "gfx.hpp"
#include <algorithm>
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);
}
// ----------------------------------------------------------------------------- : Combining functions
// Functor for combining functions for a given combining type
template <ImageCombine combine> struct Combine {
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; }
// 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) )
// ----------------------------------------------------------------------------- : Combining
/// Combine image b onto image a using some combining mode.
/// The results are stored in the image A.
template <ImageCombine combine>
void combineImageDo(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]);
}
}
void combineImage(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: combineImageDo<comb>(a,b); return
DISPATCH(COMBINE_NORMAL);
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);
}
}
void drawCombineImage(DC& dc, UInt x, UInt y, const Image& img, ImageCombine combine) {
}
+109
View File
@@ -0,0 +1,109 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GFX_GFX
#define HEADER_GFX_GFX
/** @file gfx/gfx.hpp
*
* Graphics/image processing functions.
*/
// ----------------------------------------------------------------------------- : Includes
#include "../util/prec.hpp"
// ----------------------------------------------------------------------------- : Resampling
/// Resample (resize) an image, uses bilenear filtering
/** The algorithm first resizes in horizontally, then vertically,
* the two passes are essentially the same:
* - for each row:
* - each input pixel becomes a fixed amount of output (in 1<<shift fixed point math)
* - for each output pixel:
* - 'eat' input pixels until the total is 1<<shift
* - write the total to the output pixel
* - to ensure the sum of all the pixel amounts is exacly width<<shift an extra rest amount
* is 'eaten' from the first pixel
*
* Uses fixed point numbers internally
*/
void resample(const Image& imgIn, Image& imgOut);
/// Resamples an image, first clips the input image to a specified rectangle,
/// that rectangle is resampledinto the entire output image
void resample_and_clip(const Image& imgIn, Image& imgOut, wxRect rect);
// ----------------------------------------------------------------------------- : Image rotation
/// Rotates an image counter clockwise
/// angle must be a multiple of 90, i.e. {0,90,180,270}
Image rotateImageBy(const Image& image, int angle);
// ----------------------------------------------------------------------------- : Blending
/// Blends two images together, using a horizontal gradient
/** The result is stored in img1
* To the left the color is that of img1, to the right of img2
*/
void hblend(Image& img1, const Image& img2);
/// Blends two images together, using a vertical gradient
void vblend(Image& img1, const Image& img2);
/// Blends two images together, using a third image as a mask
/** The result is stored in img1
* mask is used as a mask, white pixels are taken from img1, black pixels from img2
* color channels are blended separatly
*/
void maskBlend(Image& img1, const Image& img2, const Image& mask);
// ----------------------------------------------------------------------------- : Combining
/// Ways in which images can be combined, similair to what Photoshop supports
enum ImageCombine
{ 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 image b onto image a using some combining function.
/// The results are stored in the image A.
/// This image gets the alpha channel from B, it should then be
/// drawn onto the area where A originated.
void combineImage(Image& a, const Image& b, ImageCombine combine);
/// Draw an image to a DC using a combining function
void drawCombineImage(DC& dc, UInt x, UInt y, const Image& img, ImageCombine combine);
// ----------------------------------------------------------------------------- : Utility
inline int bot(int x) { return max(0, x); } //^ bottom range check for color values
inline int top(int x) { return min(255, x); } //^ top range check for color values
inline int col(int x) { return top(bot(x)); } //^ top and bottom range check for color values
// ----------------------------------------------------------------------------- : EOF
#endif
+87
View File
@@ -0,0 +1,87 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gfx/polynomial.hpp>
#include <complex>
// ----------------------------------------------------------------------------- : Solving
UInt solveLinear(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;
}
}
UInt solveQuadratic(double a, double b, double c, double* roots) {
if (a == 0) {
return solveLinear(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 solveCubic(double a, double b, double c, double d, double* roots) {
if (a == 0) {
return solveQuadratic(b, c, d, roots);
} else {
return solveCubic(b/a, c/a, d/a, roots);
}
}
// cubic root
template <typename T>
inline T curt(T x) { return pow(x, 1.0 / 3); }
UInt solveCubic(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;
}
+43
View File
@@ -0,0 +1,43 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GFX_POLYNOMIAL
#define HEADER_GFX_POLYNOMIAL
/** @file gfx/polynomial.hpp
*
* Solutions to polynomials, used by bezier curve algorithms
*/
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
// ----------------------------------------------------------------------------- : Solving
/// Solve a linear equation a x + b = 0
/** Returns the number of real roots, and the roots themselfs in the output parameter.
*/
UInt solveLinear(double a, double b, double* root);
/// Solve a quadratic equation a x^2 + b x + c == 0
/** Returns the number of real roots, and the roots themselfs in the output parameter.
*/
UInt solveQuadratic(double a, double b, double c, double* roots);
// Solve a cubic equation a x^3 + b x^2 + c x + d == 0
/** Returns the number of real roots, and the roots themselfs in the output parameter.
*/
UInt solveCubic(double a, double b, double c, double d, double* roots);
// Solve a cubic equation x^3 + a x^2 + b x + c == 0
/** Returns the number of real roots, and the roots themselfs in the output parameter.
* Based on http://en.wikipedia.org/wiki/Cubic_equation
*/
UInt solveCubic(double a, double b, double c, double* roots);
// ----------------------------------------------------------------------------- : EOF
#endif
+96
View File
@@ -0,0 +1,96 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include "../util/prec.hpp"
#include "gfx.hpp"
// ----------------------------------------------------------------------------- : Implementation
// Rotates an image
// 'Rotater' is a function object that knows how to 'rotate' a pixel coordinate
template <class Rotater>
Image rotateImageImpl(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;
}
// ----------------------------------------------------------------------------- : Rotations
// Function object to handle rotation
struct Rotate90 {
/// 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 Rotate180 {
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 Rotate270 {
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 rotateImageBy(const Image& image, int angle) {
if (angle == 90) {
return rotateImageImpl<Rotate90>(image);
} else if (angle == 180){
return rotateImageImpl<Rotate180>(image);
} else if (angle == 270){
return rotateImageImpl<Rotate270>(image);
} else{
return image;
}
}