Added rotation to wxImage

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@5872 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Julian Smart 2000-02-06 14:51:36 +00:00
parent 12c1b46a2f
commit 7a632f1056
13 changed files with 454 additions and 0 deletions

View File

@ -505,6 +505,16 @@ Returns the (modified) image itself.
\helpref{Scale}{wximagescale} \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} \membersection{wxImage::Scale}\label{wximagescale}
\constfunc{wxImage}{Scale}{\param{int}{ width}, \param{int}{ height}} \constfunc{wxImage}{Scale}{\param{int}{ width}, \param{int}{ height}}

View File

@ -117,6 +117,11 @@ public:
// rescales the image in place // rescales the image in place
wxImage& Rescale( int width, int height ) { return *this = Scale(width, height); } 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 // replace one colour with another
void Replace( unsigned char r1, unsigned char g1, unsigned char b1, void Replace( unsigned char r1, unsigned char g1, unsigned char b1,
unsigned char r2, unsigned char g2, unsigned char b2 ); unsigned char r2, unsigned char g2, unsigned char b2 );

View File

@ -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

BIN
samples/rotate/kclub.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

BIN
samples/rotate/mondrian.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

120
samples/rotate/rotate.cpp Normal file
View File

@ -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;
}

3
samples/rotate/rotate.rc Normal file
View File

@ -0,0 +1,3 @@
mondrian ICON "mondrian.ico"
#include "wx/msw/wx.rc"

View File

@ -30,6 +30,7 @@
// For memcpy // For memcpy
#include <string.h> #include <string.h>
#include <math.h>
#ifdef __SALFORDC__ #ifdef __SALFORDC__
#undef FAR #undef FAR
@ -2682,4 +2683,198 @@ unsigned long wxImage::ComputeHistogram( wxHashTable &h )
return nentries; 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;
}