]> git.saurik.com Git - wxWidgets.git/commitdiff
Added rotation to wxImage
authorJulian Smart <julian@anthemion.co.uk>
Sun, 6 Feb 2000 14:51:36 +0000 (14:51 +0000)
committerJulian Smart <julian@anthemion.co.uk>
Sun, 6 Feb 2000 14:51:36 +0000 (14:51 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@5872 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

13 files changed:
docs/latex/wx/image.tex
include/wx/image.h
samples/rotate/Makefile.in [new file with mode: 0644]
samples/rotate/kclub.bmp [new file with mode: 0644]
samples/rotate/makefile.b32 [new file with mode: 0644]
samples/rotate/makefile.g95 [new file with mode: 0644]
samples/rotate/makefile.unx [new file with mode: 0644]
samples/rotate/makefile.vc [new file with mode: 0644]
samples/rotate/makefile.wat [new file with mode: 0644]
samples/rotate/mondrian.ico [new file with mode: 0644]
samples/rotate/rotate.cpp [new file with mode: 0644]
samples/rotate/rotate.rc [new file with mode: 0644]
src/common/image.cpp

index ca43c0fbcc188d637c8adf4c02487ecf55a3f9cf..f913b2b6bac2e53f6cdff0397b2a47b7255dccdb 100644 (file)
@@ -505,6 +505,16 @@ Returns the (modified) image itself.
 
 \helpref{Scale}{wximagescale}
 
+\membersection{wxImage::Rotate}\label{wximagererotate}
+
+\func{wxImage}{Rotate}{\param{double}{ angle}, \param{const wxPoint\& }{rotationCentre},
+ \param{bool}{ interpolating = TRUE}, \param{wxPoint*}{ offsetAfterRotation = NULL}}
+
+Rotates the image about the given point, by {\it angle} radians. Passing TRUE
+to {\it interpolating} results in better image quality, but is slower.
+
+Returns the rotated image, leaving this image intact.
+
 \membersection{wxImage::Scale}\label{wximagescale}
 
 \constfunc{wxImage}{Scale}{\param{int}{ width}, \param{int}{ height}}
index 7f2b0904eac49eed8d4a68254f94e3d317e198d9..09aa6ed4a2899b47731ba0d27c2ff12009404db2 100644 (file)
@@ -117,6 +117,11 @@ public:
     // rescales the image in place
     wxImage& Rescale( int width, int height ) { return *this = Scale(width, height); }
 
+    // Rotates the image about the given point, 'angle' radians.
+    // Returns the rotated image, leaving this image intact.
+    wxImage Rotate(double angle, const wxPoint & centre_of_rotation,
+                   bool interpolating = TRUE, wxPoint * offset_after_rotation = (wxPoint*) NULL) const ;
+
     // replace one colour with another
     void Replace( unsigned char r1, unsigned char g1, unsigned char b1,
                   unsigned char r2, unsigned char g2, unsigned char b2 );
diff --git a/samples/rotate/Makefile.in b/samples/rotate/Makefile.in
new file mode 100644 (file)
index 0000000..a7c8e4f
--- /dev/null
@@ -0,0 +1,21 @@
+#
+# File:                makefile.unx
+# Author:      Julian Smart
+# Created:     1998
+# Updated:     
+# Copyright:   (c) 1998 Julian Smart
+#
+# "%W% %G%"
+#
+# Makefile for rotate example (UNIX).
+
+top_srcdir = @top_srcdir@/..
+top_builddir = ../..
+program_dir = samples/rotate
+
+PROGRAM=rotate
+
+OBJECTS=$(PROGRAM).o
+
+include ../../src/makeprog.env
+
diff --git a/samples/rotate/kclub.bmp b/samples/rotate/kclub.bmp
new file mode 100644 (file)
index 0000000..2a4aa03
Binary files /dev/null and b/samples/rotate/kclub.bmp differ
diff --git a/samples/rotate/makefile.b32 b/samples/rotate/makefile.b32
new file mode 100644 (file)
index 0000000..729b634
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# File:                makefile.b32
+# Author:      Julian Smart
+# Created:     1999
+# Updated:     
+# Copyright:
+#
+# Makefile : Builds sample for 32-bit BC++
+
+WXDIR = $(WXWIN)
+
+TARGET=rotate
+OBJECTS = $(TARGET).obj
+
+!include $(WXDIR)\src\makeprog.b32
+
diff --git a/samples/rotate/makefile.g95 b/samples/rotate/makefile.g95
new file mode 100644 (file)
index 0000000..9fdaa4b
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# File:         makefile.g95
+# Author:       Julian Smart
+# Created:      1999
+# Updated:
+# Copyright:    (c) Julian Smart, 1999
+#
+# Makefile for wxWindows sample (Cygwin/Mingw32).
+
+WXDIR = ../..
+
+TARGET=rotate
+OBJECTS = $(TARGET).o
+
+include $(WXDIR)/src/makeprog.g95
+
diff --git a/samples/rotate/makefile.unx b/samples/rotate/makefile.unx
new file mode 100644 (file)
index 0000000..b4e1b2e
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# File:                Makefile for samples
+# Author:      Robert Roebling
+# Created:     1999
+# Updated:     
+# Copyright:   (c) 1998 Robert Roebling
+#
+# This makefile requires a Unix version of wxWindows
+# to be installed on your system. This is most often
+# done typing "make install" when using the complete
+# sources of wxWindows or by installing the two
+# RPM packages wxGTK.XXX.rpm and wxGTK-devel.XXX.rpm
+# under Linux.
+#
+
+CC = gcc
+
+PROGRAM = rotate
+
+OBJECTS = $(PROGRAM).o
+
+# implementation
+
+.SUFFIXES:     .o .cpp
+
+.cpp.o :
+       $(CC) -c `wx-config --cflags` -o $@ $<
+
+all:    $(PROGRAM)
+
+$(PROGRAM):    $(OBJECTS)
+       $(CC) -o $(PROGRAM) $(OBJECTS) `wx-config --libs`
+
+clean: 
+       rm -f *.o $(PROGRAM)
diff --git a/samples/rotate/makefile.vc b/samples/rotate/makefile.vc
new file mode 100644 (file)
index 0000000..315ebbc
--- /dev/null
@@ -0,0 +1,18 @@
+#
+# File:                makefile.vc
+# Author:      Julian Smart
+# Created:     1999
+# Updated:     
+# Copyright:   (c) Julian Smart
+#
+# Makefile : Builds sample (VC++, WIN32)
+# Use FINAL=1 argument to nmake to build final version with no debug info.
+
+# Set WXDIR for your system
+WXDIR = $(WXWIN)
+
+PROGRAM=rotate
+OBJECTS = $(PROGRAM).obj
+
+!include $(WXDIR)\src\makeprog.vc
+
diff --git a/samples/rotate/makefile.wat b/samples/rotate/makefile.wat
new file mode 100644 (file)
index 0000000..ca68b34
--- /dev/null
@@ -0,0 +1,15 @@
+#
+# Makefile for WATCOM
+#
+# Created by Julian Smart, January 1999
+# 
+#
+
+WXDIR = $(%WXWIN)
+
+PROGRAM = rotate
+OBJECTS = $(PROGRAM).obj
+
+!include $(WXDIR)\src\makeprog.wat
+
+
diff --git a/samples/rotate/mondrian.ico b/samples/rotate/mondrian.ico
new file mode 100644 (file)
index 0000000..2310c5d
Binary files /dev/null and b/samples/rotate/mondrian.ico differ
diff --git a/samples/rotate/rotate.cpp b/samples/rotate/rotate.cpp
new file mode 100644 (file)
index 0000000..74b3f80
--- /dev/null
@@ -0,0 +1,120 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:      test.cpp
+// Purpose:   Image rotation test
+// Author:    Carlos Moreno
+// Modified by:
+// Created:   6/2/2000
+// RCS-ID:    $Id$
+// Copyright: (c) 2000
+// Licence:   wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#include "wx/image.h"
+
+class MyApp: public wxApp
+{
+    virtual bool OnInit();
+};
+
+
+class MyFrame: public wxFrame
+{
+public:
+    MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
+
+    void OnQuit (wxCommandEvent &);
+    void OnMouseLeftUp (wxMouseEvent & event);
+    void OnMouseRightUp (wxMouseEvent & event);
+
+private:
+    DECLARE_EVENT_TABLE()
+};
+
+enum
+{
+    ID_Quit = 1
+};
+
+BEGIN_EVENT_TABLE(MyFrame, wxFrame)
+    EVT_MENU (ID_Quit, MyFrame::OnQuit)
+    EVT_LEFT_UP (MyFrame::OnMouseLeftUp)
+    EVT_RIGHT_UP (MyFrame::OnMouseRightUp)
+END_EVENT_TABLE()
+
+IMPLEMENT_APP(MyApp)
+
+
+bool MyApp::OnInit()
+{
+    MyFrame *frame = new MyFrame ("wxWindows Skeleton", wxPoint(20,20), wxSize(600,450));
+
+    frame->SetBackgroundColour (wxColour (0,80,60));
+
+    frame->Show (TRUE);
+    SetTopWindow (frame);
+    return TRUE;
+}
+
+MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
+    : wxFrame((wxFrame *)NULL, -1, title, pos, size)
+{
+    wxMenu *menuFile = new wxMenu;
+    menuFile->Append (ID_Quit, "E&xit");
+
+    wxMenuBar *menuBar = new wxMenuBar;
+    menuBar->Append (menuFile, "&File");
+
+    SetMenuBar (menuBar);
+}
+
+void MyFrame::OnQuit (wxCommandEvent &)
+{
+    Close (TRUE);
+}
+
+
+// Rotate with interpolation and with offset correction
+void MyFrame::OnMouseLeftUp (wxMouseEvent & event)
+{
+    static double angle = 0.1;
+    const double pi = 3.14159265359;
+
+    wxImage img ("kclub.bmp", wxBITMAP_TYPE_BMP);
+
+    wxPoint offset;
+    wxImage img2 = img.Rotate(angle, wxPoint(img.GetWidth()/2, img.GetHeight()/2), TRUE, &offset);
+    angle += 0.05;
+
+    wxBitmap bmp = img2.ConvertToBitmap ();
+
+    wxClientDC dc (this);
+    dc.DrawBitmap (bmp, event.m_x + offset.x, event.m_y + offset.y);
+
+    return;
+}
+
+// without interpolation, and without offset correction
+void MyFrame::OnMouseRightUp (wxMouseEvent & event)
+{
+    static double angle = 0.1;
+    const double pi = 3.14159265359;
+
+    wxImage img ("kclub.bmp", wxBITMAP_TYPE_BMP);
+
+    wxImage img2 = img.Rotate(angle, wxPoint(img.GetWidth()/2, img.GetHeight()/2), FALSE);
+    angle += 0.05;
+
+    wxBitmap bmp = img2.ConvertToBitmap ();
+
+    wxClientDC dc (this);
+    dc.DrawBitmap (bmp, event.m_x, event.m_y);
+
+    return;
+}
diff --git a/samples/rotate/rotate.rc b/samples/rotate/rotate.rc
new file mode 100644 (file)
index 0000000..7655c62
--- /dev/null
@@ -0,0 +1,3 @@
+mondrian ICON "mondrian.ico"
+#include "wx/msw/wx.rc"
+
index 58dec0c3b65742abf452d68a6a371191db29235f..f73c76bfaef73f16552839f2f291452eb220da63 100644 (file)
@@ -30,6 +30,7 @@
 
 // For memcpy
 #include <string.h>
+#include <math.h>
 
 #ifdef __SALFORDC__
     #undef FAR
@@ -2682,4 +2683,198 @@ unsigned long wxImage::ComputeHistogram( wxHashTable &h )
     return nentries;
 }
 
