+
+static inline wxRealPoint
+wxRotatePoint(double x, double y, double cos_angle, double sin_angle,
+ const wxRealPoint & p0)
+{
+ return wxRotatePoint (wxRealPoint(x,y), cos_angle, sin_angle, p0);
+}
+
+wxImage wxImage::Rotate(double angle,
+ const wxPoint& centre_of_rotation,
+ bool interpolating,
+ wxPoint *offset_after_rotation) const
+{
+ // screen coordinates are a mirror image of "real" coordinates
+ angle = -angle;
+
+ const bool has_alpha = HasAlpha();
+
+ const int w = GetWidth();
+ const int h = GetHeight();
+
+ int i;
+
+ // Create pointer-based array to accelerate access to wxImage's data
+ unsigned char ** data = new unsigned char * [h];
+ data[0] = GetData();
+ for (i = 1; i < h; i++)
+ data[i] = data[i - 1] + (3 * w);
+
+ // Same for alpha channel
+ unsigned char ** alpha = NULL;
+ if (has_alpha)
+ {
+ alpha = new unsigned char * [h];
+ alpha[0] = GetAlpha();
+ for (i = 1; i < h; i++)
+ alpha[i] = alpha[i - 1] + w;
+ }
+
+ // precompute coefficients for rotation formula
+ const double cos_angle = cos(angle);
+ const double sin_angle = sin(angle);
+
+ // Create new Image to store the result
+ // First, find rectangle that covers the rotated image; to do that,
+ // rotate the four corners
+
+ const wxRealPoint p0(centre_of_rotation.x, centre_of_rotation.y);
+
+ wxRealPoint p1 = wxRotatePoint (0, 0, cos_angle, sin_angle, p0);
+ wxRealPoint p2 = wxRotatePoint (0, h, cos_angle, sin_angle, p0);
+ wxRealPoint p3 = wxRotatePoint (w, 0, cos_angle, sin_angle, p0);
+ wxRealPoint p4 = wxRotatePoint (w, h, cos_angle, sin_angle, p0);
+
+ int x1a = (int) floor (wxMin (wxMin(p1.x, p2.x), wxMin(p3.x, p4.x)));
+ int y1a = (int) floor (wxMin (wxMin(p1.y, p2.y), wxMin(p3.y, p4.y)));
+ int x2a = (int) ceil (wxMax (wxMax(p1.x, p2.x), wxMax(p3.x, p4.x)));
+ int y2a = (int) ceil (wxMax (wxMax(p1.y, p2.y), wxMax(p3.y, p4.y)));
+
+ // Create rotated image
+ wxImage rotated (x2a - x1a + 1, y2a - y1a + 1, false);
+ // With alpha channel
+ if (has_alpha)
+ rotated.SetAlpha();
+
+ if (offset_after_rotation != NULL)
+ {
+ *offset_after_rotation = wxPoint (x1a, y1a);
+ }
+
+ // the rotated (destination) image is always accessed sequentially via this
+ // pointer, there is no need for pointer-based arrays here
+ unsigned char *dst = rotated.GetData();
+
+ unsigned char *alpha_dst = has_alpha ? rotated.GetAlpha() : NULL;
+
+ // if the original image has a mask, use its RGB values as the blank pixel,
+ // else, fall back to default (black).
+ unsigned char blank_r = 0;
+ unsigned char blank_g = 0;
+ unsigned char blank_b = 0;
+
+ if (HasMask())
+ {
+ blank_r = GetMaskRed();
+ blank_g = GetMaskGreen();
+ blank_b = GetMaskBlue();
+ rotated.SetMaskColour( blank_r, blank_g, blank_b );
+ }
+
+ // Now, for each point of the rotated image, find where it came from, by
+ // performing an inverse rotation (a rotation of -angle) and getting the
+ // pixel at those coordinates
+
+ const int rH = rotated.GetHeight();
+ const int rW = rotated.GetWidth();
+
+ // do the (interpolating) test outside of the loops, so that it is done
+ // only once, instead of repeating it for each pixel.
+ if (interpolating)
+ {
+ for (int y = 0; y < rH; y++)
+ {
+ for (int x = 0; x < rW; x++)
+ {
+ wxRealPoint src = wxRotatePoint (x + x1a, y + y1a, cos_angle, -sin_angle, p0);
+
+ if (-0.25 < src.x && src.x < w - 0.75 &&
+ -0.25 < src.y && src.y < h - 0.75)
+ {
+ // interpolate using the 4 enclosing grid-points. Those
+ // points can be obtained using floor and ceiling of the
+ // exact coordinates of the point
+ int x1, y1, x2, y2;
+
+ if (0 < src.x && src.x < w - 1)
+ {
+ x1 = wxRound(floor(src.x));
+ x2 = wxRound(ceil(src.x));
+ }
+ else // else means that x is near one of the borders (0 or width-1)
+ {
+ x1 = x2 = wxRound (src.x);
+ }
+
+ if (0 < src.y && src.y < h - 1)
+ {
+ y1 = wxRound(floor(src.y));
+ y2 = wxRound(ceil(src.y));
+ }
+ else
+ {
+ y1 = y2 = wxRound (src.y);
+ }
+
+ // get four points and the distances (square of the distance,
+ // for efficiency reasons) for the interpolation formula
+
+ // GRG: Do not calculate the points until they are
+ // really needed -- this way we can calculate
+ // just one, instead of four, if d1, d2, d3
+ // or d4 are < wxROTATE_EPSILON
+
+ const double d1 = (src.x - x1) * (src.x - x1) + (src.y - y1) * (src.y - y1);
+ const double d2 = (src.x - x2) * (src.x - x2) + (src.y - y1) * (src.y - y1);
+ const double d3 = (src.x - x2) * (src.x - x2) + (src.y - y2) * (src.y - y2);
+ const double d4 = (src.x - x1) * (src.x - x1) + (src.y - y2) * (src.y - y2);
+
+ // Now interpolate as a weighted average of the four surrounding
+ // points, where the weights are the distances to each of those points
+
+ // If the point is exactly at one point of the grid of the source
+ // image, then don't interpolate -- just assign the pixel
+
+ // d1,d2,d3,d4 are positive -- no need for abs()
+ if (d1 < wxROTATE_EPSILON)
+ {
+ unsigned char *p = data[y1] + (3 * x1);
+ *(dst++) = *(p++);
+ *(dst++) = *(p++);
+ *(dst++) = *p;
+
+ if (has_alpha)
+ *(alpha_dst++) = *(alpha[y1] + x1);
+ }
+ else if (d2 < wxROTATE_EPSILON)
+ {
+ unsigned char *p = data[y1] + (3 * x2);
+ *(dst++) = *(p++);
+ *(dst++) = *(p++);
+ *(dst++) = *p;
+
+ if (has_alpha)
+ *(alpha_dst++) = *(alpha[y1] + x2);
+ }
+ else if (d3 < wxROTATE_EPSILON)
+ {
+ unsigned char *p = data[y2] + (3 * x2);
+ *(dst++) = *(p++);
+ *(dst++) = *(p++);
+ *(dst++) = *p;
+
+ if (has_alpha)
+ *(alpha_dst++) = *(alpha[y2] + x2);
+ }
+ else if (d4 < wxROTATE_EPSILON)
+ {
+ unsigned char *p = data[y2] + (3 * x1);
+ *(dst++) = *(p++);
+ *(dst++) = *(p++);
+ *(dst++) = *p;
+
+ if (has_alpha)
+ *(alpha_dst++) = *(alpha[y2] + x1);
+ }
+ else
+ {
+ // weights for the weighted average are proportional to the inverse of the distance
+ unsigned char *v1 = data[y1] + (3 * x1);
+ unsigned char *v2 = data[y1] + (3 * x2);
+ unsigned char *v3 = data[y2] + (3 * x2);
+ unsigned char *v4 = data[y2] + (3 * x1);
+
+ const double w1 = 1/d1, w2 = 1/d2, w3 = 1/d3, w4 = 1/d4;
+
+ // GRG: Unrolled.
+
+ *(dst++) = (unsigned char)
+ ( (w1 * *(v1++) + w2 * *(v2++) +
+ w3 * *(v3++) + w4 * *(v4++)) /
+ (w1 + w2 + w3 + w4) );
+ *(dst++) = (unsigned char)
+ ( (w1 * *(v1++) + w2 * *(v2++) +
+ w3 * *(v3++) + w4 * *(v4++)) /
+ (w1 + w2 + w3 + w4) );
+ *(dst++) = (unsigned char)
+ ( (w1 * *v1 + w2 * *v2 +
+ w3 * *v3 + w4 * *v4) /
+ (w1 + w2 + w3 + w4) );
+
+ if (has_alpha)
+ {
+ v1 = alpha[y1] + (x1);
+ v2 = alpha[y1] + (x2);
+ v3 = alpha[y2] + (x2);
+ v4 = alpha[y2] + (x1);
+
+ *(alpha_dst++) = (unsigned char)
+ ( (w1 * *v1 + w2 * *v2 +
+ w3 * *v3 + w4 * *v4) /
+ (w1 + w2 + w3 + w4) );
+ }
+ }
+ }
+ else
+ {
+ *(dst++) = blank_r;
+ *(dst++) = blank_g;
+ *(dst++) = blank_b;
+
+ if (has_alpha)
+ *(alpha_dst++) = 0;
+ }
+ }
+ }
+ }
+ else // not interpolating
+ {
+ for (int y = 0; y < rH; y++)
+ {
+ for (int x = 0; x < rW; x++)
+ {
+ wxRealPoint src = wxRotatePoint (x + x1a, y + y1a, cos_angle, -sin_angle, p0);
+
+ const int xs = wxRound (src.x); // wxRound rounds to the
+ const int ys = wxRound (src.y); // closest integer
+
+ if (0 <= xs && xs < w && 0 <= ys && ys < h)
+ {
+ unsigned char *p = data[ys] + (3 * xs);
+ *(dst++) = *(p++);
+ *(dst++) = *(p++);
+ *(dst++) = *p;
+
+ if (has_alpha)
+ *(alpha_dst++) = *(alpha[ys] + (xs));
+ }
+ else
+ {
+ *(dst++) = blank_r;
+ *(dst++) = blank_g;
+ *(dst++) = blank_b;
+
+ if (has_alpha)
+ *(alpha_dst++) = 255;
+ }
+ }
+ }
+ }
+
+ delete [] data;
+ delete [] alpha;
+
+ return rotated;
+}
+
+
+
+
+
+// A module to allow wxImage initialization/cleanup
+// without calling these functions from app.cpp or from
+// the user's application.
+
+class wxImageModule: public wxModule
+{
+DECLARE_DYNAMIC_CLASS(wxImageModule)
+public:
+ wxImageModule() {}
+ bool OnInit() { wxImage::InitStandardHandlers(); return true; }
+ void OnExit() { wxImage::CleanUpHandlers(); }
+};
+
+IMPLEMENT_DYNAMIC_CLASS(wxImageModule, wxModule)
+
+
+#endif // wxUSE_IMAGE