]> git.saurik.com Git - wxWidgets.git/blame - src/common/image.cpp
wxString::FormatV() fix (bug 416853)
[wxWidgets.git] / src / common / image.cpp
CommitLineData
01111366
RR
1/////////////////////////////////////////////////////////////////////////////
2// Name: image.cpp
3// Purpose: wxImage
4// Author: Robert Roebling
5// RCS-ID: $Id$
6// Copyright: (c) Robert Roebling
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10#ifdef __GNUG__
11#pragma implementation "image.h"
12#endif
13
0b4f9ee3
UU
14// For compilers that support precompilation, includes "wx.h".
15#include "wx/wxprec.h"
16
17#ifdef __BORLANDC__
edccf428 18 #pragma hdrstop
0b4f9ee3
UU
19#endif
20
01111366 21#include "wx/image.h"
99c67c77 22#include "wx/bitmap.h"
01111366
RR
23#include "wx/debug.h"
24#include "wx/log.h"
f6fcbb63 25#include "wx/app.h"
dc86cb34 26#include "wx/filefn.h"
3d05544e 27#include "wx/wfstream.h"
b75867a6 28#include "wx/intl.h"
a91b47e8 29#include "wx/module.h"
01111366 30
58a8ab88
JS
31// For memcpy
32#include <string.h>
7a632f10 33#include <math.h>
58a8ab88 34
a3ef5bf5 35#ifdef __SALFORDC__
edccf428 36 #undef FAR
a3ef5bf5
JS
37#endif
38
2432b92d 39
01111366
RR
40//-----------------------------------------------------------------------------
41// wxImage
42//-----------------------------------------------------------------------------
43
44class wxImageRefData: public wxObjectRefData
45{
fd0eed64 46public:
edccf428
VZ
47 wxImageRefData();
48 ~wxImageRefData();
c7abc967 49
dbda9e86
JS
50 int m_width;
51 int m_height;
52 unsigned char *m_data;
53 bool m_hasMask;
54 unsigned char m_maskRed,m_maskGreen,m_maskBlue;
55 bool m_ok;
f6bcfd97 56 bool m_static;
5e5437e0
JS
57 wxPalette m_palette;
58 wxArrayString m_optionNames;
59 wxArrayString m_optionValues;
01111366
RR
60};
61
edccf428 62wxImageRefData::wxImageRefData()
01111366 63{
fd0eed64
RR
64 m_width = 0;
65 m_height = 0;
66 m_data = (unsigned char*) NULL;
67 m_ok = FALSE;
68 m_maskRed = 0;
69 m_maskGreen = 0;
70 m_maskBlue = 0;
71 m_hasMask = FALSE;
f6bcfd97 72 m_static = FALSE;
01111366
RR
73}
74
edccf428 75wxImageRefData::~wxImageRefData()
01111366 76{
f6bcfd97 77 if (m_data && !m_static)
58c837a4 78 free( m_data );
01111366
RR
79}
80
81wxList wxImage::sm_handlers;
82
fec19ea9
VS
83wxImage wxNullImage;
84
01111366
RR
85//-----------------------------------------------------------------------------
86
87#define M_IMGDATA ((wxImageRefData *)m_refData)
88
5e5437e0 89IMPLEMENT_DYNAMIC_CLASS(wxImage, wxObject)
01111366
RR
90
91wxImage::wxImage()
92{
93}
94
95wxImage::wxImage( int width, int height )
96{
fd0eed64 97 Create( width, height );
01111366
RR
98}
99
f6bcfd97
BP
100wxImage::wxImage( int width, int height, unsigned char* data, bool static_data )
101{
102 Create( width, height, data, static_data );
103}
104
01111366
RR
105wxImage::wxImage( const wxString& name, long type )
106{
fd0eed64 107 LoadFile( name, type );
01111366
RR
108}
109
9e9ee68e
VS
110wxImage::wxImage( const wxString& name, const wxString& mimetype )
111{
112 LoadFile( name, mimetype );
113}
114
e02afc7a 115#if wxUSE_STREAMS
3d05544e
JS
116wxImage::wxImage( wxInputStream& stream, long type )
117{
118 LoadFile( stream, type );
119}
9e9ee68e
VS
120
121wxImage::wxImage( wxInputStream& stream, const wxString& mimetype )
122{
123 LoadFile( stream, mimetype );
124}
e02afc7a 125#endif // wxUSE_STREAMS
3d05544e 126
4698648f
VZ
127wxImage::wxImage( const wxImage& image )
128{
129 Ref(image);
01111366
RR
130}
131
4698648f
VZ
132wxImage::wxImage( const wxImage* image )
133{
134 if (image) Ref(*image);
01111366
RR
135}
136
137void wxImage::Create( int width, int height )
138{
aadaf841
GRG
139 UnRef();
140
fd0eed64 141 m_refData = new wxImageRefData();
c7abc967 142
fd0eed64
RR
143 M_IMGDATA->m_data = (unsigned char *) malloc( width*height*3 );
144 if (M_IMGDATA->m_data)
145 {
146 for (int l = 0; l < width*height*3; l++) M_IMGDATA->m_data[l] = 0;
c7abc967 147
fd0eed64
RR
148 M_IMGDATA->m_width = width;
149 M_IMGDATA->m_height = height;
150 M_IMGDATA->m_ok = TRUE;
151 }
152 else
153 {
154 UnRef();
155 }
01111366
RR
156}
157
f6bcfd97
BP
158void wxImage::Create( int width, int height, unsigned char* data, bool static_data )
159{
160 UnRef();
161
162 m_refData = new wxImageRefData();
163
164 M_IMGDATA->m_data = data;
165 if (M_IMGDATA->m_data)
166 {
167 M_IMGDATA->m_width = width;
168 M_IMGDATA->m_height = height;
169 M_IMGDATA->m_ok = TRUE;
170 M_IMGDATA->m_static = static_data;
171 }
172 else
173 {
174 UnRef();
175 }
176}
177
01111366
RR
178void wxImage::Destroy()
179{
fd0eed64 180 UnRef();
01111366
RR
181}
182
f6bcfd97
BP
183wxImage wxImage::Copy() const
184{
185 wxImage image;
186
187 wxCHECK_MSG( Ok(), image, wxT("invalid image") );
188
189 image.Create( M_IMGDATA->m_width, M_IMGDATA->m_height );
190
191 char unsigned *data = image.GetData();
192
193 wxCHECK_MSG( data, image, wxT("unable to create image") );
194
195 if (M_IMGDATA->m_hasMask)
196 image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
197
198 memcpy( data, GetData(), M_IMGDATA->m_width*M_IMGDATA->m_height*3 );
199
200 return image;
201}
202
ce9a75d2 203wxImage wxImage::Scale( int width, int height ) const
4bc67cc5
RR
204{
205 wxImage image;
c7abc967 206
223d09f6 207 wxCHECK_MSG( Ok(), image, wxT("invalid image") );
c7abc967 208
223d09f6 209 wxCHECK_MSG( (width > 0) && (height > 0), image, wxT("invalid image size") );
c7abc967 210
4bc67cc5 211 image.Create( width, height );
c7abc967 212
4bc67cc5 213 char unsigned *data = image.GetData();
c7abc967 214
223d09f6 215 wxCHECK_MSG( data, image, wxT("unable to create image") );
c7abc967 216
4bc67cc5
RR
217 if (M_IMGDATA->m_hasMask)
218 image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
c7abc967 219
6e13c196
RR
220 long old_height = M_IMGDATA->m_height;
221 long old_width = M_IMGDATA->m_width;
c7abc967 222
6e13c196
RR
223 char unsigned *source_data = M_IMGDATA->m_data;
224 char unsigned *target_data = data;
c7abc967 225
6e13c196 226 for (long j = 0; j < height; j++)
4bc67cc5 227 {
6e13c196 228 long y_offset = (j * old_height / height) * old_width;
c7abc967 229
6e13c196 230 for (long i = 0; i < width; i++)
4698648f 231 {
c7abc967
VZ
232 memcpy( target_data,
233 source_data + 3*(y_offset + ((i * old_width )/ width)),
dbda9e86 234 3 );
6e13c196 235 target_data += 3;
4698648f 236 }
4bc67cc5 237 }
c7abc967 238
4bc67cc5
RR
239 return image;
240}
4698648f 241
f6bcfd97
BP
242wxImage wxImage::Rotate90( bool clockwise ) const
243{
244 wxImage image;
245
246 wxCHECK_MSG( Ok(), image, wxT("invalid image") );
247
248 image.Create( M_IMGDATA->m_height, M_IMGDATA->m_width );
249
250 char unsigned *data = image.GetData();
251
252 wxCHECK_MSG( data, image, wxT("unable to create image") );
253
254 if (M_IMGDATA->m_hasMask)
255 image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
256
257 long height = M_IMGDATA->m_height;
258 long width = M_IMGDATA->m_width;
259
260 char unsigned *source_data = M_IMGDATA->m_data;
261 char unsigned *target_data;
262
263 for (long j = 0; j < height; j++)
264 {
265 for (long i = 0; i < width; i++)
266 {
267 if (clockwise)
268 target_data = data + (((i+1)*height) - j - 1)*3;
269 else
270 target_data = data + ((height*(width-1)) + j - (i*height))*3;
271 memcpy( target_data, source_data, 3 );
272 source_data += 3;
273 }
274 }
275
276 return image;
277}
278
279wxImage wxImage::Mirror( bool horizontally ) const
280{
281 wxImage image;
282
283 wxCHECK_MSG( Ok(), image, wxT("invalid image") );
284
285 image.Create( M_IMGDATA->m_width, M_IMGDATA->m_height );
286
287 char unsigned *data = image.GetData();
288
289 wxCHECK_MSG( data, image, wxT("unable to create image") );
290
291 if (M_IMGDATA->m_hasMask)
292 image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
293
294 long height = M_IMGDATA->m_height;
295 long width = M_IMGDATA->m_width;
296
297 char unsigned *source_data = M_IMGDATA->m_data;
298 char unsigned *target_data;
299
300 if (horizontally)
301 {
302 for (long j = 0; j < height; j++)
303 {
304 data += width*3;
305 target_data = data-3;
306 for (long i = 0; i < width; i++)
307 {
308 memcpy( target_data, source_data, 3 );
309 source_data += 3;
310 target_data -= 3;
311 }
312 }
313 }
314 else
315 {
316 for (long i = 0; i < height; i++)
317 {
318 target_data = data + 3*width*(height-1-i);
3ca6a5f0 319 memcpy( target_data, source_data, (size_t)3*width );
f6bcfd97
BP
320 source_data += 3*width;
321 }
322 }
323
324 return image;
325}
326
7b2471a0
SB
327wxImage wxImage::GetSubImage( const wxRect &rect ) const
328{
329 wxImage image;
330
223d09f6 331 wxCHECK_MSG( Ok(), image, wxT("invalid image") );
7b2471a0 332
58c837a4
RR
333 wxCHECK_MSG( (rect.GetLeft()>=0) && (rect.GetTop()>=0) && (rect.GetRight()<=GetWidth()) && (rect.GetBottom()<=GetHeight()),
334 image, wxT("invalid subimage size") );
7b2471a0
SB
335
336 int subwidth=rect.GetWidth();
337 const int subheight=rect.GetHeight();
338
339 image.Create( subwidth, subheight );
340
341 char unsigned *subdata = image.GetData(), *data=GetData();
342
223d09f6 343 wxCHECK_MSG( subdata, image, wxT("unable to create image") );
7b2471a0
SB
344
345 if (M_IMGDATA->m_hasMask)
346 image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
347
348 const int subleft=3*rect.GetLeft();
349 const int width=3*GetWidth();
350 subwidth*=3;
995612e2 351
7b2471a0
SB
352 data+=rect.GetTop()*width+subleft;
353
354 for (long j = 0; j < subheight; ++j)
355 {
356 memcpy( subdata, data, subwidth);
357 subdata+=subwidth;
995612e2 358 data+=width;
7b2471a0
SB
359 }
360
361 return image;
362}
363
f6bcfd97
BP
364void wxImage::Paste( const wxImage &image, int x, int y )
365{
366 wxCHECK_RET( Ok(), wxT("invalid image") );
367 wxCHECK_RET( image.Ok(), wxT("invalid image") );
368
369 int xx = 0;
370 int yy = 0;
371 int width = image.GetWidth();
372 int height = image.GetHeight();
373
374 if (x < 0)
375 {
376 xx = -x;
377 width += x;
378 }
379 if (y < 0)
380 {
381 yy = -y;
382 height += y;
383 }
384
385 if ((x+xx)+width > M_IMGDATA->m_width)
386 width = M_IMGDATA->m_width - (x+xx);
387 if ((y+yy)+height > M_IMGDATA->m_height)
388 height = M_IMGDATA->m_height - (y+yy);
389
390 if (width < 1) return;
391 if (height < 1) return;
392
393 if ((!HasMask() && !image.HasMask()) ||
394 ((HasMask() && image.HasMask() &&
395 (GetMaskRed()==image.GetMaskRed()) &&
396 (GetMaskGreen()==image.GetMaskGreen()) &&
397 (GetMaskBlue()==image.GetMaskBlue()))))
398 {
399 width *= 3;
400 unsigned char* source_data = image.GetData() + xx*3 + yy*3*image.GetWidth();
401 int source_step = image.GetWidth()*3;
402
403 unsigned char* target_data = GetData() + (x+xx)*3 + (y+yy)*3*M_IMGDATA->m_width;
404 int target_step = M_IMGDATA->m_width*3;
405 for (int j = 0; j < height; j++)
406 {
407 memcpy( target_data, source_data, width );
408 source_data += source_step;
409 target_data += target_step;
410 }
aa21b509 411 return;
f6bcfd97 412 }
aa21b509
RR
413
414 if (!HasMask() && image.HasMask())
f6bcfd97 415 {
aa21b509
RR
416 unsigned char r = image.GetMaskRed();
417 unsigned char g = image.GetMaskGreen();
418 unsigned char b = image.GetMaskBlue();
419
420 width *= 3;
421 unsigned char* source_data = image.GetData() + xx*3 + yy*3*image.GetWidth();
422 int source_step = image.GetWidth()*3;
423
424 unsigned char* target_data = GetData() + (x+xx)*3 + (y+yy)*3*M_IMGDATA->m_width;
425 int target_step = M_IMGDATA->m_width*3;
426
427 for (int j = 0; j < height; j++)
428 {
429 for (int i = 0; i < width; i+=3)
430 {
431 if ((source_data[i] != r) &&
432 (source_data[i+1] != g) &&
433 (source_data[i+2] != b))
434 {
435 memcpy( target_data+i, source_data+i, 3 );
436 }
437 }
438 source_data += source_step;
439 target_data += target_step;
440 }
f6bcfd97
BP
441 }
442}
443
be25e480
RR
444void wxImage::Replace( unsigned char r1, unsigned char g1, unsigned char b1,
445 unsigned char r2, unsigned char g2, unsigned char b2 )
446{
447 wxCHECK_RET( Ok(), wxT("invalid image") );
448
449 char unsigned *data = GetData();
06b466c7 450
be25e480
RR
451 const int w = GetWidth();
452 const int h = GetHeight();
453
454 for (int j = 0; j < h; j++)
455 for (int i = 0; i < w; i++)
069d0f27
VZ
456 {
457 if ((data[0] == r1) && (data[1] == g1) && (data[2] == b1))
458 {
459 data[0] = r2;
460 data[1] = g2;
461 data[2] = b2;
462 }
463 data += 3;
464 }
be25e480
RR
465}
466
f515c25a 467wxImage wxImage::ConvertToMono( unsigned char r, unsigned char g, unsigned char b ) const
fec19ea9
VS
468{
469 wxImage image;
470
471 wxCHECK_MSG( Ok(), image, wxT("invalid image") );
472
473 image.Create( M_IMGDATA->m_width, M_IMGDATA->m_height );
474
475 char unsigned *data = image.GetData();
476
477 wxCHECK_MSG( data, image, wxT("unable to create image") );
478
479 if (M_IMGDATA->m_hasMask)
480 {
481 if (M_IMGDATA->m_maskRed == r && M_IMGDATA->m_maskGreen == g &&
482 M_IMGDATA->m_maskBlue == b)
483 image.SetMaskColour( 255, 255, 255 );
484 else
485 image.SetMaskColour( 0, 0, 0 );
486 }
487
488 long size = M_IMGDATA->m_height * M_IMGDATA->m_width;
489
490 char unsigned *srcd = M_IMGDATA->m_data;
491 char unsigned *tard = image.GetData();
492
493 for ( long i = 0; i < size; i++, srcd += 3, tard += 3 )
494 {
495 if (srcd[0] == r && srcd[1] == g && srcd[2] == b)
496 tard[0] = tard[1] = tard[2] = 255;
497 else
498 tard[0] = tard[1] = tard[2] = 0;
499 }
500
501 return image;
502}
503
ef539066
RR
504void wxImage::SetRGB( int x, int y, unsigned char r, unsigned char g, unsigned char b )
505{
223d09f6 506 wxCHECK_RET( Ok(), wxT("invalid image") );
c7abc967 507
ef539066
RR
508 int w = M_IMGDATA->m_width;
509 int h = M_IMGDATA->m_height;
c7abc967 510
223d09f6 511 wxCHECK_RET( (x>=0) && (y>=0) && (x<w) && (y<h), wxT("invalid image index") );
c7abc967 512
ef539066 513 long pos = (y * w + x) * 3;
c7abc967 514
ef539066
RR
515 M_IMGDATA->m_data[ pos ] = r;
516 M_IMGDATA->m_data[ pos+1 ] = g;
517 M_IMGDATA->m_data[ pos+2 ] = b;
518}
519
f6bcfd97 520unsigned char wxImage::GetRed( int x, int y ) const
ef539066 521{
223d09f6 522 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 523
ef539066
RR
524 int w = M_IMGDATA->m_width;
525 int h = M_IMGDATA->m_height;
c7abc967 526
223d09f6 527 wxCHECK_MSG( (x>=0) && (y>=0) && (x<w) && (y<h), 0, wxT("invalid image index") );
c7abc967 528
ef539066 529 long pos = (y * w + x) * 3;
c7abc967 530
ef539066
RR
531 return M_IMGDATA->m_data[pos];
532}
533
f6bcfd97 534unsigned char wxImage::GetGreen( int x, int y ) const
ef539066 535{
223d09f6 536 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 537
ef539066
RR
538 int w = M_IMGDATA->m_width;
539 int h = M_IMGDATA->m_height;
c7abc967 540
223d09f6 541 wxCHECK_MSG( (x>=0) && (y>=0) && (x<w) && (y<h), 0, wxT("invalid image index") );
c7abc967 542
ef539066 543 long pos = (y * w + x) * 3;
c7abc967 544
ef539066
RR
545 return M_IMGDATA->m_data[pos+1];
546}
547
f6bcfd97 548unsigned char wxImage::GetBlue( int x, int y ) const
ef539066 549{
223d09f6 550 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 551
ef539066
RR
552 int w = M_IMGDATA->m_width;
553 int h = M_IMGDATA->m_height;
c7abc967 554
223d09f6 555 wxCHECK_MSG( (x>=0) && (y>=0) && (x<w) && (y<h), 0, wxT("invalid image index") );
c7abc967 556
ef539066 557 long pos = (y * w + x) * 3;
c7abc967 558
ef539066
RR
559 return M_IMGDATA->m_data[pos+2];
560}
4698648f
VZ
561
562bool wxImage::Ok() const
563{
de8c48cf
VZ
564 // image of 0 width or height can't be considered ok - at least because it
565 // causes crashes in ConvertToBitmap() if we don't catch it in time
566 wxImageRefData *data = M_IMGDATA;
567 return data && data->m_ok && data->m_width && data->m_height;
01111366
RR
568}
569
570char unsigned *wxImage::GetData() const
571{
223d09f6 572 wxCHECK_MSG( Ok(), (char unsigned *)NULL, wxT("invalid image") );
c7abc967 573
fd0eed64 574 return M_IMGDATA->m_data;
01111366
RR
575}
576
58a8ab88 577void wxImage::SetData( char unsigned *data )
01111366 578{
223d09f6 579 wxCHECK_RET( Ok(), wxT("invalid image") );
58a8ab88 580
ed58dbea
RR
581 wxImageRefData *newRefData = new wxImageRefData();
582
583 newRefData->m_width = M_IMGDATA->m_width;
584 newRefData->m_height = M_IMGDATA->m_height;
585 newRefData->m_data = data;
586 newRefData->m_ok = TRUE;
587 newRefData->m_maskRed = M_IMGDATA->m_maskRed;
588 newRefData->m_maskGreen = M_IMGDATA->m_maskGreen;
589 newRefData->m_maskBlue = M_IMGDATA->m_maskBlue;
590 newRefData->m_hasMask = M_IMGDATA->m_hasMask;
995612e2 591
ed58dbea 592 UnRef();
995612e2 593
ed58dbea 594 m_refData = newRefData;
01111366
RR
595}
596
f6bcfd97
BP
597void wxImage::SetData( char unsigned *data, int new_width, int new_height )
598{
599 wxImageRefData *newRefData = new wxImageRefData();
600
601 if (m_refData)
602 {
603 newRefData->m_width = new_width;
604 newRefData->m_height = new_height;
605 newRefData->m_data = data;
606 newRefData->m_ok = TRUE;
607 newRefData->m_maskRed = M_IMGDATA->m_maskRed;
608 newRefData->m_maskGreen = M_IMGDATA->m_maskGreen;
609 newRefData->m_maskBlue = M_IMGDATA->m_maskBlue;
610 newRefData->m_hasMask = M_IMGDATA->m_hasMask;
611 }
612 else
613 {
614 newRefData->m_width = new_width;
615 newRefData->m_height = new_height;
616 newRefData->m_data = data;
617 newRefData->m_ok = TRUE;
618 }
619
620 UnRef();
621
622 m_refData = newRefData;
623}
624
01111366
RR
625void wxImage::SetMaskColour( unsigned char r, unsigned char g, unsigned char b )
626{
223d09f6 627 wxCHECK_RET( Ok(), wxT("invalid image") );
c7abc967 628
fd0eed64
RR
629 M_IMGDATA->m_maskRed = r;
630 M_IMGDATA->m_maskGreen = g;
631 M_IMGDATA->m_maskBlue = b;
632 M_IMGDATA->m_hasMask = TRUE;
01111366
RR
633}
634
635unsigned char wxImage::GetMaskRed() const
636{
223d09f6 637 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 638
fd0eed64 639 return M_IMGDATA->m_maskRed;
01111366
RR
640}
641
642unsigned char wxImage::GetMaskGreen() const
643{
223d09f6 644 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 645
fd0eed64 646 return M_IMGDATA->m_maskGreen;
01111366
RR
647}
648
649unsigned char wxImage::GetMaskBlue() const
650{
223d09f6 651 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 652
fd0eed64 653 return M_IMGDATA->m_maskBlue;
01111366 654}
4698648f 655
01111366
RR
656void wxImage::SetMask( bool mask )
657{
223d09f6 658 wxCHECK_RET( Ok(), wxT("invalid image") );
c7abc967 659
fd0eed64 660 M_IMGDATA->m_hasMask = mask;
01111366
RR
661}
662
663bool wxImage::HasMask() const
664{
223d09f6 665 wxCHECK_MSG( Ok(), FALSE, wxT("invalid image") );
c7abc967 666
fd0eed64 667 return M_IMGDATA->m_hasMask;
01111366
RR
668}
669
4698648f
VZ
670int wxImage::GetWidth() const
671{
223d09f6 672 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 673
4698648f 674 return M_IMGDATA->m_width;
01111366
RR
675}
676
4698648f
VZ
677int wxImage::GetHeight() const
678{
223d09f6 679 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 680
4698648f 681 return M_IMGDATA->m_height;
01111366
RR
682}
683
5e5437e0
JS
684// Palette functions
685
686bool wxImage::HasPalette() const
687{
688 if (!Ok())
689 return FALSE;
690
691 return M_IMGDATA->m_palette.Ok();
692}
693
694const wxPalette& wxImage::GetPalette() const
695{
696 wxCHECK_MSG( Ok(), wxNullPalette, wxT("invalid image") );
697
698 return M_IMGDATA->m_palette;
699}
700
701void wxImage::SetPalette(const wxPalette& palette)
702{
703 wxCHECK_RET( Ok(), wxT("invalid image") );
704
705 M_IMGDATA->m_palette = palette;
706}
707
708// Option functions (arbitrary name/value mapping)
709void wxImage::SetOption(const wxString& name, const wxString& value)
710{
711 wxCHECK_RET( Ok(), wxT("invalid image") );
712
713 int idx = M_IMGDATA->m_optionNames.Index(name, FALSE);
714 if (idx == wxNOT_FOUND)
715 {
716 M_IMGDATA->m_optionNames.Add(name);
717 M_IMGDATA->m_optionValues.Add(value);
718 }
719 else
720 {
721 M_IMGDATA->m_optionNames[idx] = name;
722 M_IMGDATA->m_optionValues[idx] = value;
723 }
724}
725
726void wxImage::SetOption(const wxString& name, int value)
727{
728 wxString valStr;
729 valStr.Printf(wxT("%d"), value);
730 SetOption(name, valStr);
731}
732
733wxString wxImage::GetOption(const wxString& name) const
734{
735 wxCHECK_MSG( Ok(), wxEmptyString, wxT("invalid image") );
736
737 int idx = M_IMGDATA->m_optionNames.Index(name, FALSE);
738 if (idx == wxNOT_FOUND)
739 return wxEmptyString;
740 else
741 return M_IMGDATA->m_optionValues[idx];
742}
743
744int wxImage::GetOptionInt(const wxString& name) const
745{
746 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
747
748 return wxAtoi(GetOption(name));
749}
750
751bool wxImage::HasOption(const wxString& name) const
752{
753 wxCHECK_MSG( Ok(), FALSE, wxT("invalid image") );
754
755 return (M_IMGDATA->m_optionNames.Index(name, FALSE) != wxNOT_FOUND);
756}
757
01111366
RR
758bool wxImage::LoadFile( const wxString& filename, long type )
759{
e02afc7a 760#if wxUSE_STREAMS
3d05544e 761 if (wxFileExists(filename))
fd0eed64 762 {
3d05544e 763 wxFileInputStream stream(filename);
069d0f27 764 wxBufferedInputStream bstream( stream );
1b055864 765 return LoadFile(bstream, type);
3d05544e 766 }
97fdfcc9 767 else
58c837a4
RR
768 {
769 wxLogError( _("Can't load image from file '%s': file does not exist."), filename.c_str() );
9e9ee68e
VS
770
771 return FALSE;
772 }
773#else // !wxUSE_STREAMS
774 return FALSE;
775#endif // wxUSE_STREAMS
776}
777
778bool wxImage::LoadFile( const wxString& filename, const wxString& mimetype )
779{
780#if wxUSE_STREAMS
781 if (wxFileExists(filename))
782 {
783 wxFileInputStream stream(filename);
069d0f27 784 wxBufferedInputStream bstream( stream );
1b055864 785 return LoadFile(bstream, mimetype);
9e9ee68e 786 }
97fdfcc9 787 else
58c837a4
RR
788 {
789 wxLogError( _("Can't load image from file '%s': file does not exist."), filename.c_str() );
c7abc967 790
fd0eed64
RR
791 return FALSE;
792 }
e02afc7a 793#else // !wxUSE_STREAMS
dbda9e86 794 return FALSE;
e02afc7a 795#endif // wxUSE_STREAMS
1ccbb61a
VZ
796}
797
798bool wxImage::SaveFile( const wxString& filename, int type )
799{
e02afc7a 800#if wxUSE_STREAMS
1ccbb61a 801 wxFileOutputStream stream(filename);
9e9ee68e 802
1ccbb61a 803 if ( stream.LastError() == wxStream_NOERROR )
1b055864 804 {
069d0f27 805 wxBufferedOutputStream bstream( stream );
1b055864
RR
806 return SaveFile(bstream, type);
807 }
e02afc7a 808#endif // wxUSE_STREAMS
3ca6a5f0
BP
809
810 return FALSE;
3d05544e 811}
01111366 812
9e9ee68e
VS
813bool wxImage::SaveFile( const wxString& filename, const wxString& mimetype )
814{
815#if wxUSE_STREAMS
816 wxFileOutputStream stream(filename);
c7abc967 817
9e9ee68e 818 if ( stream.LastError() == wxStream_NOERROR )
1b055864 819 {
069d0f27 820 wxBufferedOutputStream bstream( stream );
1b055864
RR
821 return SaveFile(bstream, mimetype);
822 }
9e9ee68e 823#endif // wxUSE_STREAMS
3ca6a5f0
BP
824
825 return FALSE;
9e9ee68e
VS
826}
827
87202f78
SB
828bool wxImage::CanRead( const wxString &name )
829{
830#if wxUSE_STREAMS
831 wxFileInputStream stream(name);
832 return CanRead(stream);
833#else
834 return FALSE;
835#endif
836}
837
e02afc7a 838#if wxUSE_STREAMS
deb2fec0 839
87202f78
SB
840bool wxImage::CanRead( wxInputStream &stream )
841{
842 wxList &list=GetHandlers();
004fd0c8 843
87202f78 844 for ( wxList::Node *node = list.GetFirst(); node; node = node->GetNext() )
004fd0c8 845 {
87202f78
SB
846 wxImageHandler *handler=(wxImageHandler*)node->GetData();
847 if (handler->CanRead( stream ))
069d0f27 848 return TRUE;
87202f78
SB
849 }
850
851 return FALSE;
852}
853
3d05544e
JS
854bool wxImage::LoadFile( wxInputStream& stream, long type )
855{
856 UnRef();
c7abc967 857
fd0eed64 858 m_refData = new wxImageRefData;
c7abc967 859
deb2fec0
SB
860 wxImageHandler *handler;
861
862 if (type==wxBITMAP_TYPE_ANY)
863 {
995612e2 864 wxList &list=GetHandlers();
deb2fec0 865
995612e2
VZ
866 for ( wxList::Node *node = list.GetFirst(); node; node = node->GetNext() )
867 {
868 handler=(wxImageHandler*)node->GetData();
869 if (handler->CanRead( stream ))
870 return handler->LoadFile( this, stream );
7b2471a0 871
995612e2 872 }
deb2fec0 873
58c837a4 874 wxLogWarning( _("No handler found for image type.") );
995612e2 875 return FALSE;
deb2fec0
SB
876 }
877
878 handler = FindHandler(type);
c7abc967 879
4698648f 880 if (handler == NULL)
fd0eed64 881 {
58c837a4 882 wxLogWarning( _("No image handler for type %d defined."), type );
c7abc967 883
fd0eed64
RR
884 return FALSE;
885 }
c7abc967 886
3d05544e 887 return handler->LoadFile( this, stream );
01111366
RR
888}
889
9e9ee68e
VS
890bool wxImage::LoadFile( wxInputStream& stream, const wxString& mimetype )
891{
892 UnRef();
893
894 m_refData = new wxImageRefData;
895
896 wxImageHandler *handler = FindHandlerMime(mimetype);
897
898 if (handler == NULL)
899 {
58c837a4 900 wxLogWarning( _("No image handler for type %s defined."), mimetype.GetData() );
9e9ee68e
VS
901
902 return FALSE;
903 }
904
905 return handler->LoadFile( this, stream );
906}
907
3d05544e 908bool wxImage::SaveFile( wxOutputStream& stream, int type )
01111366 909{
223d09f6 910 wxCHECK_MSG( Ok(), FALSE, wxT("invalid image") );
c7abc967 911
fd0eed64 912 wxImageHandler *handler = FindHandler(type);
c7abc967 913
4698648f 914 if (handler == NULL)
fd0eed64 915 {
58c837a4 916 wxLogWarning( _("No image handler for type %d defined."), type );
9e9ee68e
VS
917
918 return FALSE;
919 }
920
921 return handler->SaveFile( this, stream );
922}
923
924bool wxImage::SaveFile( wxOutputStream& stream, const wxString& mimetype )
925{
223d09f6 926 wxCHECK_MSG( Ok(), FALSE, wxT("invalid image") );
c7abc967 927
9e9ee68e 928 wxImageHandler *handler = FindHandlerMime(mimetype);
c7abc967 929
9e9ee68e
VS
930 if (handler == NULL)
931 {
58c837a4 932 wxLogWarning( _("No image handler for type %s defined."), mimetype.GetData() );
c7abc967 933
dbda9e86 934 return FALSE;
fd0eed64 935 }
c7abc967 936
3d05544e 937 return handler->SaveFile( this, stream );
01111366 938}
e02afc7a 939#endif // wxUSE_STREAMS
01111366
RR
940
941void wxImage::AddHandler( wxImageHandler *handler )
942{
4698648f
VZ
943 // make sure that the memory will be freed at the program end
944 sm_handlers.DeleteContents(TRUE);
c7abc967 945
01111366
RR
946 sm_handlers.Append( handler );
947}
948
949void wxImage::InsertHandler( wxImageHandler *handler )
950{
4698648f
VZ
951 // make sure that the memory will be freed at the program end
952 sm_handlers.DeleteContents(TRUE);
c7abc967 953
01111366
RR
954 sm_handlers.Insert( handler );
955}
956
957bool wxImage::RemoveHandler( const wxString& name )
958{
fd0eed64
RR
959 wxImageHandler *handler = FindHandler(name);
960 if (handler)
961 {
962 sm_handlers.DeleteObject(handler);
963 return TRUE;
964 }
965 else
966 return FALSE;
01111366
RR
967}
968
969wxImageHandler *wxImage::FindHandler( const wxString& name )
970{
fd0eed64
RR
971 wxNode *node = sm_handlers.First();
972 while (node)
973 {
974 wxImageHandler *handler = (wxImageHandler*)node->Data();
ce3ed50d 975 if (handler->GetName().Cmp(name) == 0) return handler;
c7abc967 976
fd0eed64
RR
977 node = node->Next();
978 }
979 return (wxImageHandler *)NULL;
01111366
RR
980}
981
982wxImageHandler *wxImage::FindHandler( const wxString& extension, long bitmapType )
983{
fd0eed64
RR
984 wxNode *node = sm_handlers.First();
985 while (node)
986 {
987 wxImageHandler *handler = (wxImageHandler*)node->Data();
ce3ed50d 988 if ( (handler->GetExtension().Cmp(extension) == 0) &&
fd0eed64 989 (bitmapType == -1 || handler->GetType() == bitmapType) )
dbda9e86 990 return handler;
fd0eed64
RR
991 node = node->Next();
992 }
993 return (wxImageHandler*)NULL;
01111366
RR
994}
995
996wxImageHandler *wxImage::FindHandler( long bitmapType )
997{
fd0eed64
RR
998 wxNode *node = sm_handlers.First();
999 while (node)
1000 {
1001 wxImageHandler *handler = (wxImageHandler *)node->Data();
1002 if (handler->GetType() == bitmapType) return handler;
1003 node = node->Next();
1004 }
1005 return NULL;
1006}
1007
9e9ee68e
VS
1008wxImageHandler *wxImage::FindHandlerMime( const wxString& mimetype )
1009{
1010 wxNode *node = sm_handlers.First();
1011 while (node)
1012 {
1013 wxImageHandler *handler = (wxImageHandler *)node->Data();
1014 if (handler->GetMimeType().IsSameAs(mimetype, FALSE)) return handler;
1015 node = node->Next();
1016 }
1017 return NULL;
1018}
1019
fd0eed64
RR
1020void wxImage::InitStandardHandlers()
1021{
deb2fec0 1022 AddHandler( new wxBMPHandler );
01111366
RR
1023}
1024
1025void wxImage::CleanUpHandlers()
1026{
fd0eed64
RR
1027 wxNode *node = sm_handlers.First();
1028 while (node)
1029 {
1030 wxImageHandler *handler = (wxImageHandler *)node->Data();
1031 wxNode *next = node->Next();
1032 delete handler;
1033 delete node;
1034 node = next;
1035 }
01111366
RR
1036}
1037
1038//-----------------------------------------------------------------------------
1039// wxImageHandler
1040//-----------------------------------------------------------------------------
1041
63d963a1 1042IMPLEMENT_ABSTRACT_CLASS(wxImageHandler,wxObject)
01111366 1043
e02afc7a 1044#if wxUSE_STREAMS
700ec454 1045bool wxImageHandler::LoadFile( wxImage *WXUNUSED(image), wxInputStream& WXUNUSED(stream), bool WXUNUSED(verbose), int WXUNUSED(index) )
01111366 1046{
fd0eed64 1047 return FALSE;
01111366
RR
1048}
1049
deb2fec0 1050bool wxImageHandler::SaveFile( wxImage *WXUNUSED(image), wxOutputStream& WXUNUSED(stream), bool WXUNUSED(verbose) )
01111366 1051{
fd0eed64 1052 return FALSE;
01111366 1053}
0828c087 1054
700ec454
RR
1055int wxImageHandler::GetImageCount( wxInputStream& WXUNUSED(stream) )
1056{
1057 return 1;
1058}
1059
0828c087
VS
1060bool wxImageHandler::CanRead( const wxString& name )
1061{
0828c087
VS
1062 if (wxFileExists(name))
1063 {
1064 wxFileInputStream stream(name);
1065 return CanRead(stream);
1066 }
1067
1068 else {
58c837a4 1069 wxLogError( _("Can't check image format of file '%s': file does not exist."), name.c_str() );
0828c087
VS
1070
1071 return FALSE;
1072 }
68874acf 1073// return FALSE;
0828c087
VS
1074}
1075
e02afc7a 1076#endif // wxUSE_STREAMS
01111366 1077
fec19ea9
VS
1078
1079
01111366 1080//-----------------------------------------------------------------------------
fec19ea9 1081// wxBitmap convertion routines
01111366
RR
1082//-----------------------------------------------------------------------------
1083
fec19ea9 1084#if wxUSE_GUI
e3554471 1085
fec19ea9 1086#ifdef __WXGTK__
f515c25a 1087wxBitmap wxImage::ConvertToMonoBitmap( unsigned char red, unsigned char green, unsigned char blue ) const
e3554471 1088{
fec19ea9
VS
1089 wxImage mono = this->ConvertToMono( red, green, blue );
1090 wxBitmap bitmap( mono, 1 );
bba6f3bd 1091 return bitmap;
e3554471 1092}
03e11df5 1093#endif
7c74e7fe 1094
7c74e7fe
SC
1095wxBitmap wxImage::ConvertToBitmap() const
1096{
fec19ea9 1097 wxBitmap bitmap( *this );
5fde6fcc 1098 return bitmap;
7c74e7fe
SC
1099}
1100
1101wxImage::wxImage( const wxBitmap &bitmap )
1102{
fec19ea9 1103 *this = bitmap.ConvertToImage();
82ea63e6
RR
1104}
1105
ba0730de 1106#endif
c7abc967 1107
c7abc967 1108
004fd0c8 1109
a91b47e8
JS
1110// A module to allow wxImage initialization/cleanup
1111// without calling these functions from app.cpp or from
1112// the user's application.
1113
1114class wxImageModule: public wxModule
1115{
1116DECLARE_DYNAMIC_CLASS(wxImageModule)
1117public:
1118 wxImageModule() {}
1119 bool OnInit() { wxImage::InitStandardHandlers(); return TRUE; };
1120 void OnExit() { wxImage::CleanUpHandlers(); };
1121};
1122
1123IMPLEMENT_DYNAMIC_CLASS(wxImageModule, wxModule)
c9d01afd
GRG
1124
1125
1126//-----------------------------------------------------------------------------
1127
89d00456
GRG
1128// GRG, Dic/99
1129// Counts and returns the number of different colours. Optionally stops
cc9f7d79
GRG
1130// when it exceeds 'stopafter' different colours. This is useful, for
1131// example, to see if the image can be saved as 8-bit (256 colour or
1132// less, in this case it would be invoked as CountColours(256)). Default
1133// value for stopafter is -1 (don't care).
89d00456 1134//
cc9f7d79 1135unsigned long wxImage::CountColours( unsigned long stopafter )
89d00456
GRG
1136{
1137 wxHashTable h;
ad30de59 1138 wxObject dummy;
89d00456
GRG
1139 unsigned char r, g, b, *p;
1140 unsigned long size, nentries, key;
1141
1142 p = GetData();
1143 size = GetWidth() * GetHeight();
1144 nentries = 0;
1145
cc9f7d79 1146 for (unsigned long j = 0; (j < size) && (nentries <= stopafter) ; j++)
89d00456
GRG
1147 {
1148 r = *(p++);
1149 g = *(p++);
1150 b = *(p++);
1151 key = (r << 16) | (g << 8) | b;
1152
ad30de59 1153 if (h.Get(key) == NULL)
89d00456 1154 {
ad30de59 1155 h.Put(key, &dummy);
89d00456
GRG
1156 nentries++;
1157 }
1158 }
1159
89d00456
GRG
1160 return nentries;
1161}
1162
1163
c9d01afd
GRG
1164// GRG, Dic/99
1165// Computes the histogram of the image and fills a hash table, indexed
1166// with integer keys built as 0xRRGGBB, containing wxHNode objects. Each
1167// wxHNode contains an 'index' (useful to build a palette with the image
1168// colours) and a 'value', which is the number of pixels in the image with
1169// that colour.
89d00456 1170//
c9d01afd
GRG
1171unsigned long wxImage::ComputeHistogram( wxHashTable &h )
1172{
1173 unsigned char r, g, b, *p;
1174 unsigned long size, nentries, key;
1175 wxHNode *hnode;
1176
1177 p = GetData();
1178 size = GetWidth() * GetHeight();
1179 nentries = 0;
1180
1181 for (unsigned long j = 0; j < size; j++)
1182 {
1183 r = *(p++);
1184 g = *(p++);
1185 b = *(p++);
1186 key = (r << 16) | (g << 8) | b;
1187
1188 hnode = (wxHNode *) h.Get(key);
1189
1190 if (hnode)
1191 hnode->value++;
1192 else
1193 {
1194 hnode = new wxHNode();
97fdfcc9 1195 hnode->index = nentries++;
c9d01afd
GRG
1196 hnode->value = 1;
1197
1198 h.Put(key, (wxObject *)hnode);
1199 }
1200 }
1201
1202 return nentries;
1203}
1204
7a632f10
JS
1205/*
1206 * Rotation code by Carlos Moreno
1207 */
1208
b5c91ac6
GRG
1209// GRG: I've removed wxRotationPoint - we already have wxRealPoint which
1210// does exactly the same thing. And I also got rid of wxRotationPixel
1211// bacause of potential problems in architectures where alignment
1212// is an issue, so I had to rewrite parts of the code.
7a632f10 1213
7a632f10
JS
1214static const double gs_Epsilon = 1e-10;
1215
1216static inline int wxCint (double x)
1217{
1218 return (x > 0) ? (int) (x + 0.5) : (int) (x - 0.5);
1219}
1220
1221
1222// Auxiliary function to rotate a point (x,y) with respect to point p0
1223// make it inline and use a straight return to facilitate optimization
1224// also, the function receives the sine and cosine of the angle to avoid
1225// repeating the time-consuming calls to these functions -- sin/cos can
1226// be computed and stored in the calling function.
1227
b5c91ac6 1228inline wxRealPoint rotated_point (const wxRealPoint & p, double cos_angle, double sin_angle, const wxRealPoint & p0)
7a632f10 1229{
b5c91ac6
GRG
1230 return wxRealPoint (p0.x + (p.x - p0.x) * cos_angle - (p.y - p0.y) * sin_angle,
1231 p0.y + (p.y - p0.y) * cos_angle + (p.x - p0.x) * sin_angle);
7a632f10
JS
1232}
1233
b5c91ac6 1234inline wxRealPoint rotated_point (double x, double y, double cos_angle, double sin_angle, const wxRealPoint & p0)
7a632f10 1235{
b5c91ac6 1236 return rotated_point (wxRealPoint(x,y), cos_angle, sin_angle, p0);
7a632f10
JS
1237}
1238
1239wxImage wxImage::Rotate(double angle, const wxPoint & centre_of_rotation, bool interpolating, wxPoint * offset_after_rotation) const
1240{
7a632f10
JS
1241 int i;
1242 angle = -angle; // screen coordinates are a mirror image of "real" coordinates
1243
ad30de59 1244 // Create pointer-based array to accelerate access to wxImage's data
b5c91ac6 1245 unsigned char ** data = new unsigned char * [GetHeight()];
7a632f10 1246
b5c91ac6 1247 data[0] = GetData();
7a632f10 1248
b5c91ac6
GRG
1249 for (i = 1; i < GetHeight(); i++)
1250 data[i] = data[i - 1] + (3 * GetWidth());
7a632f10 1251
b5c91ac6 1252 // precompute coefficients for rotation formula
ad30de59 1253 // (sine and cosine of the angle)
7a632f10
JS
1254 const double cos_angle = cos(angle);
1255 const double sin_angle = sin(angle);
1256
ad30de59
GRG
1257 // Create new Image to store the result
1258 // First, find rectangle that covers the rotated image; to do that,
1259 // rotate the four corners
7a632f10 1260
b5c91ac6 1261 const wxRealPoint p0(centre_of_rotation.x, centre_of_rotation.y);
7a632f10 1262
b5c91ac6
GRG
1263 wxRealPoint p1 = rotated_point (0, 0, cos_angle, sin_angle, p0);
1264 wxRealPoint p2 = rotated_point (0, GetHeight(), cos_angle, sin_angle, p0);
1265 wxRealPoint p3 = rotated_point (GetWidth(), 0, cos_angle, sin_angle, p0);
1266 wxRealPoint p4 = rotated_point (GetWidth(), GetHeight(), cos_angle, sin_angle, p0);
7a632f10 1267
57c1c6cb
BJ
1268 int x1 = (int) floor (wxMin (wxMin(p1.x, p2.x), wxMin(p3.x, p4.x)));
1269 int y1 = (int) floor (wxMin (wxMin(p1.y, p2.y), wxMin(p3.y, p4.y)));
57c1c6cb
BJ
1270 int x2 = (int) ceil (wxMax (wxMax(p1.x, p2.x), wxMax(p3.x, p4.x)));
1271 int y2 = (int) ceil (wxMax (wxMax(p1.y, p2.y), wxMax(p3.y, p4.y)));
7a632f10
JS
1272
1273 wxImage rotated (x2 - x1 + 1, y2 - y1 + 1);
1274
1275 if (offset_after_rotation != NULL)
1276 {
06b466c7 1277 *offset_after_rotation = wxPoint (x1, y1);
7a632f10
JS
1278 }
1279
b5c91ac6
GRG
1280 // GRG: The rotated (destination) image is always accessed
1281 // sequentially, so there is no need for a pointer-based
1282 // array here (and in fact it would be slower).
1283 //
1284 unsigned char * dst = rotated.GetData();
7a632f10 1285
ad30de59
GRG
1286 // GRG: if the original image has a mask, use its RGB values
1287 // as the blank pixel, else, fall back to default (black).
1288 //
b5c91ac6
GRG
1289 unsigned char blank_r = 0;
1290 unsigned char blank_g = 0;
1291 unsigned char blank_b = 0;
ad30de59
GRG
1292
1293 if (HasMask())
1294 {
b5c91ac6
GRG
1295 blank_r = GetMaskRed();
1296 blank_g = GetMaskGreen();
1297 blank_b = GetMaskBlue();
1298 rotated.SetMaskColour( blank_r, blank_g, blank_b );
ad30de59
GRG
1299 }
1300
1301 // Now, for each point of the rotated image, find where it came from, by
1302 // performing an inverse rotation (a rotation of -angle) and getting the
1303 // pixel at those coordinates
1304
b5c91ac6
GRG
1305 // GRG: I've taken the (interpolating) test out of the loops, so that
1306 // it is done only once, instead of repeating it for each pixel.
7a632f10
JS
1307
1308 int x;
b5c91ac6 1309 if (interpolating)
7a632f10
JS
1310 {
1311 for (int y = 0; y < rotated.GetHeight(); y++)
1312 {
b5c91ac6 1313 for (x = 0; x < rotated.GetWidth(); x++)
7a632f10 1314 {
b5c91ac6
GRG
1315 wxRealPoint src = rotated_point (x + x1, y + y1, cos_angle, -sin_angle, p0);
1316
f2506310
JS
1317 if (-0.25 < src.x && src.x < GetWidth() - 0.75 &&
1318 -0.25 < src.y && src.y < GetHeight() - 0.75)
7a632f10 1319 {
ad30de59
GRG
1320 // interpolate using the 4 enclosing grid-points. Those
1321 // points can be obtained using floor and ceiling of the
1322 // exact coordinates of the point
f2506310 1323 // C.M. 2000-02-17: when the point is near the border, special care is required.
7a632f10 1324
f2506310
JS
1325 int x1, y1, x2, y2;
1326
1327 if (0 < src.x && src.x < GetWidth() - 1)
1328 {
1329 x1 = wxCint(floor(src.x));
1330 x2 = wxCint(ceil(src.x));
1331 }
1332 else // else means that x is near one of the borders (0 or width-1)
1333 {
1334 x1 = x2 = wxCint (src.x);
1335 }
1336
1337 if (0 < src.y && src.y < GetHeight() - 1)
1338 {
1339 y1 = wxCint(floor(src.y));
1340 y2 = wxCint(ceil(src.y));
1341 }
1342 else
1343 {
1344 y1 = y2 = wxCint (src.y);
1345 }
7a632f10 1346
ad30de59
GRG
1347 // get four points and the distances (square of the distance,
1348 // for efficiency reasons) for the interpolation formula
b5c91ac6
GRG
1349
1350 // GRG: Do not calculate the points until they are
1351 // really needed -- this way we can calculate
1352 // just one, instead of four, if d1, d2, d3
1353 // or d4 are < gs_Epsilon
7a632f10
JS
1354
1355 const double d1 = (src.x - x1) * (src.x - x1) + (src.y - y1) * (src.y - y1);
1356 const double d2 = (src.x - x2) * (src.x - x2) + (src.y - y1) * (src.y - y1);
1357 const double d3 = (src.x - x2) * (src.x - x2) + (src.y - y2) * (src.y - y2);
1358 const double d4 = (src.x - x1) * (src.x - x1) + (src.y - y2) * (src.y - y2);
1359
ad30de59
GRG
1360 // Now interpolate as a weighted average of the four surrounding
1361 // points, where the weights are the distances to each of those points
7a632f10 1362
ad30de59
GRG
1363 // If the point is exactly at one point of the grid of the source
1364 // image, then don't interpolate -- just assign the pixel
7a632f10 1365
06b466c7 1366 if (d1 < gs_Epsilon) // d1,d2,d3,d4 are positive -- no need for abs()
7a632f10 1367 {
b5c91ac6
GRG
1368 unsigned char *p = data[y1] + (3 * x1);
1369 *(dst++) = *(p++);
1370 *(dst++) = *(p++);
1371 *(dst++) = *(p++);
7a632f10
JS
1372 }
1373 else if (d2 < gs_Epsilon)
1374 {
b5c91ac6
GRG
1375 unsigned char *p = data[y1] + (3 * x2);
1376 *(dst++) = *(p++);
1377 *(dst++) = *(p++);
1378 *(dst++) = *(p++);
7a632f10
JS
1379 }
1380 else if (d3 < gs_Epsilon)
1381 {
b5c91ac6
GRG
1382 unsigned char *p = data[y2] + (3 * x2);
1383 *(dst++) = *(p++);
1384 *(dst++) = *(p++);
1385 *(dst++) = *(p++);
7a632f10
JS
1386 }
1387 else if (d4 < gs_Epsilon)
1388 {
b5c91ac6
GRG
1389 unsigned char *p = data[y2] + (3 * x1);
1390 *(dst++) = *(p++);
1391 *(dst++) = *(p++);
1392 *(dst++) = *(p++);
7a632f10
JS
1393 }
1394 else
1395 {
06b466c7 1396 // weights for the weighted average are proportional to the inverse of the distance
b5c91ac6
GRG
1397 unsigned char *v1 = data[y1] + (3 * x1);
1398 unsigned char *v2 = data[y1] + (3 * x2);
1399 unsigned char *v3 = data[y2] + (3 * x2);
1400 unsigned char *v4 = data[y2] + (3 * x1);
1401
06b466c7
VZ
1402 const double w1 = 1/d1, w2 = 1/d2, w3 = 1/d3, w4 = 1/d4;
1403
b5c91ac6
GRG
1404 // GRG: Unrolled.
1405
1406 *(dst++) = (unsigned char)
1407 ( (w1 * *(v1++) + w2 * *(v2++) +
1408 w3 * *(v3++) + w4 * *(v4++)) /
1409 (w1 + w2 + w3 + w4) );
1410 *(dst++) = (unsigned char)
1411 ( (w1 * *(v1++) + w2 * *(v2++) +
1412 w3 * *(v3++) + w4 * *(v4++)) /
1413 (w1 + w2 + w3 + w4) );
1414 *(dst++) = (unsigned char)
1415 ( (w1 * *(v1++) + w2 * *(v2++) +
1416 w3 * *(v3++) + w4 * *(v4++)) /
1417 (w1 + w2 + w3 + w4) );
7a632f10
JS
1418 }
1419 }
1420 else
1421 {
b5c91ac6
GRG
1422 *(dst++) = blank_r;
1423 *(dst++) = blank_g;
1424 *(dst++) = blank_b;
7a632f10
JS
1425 }
1426 }
b5c91ac6
GRG
1427 }
1428 }
1429 else // not interpolating
1430 {
1431 for (int y = 0; y < rotated.GetHeight(); y++)
1432 {
1433 for (x = 0; x < rotated.GetWidth(); x++)
7a632f10 1434 {
b5c91ac6
GRG
1435 wxRealPoint src = rotated_point (x + x1, y + y1, cos_angle, -sin_angle, p0);
1436
1437 const int xs = wxCint (src.x); // wxCint rounds to the
457e6c54 1438 const int ys = wxCint (src.y); // closest integer
7a632f10 1439
b5c91ac6
GRG
1440 if (0 <= xs && xs < GetWidth() &&
1441 0 <= ys && ys < GetHeight())
7a632f10 1442 {
b5c91ac6
GRG
1443 unsigned char *p = data[ys] + (3 * xs);
1444 *(dst++) = *(p++);
1445 *(dst++) = *(p++);
1446 *(dst++) = *(p++);
7a632f10
JS
1447 }
1448 else
1449 {
b5c91ac6
GRG
1450 *(dst++) = blank_r;
1451 *(dst++) = blank_g;
1452 *(dst++) = blank_b;
7a632f10
JS
1453 }
1454 }
1455 }
1456 }
1457
4aff28fc 1458 delete [] data;
4aff28fc 1459
7a632f10
JS
1460 return rotated;
1461}
c9d01afd 1462