+/*
+ * Rotation code by Carlos Moreno
+ */
+
+struct wxRotationPixel
+{
+    unsigned char rgb[3];
+};
+
+struct wxRotationPoint
+{
+       wxRotationPoint (double _x, double _y) : x(_x), y(_y) {}
+       wxRotationPoint (const wxPoint & p) : x(p.x), y(p.y) {}
+       double x, y;
+};
+
+static const wxRotationPixel gs_BlankPixel = {0,0,0};
+static const double gs_Epsilon = 1e-10;
+
+static inline int wxCint (double x)
+{
+    return (x > 0) ? (int) (x + 0.5) : (int) (x - 0.5);
+}
+
+
+// Auxiliary function to rotate a point (x,y) with respect to point p0
+// make it inline and use a straight return to facilitate optimization
+// also, the function receives the sine and cosine of the angle to avoid
+// repeating the time-consuming calls to these functions -- sin/cos can
+// be computed and stored in the calling function.
+
+inline wxRotationPoint rotated_point (const wxRotationPoint & p, double cos_angle, double sin_angle, const wxRotationPoint & p0)
+{
+    return wxRotationPoint (p0.x + (p.x - p0.x) * cos_angle - (p.y - p0.y) * sin_angle,
+                  p0.y + (p.y - p0.y) * cos_angle + (p.x - p0.x) * sin_angle);
+}
+
+inline wxRotationPoint rotated_point (double x, double y, double cos_angle, double sin_angle, const wxRotationPoint & p0)
+{
+    return rotated_point (wxRotationPoint(x,y), cos_angle, sin_angle, p0);
+}
+
+wxImage wxImage::Rotate(double angle, const wxPoint & centre_of_rotation, bool interpolating, wxPoint * offset_after_rotation) const
+{
+    const wxImage& img = * this;
+    int i;
+    angle = -angle;     // screen coordinates are a mirror image of "real" coordinates
+
+        // Create pointer-based array to accelerate access to wxImage's data
+    wxRotationPixel ** data = new wxRotationPixel * [img.GetHeight()];
+
+    data[0] = (wxRotationPixel *) img.GetData();
+
+    for (i = 1; i < img.GetHeight(); i++)
+    {
+        data[i] = data[i - 1] + img.GetWidth();
+    }
+
+        // pre-compute coefficients for rotation formula (sine and cosine of the angle)
+    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 wxRotationPoint & p0 = centre_of_rotation;
+
+    wxRotationPoint p1 = rotated_point (0, 0, cos_angle, sin_angle, p0);
+    wxRotationPoint p2 = rotated_point (0, img.GetHeight(), cos_angle, sin_angle, p0);
+    wxRotationPoint p3 = rotated_point (img.GetWidth(), 0, cos_angle, sin_angle, p0);
+    wxRotationPoint p4 = rotated_point (img.GetWidth(), img.GetHeight(), cos_angle, sin_angle, p0);
+
+    int x1 = floor (min (min(p1.x, p2.x), min(p3.x, p4.x)));
+    int y1 = floor (min (min(p1.y, p2.y), min(p3.y, p4.y)));
+
+    int x2 = ceil (max (max(p1.x, p2.x), max(p3.x, p4.x)));
+    int y2 = ceil (max (max(p1.y, p2.y), max(p3.y, p4.y)));
+
+    wxImage rotated (x2 - x1 + 1, y2 - y1 + 1);
+
+    if (offset_after_rotation != NULL)
+    {
+       *offset_after_rotation = wxPoint (x1, y1);
+    }
+
+
+    wxRotationPixel ** result_data = new wxRotationPixel * [rotated.GetHeight()];
+
+    result_data[0] = (wxRotationPixel *) rotated.GetData();
+
+    for (i = 1; i < rotated.GetHeight(); i++)
+    {
+        result_data[i] = result_data[i - 1] + rotated.GetWidth();
+    }
+
+        // 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
+
+    int x;
+    for (x = 0; x < rotated.GetWidth(); x++)
+    {
+        for (int y = 0; y < rotated.GetHeight(); y++)
+        {
+            wxRotationPoint src = rotated_point (x + x1, y + y1, cos_angle, -sin_angle, p0);
+
+            if (interpolating)
+            {
+                if (0 < src.x && src.x < img.GetWidth() - 1 &&
+                    0 < src.y && src.y < img.GetHeight() - 1)
+                {
+                        // interpolate using the 4 enclosing grid-points.  Those
+                        // points can be obtained using floor and ceiling of the
+                        // exact coordinates of the point
+
+                    const int x1 = wxCint(floor(src.x));
+                    const int y1 = wxCint(floor(src.y));
+                    const int x2 = wxCint(ceil(src.x));
+                    const int y2 = wxCint(ceil(src.y));
+
+                        // get four points and the distances (square of the distance,
+                        // for efficiency reasons) for the interpolation formula
+                    const wxRotationPixel & v1 = data[y1][x1];
+                    const wxRotationPixel & v2 = data[y1][x2];
+                    const wxRotationPixel & v3 = data[y2][x2];
+                    const wxRotationPixel & v4 = data[y2][x1];
+
+                    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
+
+                    if (d1 < gs_Epsilon)               // d1,d2,d3,d4 are positive -- no need for abs()
+                    {
+                       result_data[y][x] = v1;
+                    }
+                    else if (d2 < gs_Epsilon)
+                    {
+                       result_data[y][x] = v2;
+                    }
+                    else if (d3 < gs_Epsilon)
+                    {
+                       result_data[y][x] = v3;
+                    }
+                    else if (d4 < gs_Epsilon)
+                    {
+                       result_data[y][x] = v4;
+                    }
+                    else
+                    {
+                               // weights for the weighted average are proportional to the inverse of the distance
+                       const w1 = 1/d1, w2 = 1/d2, w3 = 1/d3, w4 = 1/d4;
+                       
+                           for (int i = 0; i < 3; i++)     // repeat calculation for R, G, and B
+                       {
+                               result_data[y][x].rgb[i] =
+                               static_cast<unsigned char> ( (w1 * v1.rgb[i] + w2 * v2.rgb[i] +
+                                                                w3 * v3.rgb[i] + w4 * v4.rgb[i]) /
+                                                            (w1 + w2 + w3 + w4) );
+                           }
+                    }
+                }
+                else
+                {
+                    result_data[y][x] = gs_BlankPixel;
+                }
+            }
+            else
+            {
+                const int & xs = wxCint (src.x);      // wxCint performs rounding to the
+                const int & ys = wxCint (src.y);      // closest integer
+
+                if (0 <= xs && xs < img.GetWidth() &&
+                    0 <= ys && ys < img.GetHeight())
+                {
+                    result_data[y][x] = data[ys][xs];
+                }
+                else
+                {
+                    result_data[y][x] = gs_BlankPixel;
+                }
+            }
+        }
+    }
+
+    return rotated;
+}