sorry previous commit message was wrong, should read
[wxWidgets.git] / src / common / image.cpp
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
14 // For compilers that support precompilation, includes "wx.h".
15 #include "wx/wxprec.h"
16
17 #ifdef __BORLANDC__
18 #pragma hdrstop
19 #endif
20
21 #include "wx/defs.h"
22
23 #if wxUSE_IMAGE
24
25 #include "wx/image.h"
26 #include "wx/bitmap.h"
27 #include "wx/debug.h"
28 #include "wx/log.h"
29 #include "wx/app.h"
30 #include "wx/filefn.h"
31 #include "wx/wfstream.h"
32 #include "wx/intl.h"
33 #include "wx/module.h"
34 #include "wx/hash.h"
35
36 // For memcpy
37 #include <string.h>
38 #include <math.h>
39
40 #ifdef __SALFORDC__
41 #undef FAR
42 #endif
43
44
45 //-----------------------------------------------------------------------------
46 // wxImage
47 //-----------------------------------------------------------------------------
48
49 class wxImageRefData: public wxObjectRefData
50 {
51 public:
52 wxImageRefData();
53 ~wxImageRefData();
54
55 int m_width;
56 int m_height;
57 unsigned char *m_data;
58 bool m_hasMask;
59 unsigned char m_maskRed,m_maskGreen,m_maskBlue;
60 bool m_ok;
61 bool m_static;
62 #if wxUSE_PALETTE
63 wxPalette m_palette;
64 #endif // wxUSE_PALETTE
65 wxArrayString m_optionNames;
66 wxArrayString m_optionValues;
67
68 DECLARE_NO_COPY_CLASS(wxImageRefData)
69 };
70
71 wxImageRefData::wxImageRefData()
72 {
73 m_width = 0;
74 m_height = 0;
75 m_data = (unsigned char*) NULL;
76 m_ok = FALSE;
77 m_maskRed = 0;
78 m_maskGreen = 0;
79 m_maskBlue = 0;
80 m_hasMask = FALSE;
81 m_static = FALSE;
82 }
83
84 wxImageRefData::~wxImageRefData()
85 {
86 if (m_data && !m_static)
87 free( m_data );
88 }
89
90 wxList wxImage::sm_handlers;
91
92 wxImage wxNullImage;
93
94 //-----------------------------------------------------------------------------
95
96 #define M_IMGDATA ((wxImageRefData *)m_refData)
97
98 IMPLEMENT_DYNAMIC_CLASS(wxImage, wxObject)
99
100 wxImage::wxImage()
101 {
102 }
103
104 wxImage::wxImage( int width, int height )
105 {
106 Create( width, height );
107 }
108
109 wxImage::wxImage( int width, int height, unsigned char* data, bool static_data )
110 {
111 Create( width, height, data, static_data );
112 }
113
114 wxImage::wxImage( const wxString& name, long type, int index )
115 {
116 LoadFile( name, type, index );
117 }
118
119 wxImage::wxImage( const wxString& name, const wxString& mimetype, int index )
120 {
121 LoadFile( name, mimetype, index );
122 }
123
124 #if wxUSE_STREAMS
125 wxImage::wxImage( wxInputStream& stream, long type, int index )
126 {
127 LoadFile( stream, type, index );
128 }
129
130 wxImage::wxImage( wxInputStream& stream, const wxString& mimetype, int index )
131 {
132 LoadFile( stream, mimetype, index );
133 }
134 #endif // wxUSE_STREAMS
135
136 wxImage::wxImage( const wxImage& image )
137 : wxObject()
138 {
139 Ref(image);
140 }
141
142 wxImage::wxImage( const wxImage* image )
143 {
144 if (image) Ref(*image);
145 }
146
147 void wxImage::Create( int width, int height )
148 {
149 UnRef();
150
151 m_refData = new wxImageRefData();
152
153 M_IMGDATA->m_data = (unsigned char *) malloc( width*height*3 );
154 if (M_IMGDATA->m_data)
155 {
156 for (int l = 0; l < width*height*3; l++) M_IMGDATA->m_data[l] = 0;
157
158 M_IMGDATA->m_width = width;
159 M_IMGDATA->m_height = height;
160 M_IMGDATA->m_ok = TRUE;
161 }
162 else
163 {
164 UnRef();
165 }
166 }
167
168 void wxImage::Create( int width, int height, unsigned char* data, bool static_data )
169 {
170 UnRef();
171
172 m_refData = new wxImageRefData();
173
174 M_IMGDATA->m_data = data;
175 if (M_IMGDATA->m_data)
176 {
177 M_IMGDATA->m_width = width;
178 M_IMGDATA->m_height = height;
179 M_IMGDATA->m_ok = TRUE;
180 M_IMGDATA->m_static = static_data;
181 }
182 else
183 {
184 UnRef();
185 }
186 }
187
188 void wxImage::Destroy()
189 {
190 UnRef();
191 }
192
193 wxImage wxImage::Copy() const
194 {
195 wxImage image;
196
197 wxCHECK_MSG( Ok(), image, wxT("invalid image") );
198
199 image.Create( M_IMGDATA->m_width, M_IMGDATA->m_height );
200
201 char unsigned *data = image.GetData();
202
203 wxCHECK_MSG( data, image, wxT("unable to create image") );
204
205 image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
206 image.SetMask( M_IMGDATA->m_hasMask );
207
208 memcpy( data, GetData(), M_IMGDATA->m_width*M_IMGDATA->m_height*3 );
209
210 return image;
211 }
212
213 wxImage wxImage::ShrinkBy( int xFactor , int yFactor ) const
214 {
215 if( xFactor == 1 && yFactor == 1 )
216 return Copy() ;
217
218 wxImage image;
219
220 wxCHECK_MSG( Ok(), image, wxT("invalid image") );
221
222 // can't scale to/from 0 size
223 wxCHECK_MSG( (xFactor > 0) && (yFactor > 0), image,
224 wxT("invalid new image size") );
225
226 long old_height = M_IMGDATA->m_height,
227 old_width = M_IMGDATA->m_width;
228
229 wxCHECK_MSG( (old_height > 0) && (old_width > 0), image,
230 wxT("invalid old image size") );
231
232 long width = old_width / xFactor ;
233 long height = old_height / yFactor ;
234
235 image.Create( width , height );
236
237 char unsigned *data = image.GetData();
238
239 wxCHECK_MSG( data, image, wxT("unable to create image") );
240
241 bool hasMask = false ;
242 unsigned char maskRed = 0;
243 unsigned char maskGreen = 0;
244 unsigned char maskBlue =0 ;
245 if (M_IMGDATA->m_hasMask)
246 {
247 hasMask = true ;
248 maskRed = M_IMGDATA->m_maskRed;
249 maskGreen = M_IMGDATA->m_maskGreen;
250 maskBlue =M_IMGDATA->m_maskBlue ;
251
252 image.SetMaskColour( M_IMGDATA->m_maskRed,
253 M_IMGDATA->m_maskGreen,
254 M_IMGDATA->m_maskBlue );
255 }
256 char unsigned *source_data = M_IMGDATA->m_data;
257 char unsigned *target_data = data;
258
259 for (long y = 0; y < height; y++)
260 {
261 for (long x = 0; x < width; x++)
262 {
263 unsigned long avgRed = 0 ;
264 unsigned long avgGreen = 0;
265 unsigned long avgBlue = 0;
266 unsigned long counter = 0 ;
267 // determine average
268 for ( int y1 = 0 ; y1 < yFactor ; ++y1 )
269 {
270 long y_offset = (y * yFactor + y1) * old_width;
271 for ( int x1 = 0 ; x1 < xFactor ; ++x1 )
272 {
273 unsigned char *pixel = source_data + 3 * ( y_offset + x * xFactor + x1 ) ;
274 unsigned char red = pixel[0] ;
275 unsigned char green = pixel[1] ;
276 unsigned char blue = pixel[2] ;
277 if ( !hasMask || red != maskRed || green != maskGreen || blue != maskBlue )
278 {
279 avgRed += red ;
280 avgGreen += green ;
281 avgBlue += blue ;
282 counter++ ;
283 }
284 }
285 }
286 if ( counter == 0 )
287 {
288 *(target_data++) = M_IMGDATA->m_maskRed ;
289 *(target_data++) = M_IMGDATA->m_maskGreen ;
290 *(target_data++) = M_IMGDATA->m_maskBlue ;
291 }
292 else
293 {
294 *(target_data++) = avgRed / counter ;
295 *(target_data++) = avgGreen / counter ;
296 *(target_data++) = avgBlue / counter ;
297 }
298 }
299 }
300
301 // In case this is a cursor, make sure the hotspot is scalled accordingly:
302 if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X) )
303 image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X,
304 (GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X))/xFactor);
305 if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y) )
306 image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y,
307 (GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y))/yFactor);
308
309 return image;
310 }
311
312 wxImage wxImage::Scale( int width, int height ) const
313 {
314 wxImage image;
315
316 wxCHECK_MSG( Ok(), image, wxT("invalid image") );
317
318 // can't scale to/from 0 size
319 wxCHECK_MSG( (width > 0) && (height > 0), image,
320 wxT("invalid new image size") );
321
322 long old_height = M_IMGDATA->m_height,
323 old_width = M_IMGDATA->m_width;
324 wxCHECK_MSG( (old_height > 0) && (old_width > 0), image,
325 wxT("invalid old image size") );
326
327 if ( old_width % width == 0 && old_width >= width &&
328 old_height % height == 0 && old_height >= height )
329 {
330 return ShrinkBy( old_width / width , old_height / height ) ;
331 }
332 image.Create( width, height );
333
334 char unsigned *data = image.GetData();
335
336 wxCHECK_MSG( data, image, wxT("unable to create image") );
337
338 if (M_IMGDATA->m_hasMask)
339 {
340 image.SetMaskColour( M_IMGDATA->m_maskRed,
341 M_IMGDATA->m_maskGreen,
342 M_IMGDATA->m_maskBlue );
343 }
344
345 char unsigned *source_data = M_IMGDATA->m_data;
346 char unsigned *target_data = data;
347
348 #if 0
349 // This is nonsense, RR.
350
351 // We do (x, y) -> (x, y)*oldSize/newSize but the valid values of x and y
352 // are from 0 to size-1, hence all decrement the sizes
353 long old_old_width = old_width;
354 old_height--;
355 old_width--;
356 height--;
357 width--;
358 for ( long j = 0; j <= height; j++ )
359 {
360 // don't crash for images with height == 1
361 long y_offset = height ? (j * old_height / height)* old_old_width : 0;
362
363 for ( long i = 0; i <= width; i++ )
364 {
365 long x_offset = width ? (i * old_width) / width : 0;
366
367 memcpy( target_data, source_data + 3*(y_offset + x_offset), 3 );
368 target_data += 3;
369 }
370 }
371 #else
372 for (long j = 0; j < height; j++)
373 {
374 long y_offset = (j * old_height / height) * old_width;
375
376 for (long i = 0; i < width; i++)
377 {
378 memcpy( target_data,
379 source_data + 3*(y_offset + ((i * old_width )/ width)),
380 3 );
381 target_data += 3;
382 }
383 }
384 #endif
385
386 // In case this is a cursor, make sure the hotspot is scalled accordingly:
387 if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X) )
388 image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X,
389 (GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X)*width)/old_width);
390 if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y) )
391 image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y,
392 (GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y)*height)/old_height);
393
394 return image;
395 }
396
397 wxImage wxImage::Rotate90( bool clockwise ) const
398 {
399 wxImage image;
400
401 wxCHECK_MSG( Ok(), image, wxT("invalid image") );
402
403 image.Create( M_IMGDATA->m_height, M_IMGDATA->m_width );
404
405 char unsigned *data = image.GetData();
406
407 wxCHECK_MSG( data, image, wxT("unable to create image") );
408
409 if (M_IMGDATA->m_hasMask)
410 image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
411
412 long height = M_IMGDATA->m_height;
413 long width = M_IMGDATA->m_width;
414
415 char unsigned *source_data = M_IMGDATA->m_data;
416 char unsigned *target_data;
417
418 for (long j = 0; j < height; j++)
419 {
420 for (long i = 0; i < width; i++)
421 {
422 if (clockwise)
423 target_data = data + (((i+1)*height) - j - 1)*3;
424 else
425 target_data = data + ((height*(width-1)) + j - (i*height))*3;
426 memcpy( target_data, source_data, 3 );
427 source_data += 3;
428 }
429 }
430
431 return image;
432 }
433
434 wxImage wxImage::Mirror( bool horizontally ) const
435 {
436 wxImage image;
437
438 wxCHECK_MSG( Ok(), image, wxT("invalid image") );
439
440 image.Create( M_IMGDATA->m_width, M_IMGDATA->m_height );
441
442 char unsigned *data = image.GetData();
443
444 wxCHECK_MSG( data, image, wxT("unable to create image") );
445
446 if (M_IMGDATA->m_hasMask)
447 image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
448
449 long height = M_IMGDATA->m_height;
450 long width = M_IMGDATA->m_width;
451
452 char unsigned *source_data = M_IMGDATA->m_data;
453 char unsigned *target_data;
454
455 if (horizontally)
456 {
457 for (long j = 0; j < height; j++)
458 {
459 data += width*3;
460 target_data = data-3;
461 for (long i = 0; i < width; i++)
462 {
463 memcpy( target_data, source_data, 3 );
464 source_data += 3;
465 target_data -= 3;
466 }
467 }
468 }
469 else
470 {
471 for (long i = 0; i < height; i++)
472 {
473 target_data = data + 3*width*(height-1-i);
474 memcpy( target_data, source_data, (size_t)3*width );
475 source_data += 3*width;
476 }
477 }
478
479 return image;
480 }
481
482 wxImage wxImage::GetSubImage( const wxRect &rect ) const
483 {
484 wxImage image;
485
486 wxCHECK_MSG( Ok(), image, wxT("invalid image") );
487
488 wxCHECK_MSG( (rect.GetLeft()>=0) && (rect.GetTop()>=0) && (rect.GetRight()<=GetWidth()) && (rect.GetBottom()<=GetHeight()),
489 image, wxT("invalid subimage size") );
490
491 int subwidth=rect.GetWidth();
492 const int subheight=rect.GetHeight();
493
494 image.Create( subwidth, subheight );
495
496 char unsigned *subdata = image.GetData(), *data=GetData();
497
498 wxCHECK_MSG( subdata, image, wxT("unable to create image") );
499
500 if (M_IMGDATA->m_hasMask)
501 image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
502
503 const int subleft=3*rect.GetLeft();
504 const int width=3*GetWidth();
505 subwidth*=3;
506
507 data+=rect.GetTop()*width+subleft;
508
509 for (long j = 0; j < subheight; ++j)
510 {
511 memcpy( subdata, data, subwidth);
512 subdata+=subwidth;
513 data+=width;
514 }
515
516 return image;
517 }
518
519 void wxImage::Paste( const wxImage &image, int x, int y )
520 {
521 wxCHECK_RET( Ok(), wxT("invalid image") );
522 wxCHECK_RET( image.Ok(), wxT("invalid image") );
523
524 int xx = 0;
525 int yy = 0;
526 int width = image.GetWidth();
527 int height = image.GetHeight();
528
529 if (x < 0)
530 {
531 xx = -x;
532 width += x;
533 }
534 if (y < 0)
535 {
536 yy = -y;
537 height += y;
538 }
539
540 if ((x+xx)+width > M_IMGDATA->m_width)
541 width = M_IMGDATA->m_width - (x+xx);
542 if ((y+yy)+height > M_IMGDATA->m_height)
543 height = M_IMGDATA->m_height - (y+yy);
544
545 if (width < 1) return;
546 if (height < 1) return;
547
548 if ((!HasMask() && !image.HasMask()) ||
549 ((HasMask() && image.HasMask() &&
550 (GetMaskRed()==image.GetMaskRed()) &&
551 (GetMaskGreen()==image.GetMaskGreen()) &&
552 (GetMaskBlue()==image.GetMaskBlue()))))
553 {
554 width *= 3;
555 unsigned char* source_data = image.GetData() + xx*3 + yy*3*image.GetWidth();
556 int source_step = image.GetWidth()*3;
557
558 unsigned char* target_data = GetData() + (x+xx)*3 + (y+yy)*3*M_IMGDATA->m_width;
559 int target_step = M_IMGDATA->m_width*3;
560 for (int j = 0; j < height; j++)
561 {
562 memcpy( target_data, source_data, width );
563 source_data += source_step;
564 target_data += target_step;
565 }
566 return;
567 }
568
569 if (!HasMask() && image.HasMask())
570 {
571 unsigned char r = image.GetMaskRed();
572 unsigned char g = image.GetMaskGreen();
573 unsigned char b = image.GetMaskBlue();
574
575 width *= 3;
576 unsigned char* source_data = image.GetData() + xx*3 + yy*3*image.GetWidth();
577 int source_step = image.GetWidth()*3;
578
579 unsigned char* target_data = GetData() + (x+xx)*3 + (y+yy)*3*M_IMGDATA->m_width;
580 int target_step = M_IMGDATA->m_width*3;
581
582 for (int j = 0; j < height; j++)
583 {
584 for (int i = 0; i < width; i+=3)
585 {
586 if ((source_data[i] != r) &&
587 (source_data[i+1] != g) &&
588 (source_data[i+2] != b))
589 {
590 memcpy( target_data+i, source_data+i, 3 );
591 }
592 }
593 source_data += source_step;
594 target_data += target_step;
595 }
596 }
597 }
598
599 void wxImage::Replace( unsigned char r1, unsigned char g1, unsigned char b1,
600 unsigned char r2, unsigned char g2, unsigned char b2 )
601 {
602 wxCHECK_RET( Ok(), wxT("invalid image") );
603
604 char unsigned *data = GetData();
605
606 const int w = GetWidth();
607 const int h = GetHeight();
608
609 for (int j = 0; j < h; j++)
610 for (int i = 0; i < w; i++)
611 {
612 if ((data[0] == r1) && (data[1] == g1) && (data[2] == b1))
613 {
614 data[0] = r2;
615 data[1] = g2;
616 data[2] = b2;
617 }
618 data += 3;
619 }
620 }
621
622 wxImage wxImage::ConvertToMono( unsigned char r, unsigned char g, unsigned char b ) const
623 {
624 wxImage image;
625
626 wxCHECK_MSG( Ok(), image, wxT("invalid image") );
627
628 image.Create( M_IMGDATA->m_width, M_IMGDATA->m_height );
629
630 char unsigned *data = image.GetData();
631
632 wxCHECK_MSG( data, image, wxT("unable to create image") );
633
634 if (M_IMGDATA->m_hasMask)
635 {
636 if (M_IMGDATA->m_maskRed == r && M_IMGDATA->m_maskGreen == g &&
637 M_IMGDATA->m_maskBlue == b)
638 image.SetMaskColour( 255, 255, 255 );
639 else
640 image.SetMaskColour( 0, 0, 0 );
641 }
642
643 long size = M_IMGDATA->m_height * M_IMGDATA->m_width;
644
645 char unsigned *srcd = M_IMGDATA->m_data;
646 char unsigned *tard = image.GetData();
647
648 for ( long i = 0; i < size; i++, srcd += 3, tard += 3 )
649 {
650 if (srcd[0] == r && srcd[1] == g && srcd[2] == b)
651 tard[0] = tard[1] = tard[2] = 255;
652 else
653 tard[0] = tard[1] = tard[2] = 0;
654 }
655
656 return image;
657 }
658
659 void wxImage::SetRGB( int x, int y, unsigned char r, unsigned char g, unsigned char b )
660 {
661 wxCHECK_RET( Ok(), wxT("invalid image") );
662
663 int w = M_IMGDATA->m_width;
664 int h = M_IMGDATA->m_height;
665
666 wxCHECK_RET( (x>=0) && (y>=0) && (x<w) && (y<h), wxT("invalid image index") );
667
668 long pos = (y * w + x) * 3;
669
670 M_IMGDATA->m_data[ pos ] = r;
671 M_IMGDATA->m_data[ pos+1 ] = g;
672 M_IMGDATA->m_data[ pos+2 ] = b;
673 }
674
675 unsigned char wxImage::GetRed( int x, int y ) const
676 {
677 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
678
679 int w = M_IMGDATA->m_width;
680 int h = M_IMGDATA->m_height;
681
682 wxCHECK_MSG( (x>=0) && (y>=0) && (x<w) && (y<h), 0, wxT("invalid image index") );
683
684 long pos = (y * w + x) * 3;
685
686 return M_IMGDATA->m_data[pos];
687 }
688
689 unsigned char wxImage::GetGreen( int x, int y ) const
690 {
691 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
692
693 int w = M_IMGDATA->m_width;
694 int h = M_IMGDATA->m_height;
695
696 wxCHECK_MSG( (x>=0) && (y>=0) && (x<w) && (y<h), 0, wxT("invalid image index") );
697
698 long pos = (y * w + x) * 3;
699
700 return M_IMGDATA->m_data[pos+1];
701 }
702
703 unsigned char wxImage::GetBlue( int x, int y ) const
704 {
705 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
706
707 int w = M_IMGDATA->m_width;
708 int h = M_IMGDATA->m_height;
709
710 wxCHECK_MSG( (x>=0) && (y>=0) && (x<w) && (y<h), 0, wxT("invalid image index") );
711
712 long pos = (y * w + x) * 3;
713
714 return M_IMGDATA->m_data[pos+2];
715 }
716
717 bool wxImage::Ok() const
718 {
719 // image of 0 width or height can't be considered ok - at least because it
720 // causes crashes in ConvertToBitmap() if we don't catch it in time
721 wxImageRefData *data = M_IMGDATA;
722 return data && data->m_ok && data->m_width && data->m_height;
723 }
724
725 char unsigned *wxImage::GetData() const
726 {
727 wxCHECK_MSG( Ok(), (char unsigned *)NULL, wxT("invalid image") );
728
729 return M_IMGDATA->m_data;
730 }
731
732 void wxImage::SetData( char unsigned *data )
733 {
734 wxCHECK_RET( Ok(), wxT("invalid image") );
735
736 wxImageRefData *newRefData = new wxImageRefData();
737
738 newRefData->m_width = M_IMGDATA->m_width;
739 newRefData->m_height = M_IMGDATA->m_height;
740 newRefData->m_data = data;
741 newRefData->m_ok = TRUE;
742 newRefData->m_maskRed = M_IMGDATA->m_maskRed;
743 newRefData->m_maskGreen = M_IMGDATA->m_maskGreen;
744 newRefData->m_maskBlue = M_IMGDATA->m_maskBlue;
745 newRefData->m_hasMask = M_IMGDATA->m_hasMask;
746
747 UnRef();
748
749 m_refData = newRefData;
750 }
751
752 void wxImage::SetData( char unsigned *data, int new_width, int new_height )
753 {
754 wxImageRefData *newRefData = new wxImageRefData();
755
756 if (m_refData)
757 {
758 newRefData->m_width = new_width;
759 newRefData->m_height = new_height;
760 newRefData->m_data = data;
761 newRefData->m_ok = TRUE;
762 newRefData->m_maskRed = M_IMGDATA->m_maskRed;
763 newRefData->m_maskGreen = M_IMGDATA->m_maskGreen;
764 newRefData->m_maskBlue = M_IMGDATA->m_maskBlue;
765 newRefData->m_hasMask = M_IMGDATA->m_hasMask;
766 }
767 else
768 {
769 newRefData->m_width = new_width;
770 newRefData->m_height = new_height;
771 newRefData->m_data = data;
772 newRefData->m_ok = TRUE;
773 }
774
775 UnRef();
776
777 m_refData = newRefData;
778 }
779
780 void wxImage::SetMaskColour( unsigned char r, unsigned char g, unsigned char b )
781 {
782 wxCHECK_RET( Ok(), wxT("invalid image") );
783
784 M_IMGDATA->m_maskRed = r;
785 M_IMGDATA->m_maskGreen = g;
786 M_IMGDATA->m_maskBlue = b;
787 M_IMGDATA->m_hasMask = TRUE;
788 }
789
790 unsigned char wxImage::GetMaskRed() const
791 {
792 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
793
794 return M_IMGDATA->m_maskRed;
795 }
796
797 unsigned char wxImage::GetMaskGreen() const
798 {
799 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
800
801 return M_IMGDATA->m_maskGreen;
802 }
803
804 unsigned char wxImage::GetMaskBlue() const
805 {
806 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
807
808 return M_IMGDATA->m_maskBlue;
809 }
810
811 void wxImage::SetMask( bool mask )
812 {
813 wxCHECK_RET( Ok(), wxT("invalid image") );
814
815 M_IMGDATA->m_hasMask = mask;
816 }
817
818 bool wxImage::HasMask() const
819 {
820 wxCHECK_MSG( Ok(), FALSE, wxT("invalid image") );
821
822 return M_IMGDATA->m_hasMask;
823 }
824
825 int wxImage::GetWidth() const
826 {
827 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
828
829 return M_IMGDATA->m_width;
830 }
831
832 int wxImage::GetHeight() const
833 {
834 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
835
836 return M_IMGDATA->m_height;
837 }
838
839
840 bool wxImage::FindFirstUnusedColour(
841 unsigned char *r, unsigned char *g, unsigned char *b,
842 unsigned char startR, unsigned char startG, unsigned char startB) const
843 {
844 wxImageHistogram histogram;
845 unsigned long key;
846
847 ComputeHistogram(histogram);
848
849 unsigned char r2 = startR;
850 unsigned char g2 = startG;
851 unsigned char b2 = startB;
852
853 key = (r2 << 16) | (g2 << 8) | b2;
854
855 while ( histogram.find(key) != histogram.end() )
856 {
857 // color already used
858 r2++;
859 if ( r2 >= 255 )
860 {
861 r2 = 0;
862 g2++;
863 if ( g2 >= 255 )
864 {
865 g2 = 0;
866 b2++;
867 if ( b2 >= 255 )
868 {
869 wxLogError( _("GetUnusedColour:: No Unused Color in image ") );
870 return FALSE;
871 }
872 }
873 }
874
875 key = (r2 << 16) | (g2 << 8) | b2;
876 }
877
878 if (r) *r = r2;
879 if (g) *g = g2;
880 if (b) *b = b2;
881
882 return TRUE;
883 }
884
885
886 bool wxImage::SetMaskFromImage(const wxImage& mask,
887 unsigned char mr, unsigned char mg, unsigned char mb)
888 {
889 // check that the images are the same size
890 if ( (M_IMGDATA->m_height != mask.GetHeight() ) || (M_IMGDATA->m_width != mask.GetWidth () ) )
891 {
892 wxLogError( _("Image and Mask have different sizes") );
893 return FALSE;
894 }
895
896 // find unused colour
897 unsigned char r,g,b ;
898 if (!FindFirstUnusedColour(&r, &g, &b))
899 {
900 wxLogError( _("No Unused Color in image being masked") );
901 return FALSE ;
902 }
903
904 char unsigned *imgdata = GetData();
905 char unsigned *maskdata = mask.GetData();
906
907 const int w = GetWidth();
908 const int h = GetHeight();
909
910 for (int j = 0; j < h; j++)
911 {
912 for (int i = 0; i < w; i++)
913 {
914 if ((maskdata[0] == mr) && (maskdata[1] == mg) && (maskdata[2] == mb))
915 {
916 imgdata[0] = r;
917 imgdata[1] = g;
918 imgdata[2] = b;
919 }
920 imgdata += 3;
921 maskdata += 3;
922 }
923 }
924
925 SetMaskColour(r, g, b);
926 SetMask(TRUE);
927
928 return TRUE;
929 }
930
931 #if wxUSE_PALETTE
932
933 // Palette functions
934
935 bool wxImage::HasPalette() const
936 {
937 if (!Ok())
938 return FALSE;
939
940 return M_IMGDATA->m_palette.Ok();
941 }
942
943 const wxPalette& wxImage::GetPalette() const
944 {
945 wxCHECK_MSG( Ok(), wxNullPalette, wxT("invalid image") );
946
947 return M_IMGDATA->m_palette;
948 }
949
950 void wxImage::SetPalette(const wxPalette& palette)
951 {
952 wxCHECK_RET( Ok(), wxT("invalid image") );
953
954 M_IMGDATA->m_palette = palette;
955 }
956
957 #endif // wxUSE_PALETTE
958
959 // Option functions (arbitrary name/value mapping)
960 void wxImage::SetOption(const wxString& name, const wxString& value)
961 {
962 wxCHECK_RET( Ok(), wxT("invalid image") );
963
964 int idx = M_IMGDATA->m_optionNames.Index(name, FALSE);
965 if (idx == wxNOT_FOUND)
966 {
967 M_IMGDATA->m_optionNames.Add(name);
968 M_IMGDATA->m_optionValues.Add(value);
969 }
970 else
971 {
972 M_IMGDATA->m_optionNames[idx] = name;
973 M_IMGDATA->m_optionValues[idx] = value;
974 }
975 }
976
977 void wxImage::SetOption(const wxString& name, int value)
978 {
979 wxString valStr;
980 valStr.Printf(wxT("%d"), value);
981 SetOption(name, valStr);
982 }
983
984 wxString wxImage::GetOption(const wxString& name) const
985 {
986 wxCHECK_MSG( Ok(), wxEmptyString, wxT("invalid image") );
987
988 int idx = M_IMGDATA->m_optionNames.Index(name, FALSE);
989 if (idx == wxNOT_FOUND)
990 return wxEmptyString;
991 else
992 return M_IMGDATA->m_optionValues[idx];
993 }
994
995 int wxImage::GetOptionInt(const wxString& name) const
996 {
997 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
998
999 return wxAtoi(GetOption(name));
1000 }
1001
1002 bool wxImage::HasOption(const wxString& name) const
1003 {
1004 wxCHECK_MSG( Ok(), FALSE, wxT("invalid image") );
1005
1006 return (M_IMGDATA->m_optionNames.Index(name, FALSE) != wxNOT_FOUND);
1007 }
1008
1009 bool wxImage::LoadFile( const wxString& filename, long type, int index )
1010 {
1011 #if wxUSE_STREAMS
1012 if (wxFileExists(filename))
1013 {
1014 wxFileInputStream stream(filename);
1015 wxBufferedInputStream bstream( stream );
1016 return LoadFile(bstream, type, index);
1017 }
1018 else
1019 {
1020 wxLogError( _("Can't load image from file '%s': file does not exist."), filename.c_str() );
1021
1022 return FALSE;
1023 }
1024 #else // !wxUSE_STREAMS
1025 return FALSE;
1026 #endif // wxUSE_STREAMS
1027 }
1028
1029 bool wxImage::LoadFile( const wxString& filename, const wxString& mimetype, int index )
1030 {
1031 #if wxUSE_STREAMS
1032 if (wxFileExists(filename))
1033 {
1034 wxFileInputStream stream(filename);
1035 wxBufferedInputStream bstream( stream );
1036 return LoadFile(bstream, mimetype, index);
1037 }
1038 else
1039 {
1040 wxLogError( _("Can't load image from file '%s': file does not exist."), filename.c_str() );
1041
1042 return FALSE;
1043 }
1044 #else // !wxUSE_STREAMS
1045 return FALSE;
1046 #endif // wxUSE_STREAMS
1047 }
1048
1049
1050
1051 bool wxImage::SaveFile( const wxString& filename ) const
1052 {
1053 wxString ext = filename.AfterLast('.').Lower();
1054
1055 wxImageHandler * pHandler = FindHandler(ext, -1);
1056 if (pHandler)
1057 {
1058 SaveFile(filename, pHandler->GetType());
1059 return TRUE;
1060 }
1061
1062 wxLogError(_("Can't save image to file '%s': unknown extension."), filename.c_str());
1063
1064 return FALSE;
1065 }
1066
1067 bool wxImage::SaveFile( const wxString& filename, int type ) const
1068 {
1069 #if wxUSE_STREAMS
1070 ((wxImage*)this)->SetOption(wxIMAGE_OPTION_FILENAME, filename);
1071
1072 wxFileOutputStream stream(filename);
1073
1074 if ( stream.IsOk() )
1075 {
1076 wxBufferedOutputStream bstream( stream );
1077 return SaveFile(bstream, type);
1078 }
1079 #endif // wxUSE_STREAMS
1080
1081 return FALSE;
1082 }
1083
1084 bool wxImage::SaveFile( const wxString& filename, const wxString& mimetype ) const
1085 {
1086 #if wxUSE_STREAMS
1087 ((wxImage*)this)->SetOption(wxIMAGE_OPTION_FILENAME, filename);
1088
1089 wxFileOutputStream stream(filename);
1090
1091 if ( stream.IsOk() )
1092 {
1093 wxBufferedOutputStream bstream( stream );
1094 return SaveFile(bstream, mimetype);
1095 }
1096 #endif // wxUSE_STREAMS
1097
1098 return FALSE;
1099 }
1100
1101 bool wxImage::CanRead( const wxString &name )
1102 {
1103 #if wxUSE_STREAMS
1104 wxFileInputStream stream(name);
1105 return CanRead(stream);
1106 #else
1107 return FALSE;
1108 #endif
1109 }
1110
1111 int wxImage::GetImageCount( const wxString &name, long type )
1112 {
1113 #if wxUSE_STREAMS
1114 wxFileInputStream stream(name);
1115 if (stream.Ok())
1116 return GetImageCount(stream, type);
1117 #endif
1118
1119 return 0;
1120 }
1121
1122 #if wxUSE_STREAMS
1123
1124 bool wxImage::CanRead( wxInputStream &stream )
1125 {
1126 const wxList& list = GetHandlers();
1127
1128 for ( wxList::Node *node = list.GetFirst(); node; node = node->GetNext() )
1129 {
1130 wxImageHandler *handler=(wxImageHandler*)node->GetData();
1131 if (handler->CanRead( stream ))
1132 return TRUE;
1133 }
1134
1135 return FALSE;
1136 }
1137
1138 int wxImage::GetImageCount( wxInputStream &stream, long type )
1139 {
1140 wxImageHandler *handler;
1141
1142 if ( type == wxBITMAP_TYPE_ANY )
1143 {
1144 wxList &list=GetHandlers();
1145
1146 for (wxList::Node *node = list.GetFirst(); node; node = node->GetNext())
1147 {
1148 handler=(wxImageHandler*)node->GetData();
1149 if ( handler->CanRead(stream) )
1150 return handler->GetImageCount(stream);
1151
1152 }
1153
1154 wxLogWarning(_("No handler found for image type."));
1155 return 0;
1156 }
1157
1158 handler = FindHandler(type);
1159
1160 if ( !handler )
1161 {
1162 wxLogWarning(_("No image handler for type %d defined."), type);
1163 return FALSE;
1164 }
1165
1166 if ( handler->CanRead(stream) )
1167 {
1168 return handler->GetImageCount(stream);
1169 }
1170 else
1171 {
1172 wxLogError(_("Image file is not of type %d."), type);
1173 return 0;
1174 }
1175 }
1176
1177 bool wxImage::LoadFile( wxInputStream& stream, long type, int index )
1178 {
1179 UnRef();
1180
1181 m_refData = new wxImageRefData;
1182
1183 wxImageHandler *handler;
1184
1185 if ( type == wxBITMAP_TYPE_ANY )
1186 {
1187 wxList &list=GetHandlers();
1188
1189 for ( wxList::Node *node = list.GetFirst(); node; node = node->GetNext() )
1190 {
1191 handler=(wxImageHandler*)node->GetData();
1192 if ( handler->CanRead(stream) )
1193 return handler->LoadFile(this, stream, TRUE/*verbose*/, index);
1194
1195 }
1196
1197 wxLogWarning( _("No handler found for image type.") );
1198 return FALSE;
1199 }
1200
1201 handler = FindHandler(type);
1202
1203 if (handler == 0)
1204 {
1205 wxLogWarning( _("No image handler for type %d defined."), type );
1206
1207 return FALSE;
1208 }
1209
1210 return handler->LoadFile(this, stream, TRUE/*verbose*/, index);
1211 }
1212
1213 bool wxImage::LoadFile( wxInputStream& stream, const wxString& mimetype, int index )
1214 {
1215 UnRef();
1216
1217 m_refData = new wxImageRefData;
1218
1219 wxImageHandler *handler = FindHandlerMime(mimetype);
1220
1221 if (handler == 0)
1222 {
1223 wxLogWarning( _("No image handler for type %s defined."), mimetype.GetData() );
1224
1225 return FALSE;
1226 }
1227
1228 return handler->LoadFile( this, stream, TRUE/*verbose*/, index );
1229 }
1230
1231 bool wxImage::SaveFile( wxOutputStream& stream, int type ) const
1232 {
1233 wxCHECK_MSG( Ok(), FALSE, wxT("invalid image") );
1234
1235 wxImageHandler *handler = FindHandler(type);
1236
1237 if (handler == 0)
1238 {
1239 wxLogWarning( _("No image handler for type %d defined."), type );
1240
1241 return FALSE;
1242 }
1243
1244 return handler->SaveFile( (wxImage*)this, stream );
1245 }
1246
1247 bool wxImage::SaveFile( wxOutputStream& stream, const wxString& mimetype ) const
1248 {
1249 wxCHECK_MSG( Ok(), FALSE, wxT("invalid image") );
1250
1251 wxImageHandler *handler = FindHandlerMime(mimetype);
1252
1253 if (handler == 0)
1254 {
1255 wxLogWarning( _("No image handler for type %s defined."), mimetype.GetData() );
1256
1257 return FALSE;
1258 }
1259
1260 return handler->SaveFile( (wxImage*)this, stream );
1261 }
1262 #endif // wxUSE_STREAMS
1263
1264 void wxImage::AddHandler( wxImageHandler *handler )
1265 {
1266 // make sure that the memory will be freed at the program end
1267 sm_handlers.DeleteContents(TRUE);
1268
1269 // Check for an existing handler of the type being added.
1270 if (FindHandler( handler->GetType() ) == 0)
1271 {
1272 sm_handlers.Append( handler );
1273 }
1274 else
1275 {
1276 // This is not documented behaviour, merely the simplest 'fix'
1277 // for preventing duplicate additions. If someone ever has
1278 // a good reason to add and remove duplicate handlers (and they
1279 // may) we should probably refcount the duplicates.
1280 // also an issue in InsertHandler below.
1281
1282 wxLogDebug( _T("Adding duplicate image handler for '%s'"),
1283 handler->GetName().c_str() );
1284 delete handler;
1285 }
1286 }
1287
1288 void wxImage::InsertHandler( wxImageHandler *handler )
1289 {
1290 // make sure that the memory will be freed at the program end
1291 sm_handlers.DeleteContents(TRUE);
1292
1293 // Check for an existing handler of the type being added.
1294 if (FindHandler( handler->GetType() ) == 0)
1295 {
1296 sm_handlers.Insert( handler );
1297 }
1298 else
1299 {
1300 // see AddHandler for additional comments.
1301 wxLogDebug( _T("Inserting duplicate image handler for '%s'"),
1302 handler->GetName().c_str() );
1303 delete handler;
1304 }
1305 }
1306
1307 bool wxImage::RemoveHandler( const wxString& name )
1308 {
1309 wxImageHandler *handler = FindHandler(name);
1310 if (handler)
1311 {
1312 sm_handlers.DeleteObject(handler);
1313 return TRUE;
1314 }
1315 else
1316 return FALSE;
1317 }
1318
1319 wxImageHandler *wxImage::FindHandler( const wxString& name )
1320 {
1321 wxNode *node = sm_handlers.GetFirst();
1322 while (node)
1323 {
1324 wxImageHandler *handler = (wxImageHandler*)node->GetData();
1325 if (handler->GetName().Cmp(name) == 0) return handler;
1326
1327 node = node->GetNext();
1328 }
1329 return 0;
1330 }
1331
1332 wxImageHandler *wxImage::FindHandler( const wxString& extension, long bitmapType )
1333 {
1334 wxNode *node = sm_handlers.GetFirst();
1335 while (node)
1336 {
1337 wxImageHandler *handler = (wxImageHandler*)node->GetData();
1338 if ( (handler->GetExtension().Cmp(extension) == 0) &&
1339 (bitmapType == -1 || handler->GetType() == bitmapType) )
1340 return handler;
1341 node = node->GetNext();
1342 }
1343 return 0;
1344 }
1345
1346 wxImageHandler *wxImage::FindHandler( long bitmapType )
1347 {
1348 wxNode *node = sm_handlers.GetFirst();
1349 while (node)
1350 {
1351 wxImageHandler *handler = (wxImageHandler *)node->GetData();
1352 if (handler->GetType() == bitmapType) return handler;
1353 node = node->GetNext();
1354 }
1355 return 0;
1356 }
1357
1358 wxImageHandler *wxImage::FindHandlerMime( const wxString& mimetype )
1359 {
1360 wxNode *node = sm_handlers.GetFirst();
1361 while (node)
1362 {
1363 wxImageHandler *handler = (wxImageHandler *)node->GetData();
1364 if (handler->GetMimeType().IsSameAs(mimetype, FALSE)) return handler;
1365 node = node->GetNext();
1366 }
1367 return 0;
1368 }
1369
1370 void wxImage::InitStandardHandlers()
1371 {
1372 #if wxUSE_STREAMS
1373 AddHandler(new wxBMPHandler);
1374 #endif // wxUSE_STREAMS
1375 }
1376
1377 void wxImage::CleanUpHandlers()
1378 {
1379 wxNode *node = sm_handlers.GetFirst();
1380 while (node)
1381 {
1382 wxImageHandler *handler = (wxImageHandler *)node->GetData();
1383 wxNode *next = node->GetNext();
1384 delete handler;
1385 delete node;
1386 node = next;
1387 }
1388 }
1389
1390 //-----------------------------------------------------------------------------
1391 // wxImageHandler
1392 //-----------------------------------------------------------------------------
1393
1394 IMPLEMENT_ABSTRACT_CLASS(wxImageHandler,wxObject)
1395
1396 #if wxUSE_STREAMS
1397 bool wxImageHandler::LoadFile( wxImage *WXUNUSED(image), wxInputStream& WXUNUSED(stream), bool WXUNUSED(verbose), int WXUNUSED(index) )
1398 {
1399 return FALSE;
1400 }
1401
1402 bool wxImageHandler::SaveFile( wxImage *WXUNUSED(image), wxOutputStream& WXUNUSED(stream), bool WXUNUSED(verbose) )
1403 {
1404 return FALSE;
1405 }
1406
1407 int wxImageHandler::GetImageCount( wxInputStream& WXUNUSED(stream) )
1408 {
1409 return 1;
1410 }
1411
1412 bool wxImageHandler::CanRead( const wxString& name )
1413 {
1414 if (wxFileExists(name))
1415 {
1416 wxFileInputStream stream(name);
1417 return CanRead(stream);
1418 }
1419
1420 wxLogError( _("Can't check image format of file '%s': file does not exist."), name.c_str() );
1421
1422 return FALSE;
1423 }
1424
1425 bool wxImageHandler::CallDoCanRead(wxInputStream& stream)
1426 {
1427 off_t posOld = stream.TellI();
1428 if ( posOld == wxInvalidOffset )
1429 {
1430 // can't test unseekable stream
1431 return FALSE;
1432 }
1433
1434 bool ok = DoCanRead(stream);
1435
1436 // restore the old position to be able to test other formats and so on
1437 if ( stream.SeekI(posOld) == wxInvalidOffset )
1438 {
1439 wxLogDebug(_T("Failed to rewind the stream in wxImageHandler!"));
1440
1441 // reading would fail anyhow as we're not at the right position
1442 return FALSE;
1443 }
1444
1445 return ok;
1446 }
1447
1448 #endif // wxUSE_STREAMS
1449
1450
1451
1452 //-----------------------------------------------------------------------------
1453 // Deprecated wxBitmap conversion routines
1454 //-----------------------------------------------------------------------------
1455
1456 #if WXWIN_COMPATIBILITY_2_2 && wxUSE_GUI
1457
1458 #ifdef __WXGTK__
1459 wxBitmap wxImage::ConvertToMonoBitmap( unsigned char red, unsigned char green, unsigned char blue ) const
1460 {
1461 wxImage mono = this->ConvertToMono( red, green, blue );
1462 wxBitmap bitmap( mono, 1 );
1463 return bitmap;
1464 }
1465 #endif
1466
1467 wxBitmap wxImage::ConvertToBitmap() const
1468 {
1469 wxBitmap bitmap( *this );
1470 return bitmap;
1471 }
1472
1473 wxImage::wxImage( const wxBitmap &bitmap )
1474 {
1475 *this = bitmap.ConvertToImage();
1476 }
1477
1478 #endif // WXWIN_COMPATIBILITY_2_2 && wxUSE_GUI
1479
1480
1481 //-----------------------------------------------------------------------------
1482
1483 // GRG, Dic/99
1484 // Counts and returns the number of different colours. Optionally stops
1485 // when it exceeds 'stopafter' different colours. This is useful, for
1486 // example, to see if the image can be saved as 8-bit (256 colour or
1487 // less, in this case it would be invoked as CountColours(256)). Default
1488 // value for stopafter is -1 (don't care).
1489 //
1490 unsigned long wxImage::CountColours( unsigned long stopafter ) const
1491 {
1492 wxHashTable h;
1493 wxObject dummy;
1494 unsigned char r, g, b;
1495 unsigned char *p;
1496 unsigned long size, nentries, key;
1497
1498 p = GetData();
1499 size = GetWidth() * GetHeight();
1500 nentries = 0;
1501
1502 for (unsigned long j = 0; (j < size) && (nentries <= stopafter) ; j++)
1503 {
1504 r = *(p++);
1505 g = *(p++);
1506 b = *(p++);
1507 key = (r << 16) | (g << 8) | b;
1508
1509 if (h.Get(key) == NULL)
1510 {
1511 h.Put(key, &dummy);
1512 nentries++;
1513 }
1514 }
1515
1516 return nentries;
1517 }
1518
1519
1520 unsigned long wxImage::ComputeHistogram( wxImageHistogram &h ) const
1521 {
1522 unsigned char r, g, b;
1523 unsigned char *p;
1524 unsigned long size, nentries, key;
1525
1526 h.clear();
1527
1528 p = GetData();
1529 size = GetWidth() * GetHeight();
1530 nentries = 0;
1531
1532 for (unsigned long j = 0; j < size; j++)
1533 {
1534 r = *(p++);
1535 g = *(p++);
1536 b = *(p++);
1537 key = (r << 16) | (g << 8) | b;
1538
1539 wxImageHistogramEntry& entry = h[key];
1540 if ( entry.value++ == 0 )
1541 entry.index = nentries++;
1542 }
1543
1544 return nentries;
1545 }
1546
1547 /*
1548 * Rotation code by Carlos Moreno
1549 */
1550
1551 // GRG: I've removed wxRotationPoint - we already have wxRealPoint which
1552 // does exactly the same thing. And I also got rid of wxRotationPixel
1553 // bacause of potential problems in architectures where alignment
1554 // is an issue, so I had to rewrite parts of the code.
1555
1556 static const double gs_Epsilon = 1e-10;
1557
1558 static inline int wxCint (double x)
1559 {
1560 return (x > 0) ? (int) (x + 0.5) : (int) (x - 0.5);
1561 }
1562
1563
1564 // Auxiliary function to rotate a point (x,y) with respect to point p0
1565 // make it inline and use a straight return to facilitate optimization
1566 // also, the function receives the sine and cosine of the angle to avoid
1567 // repeating the time-consuming calls to these functions -- sin/cos can
1568 // be computed and stored in the calling function.
1569
1570 inline wxRealPoint rotated_point (const wxRealPoint & p, double cos_angle, double sin_angle, const wxRealPoint & p0)
1571 {
1572 return wxRealPoint (p0.x + (p.x - p0.x) * cos_angle - (p.y - p0.y) * sin_angle,
1573 p0.y + (p.y - p0.y) * cos_angle + (p.x - p0.x) * sin_angle);
1574 }
1575
1576 inline wxRealPoint rotated_point (double x, double y, double cos_angle, double sin_angle, const wxRealPoint & p0)
1577 {
1578 return rotated_point (wxRealPoint(x,y), cos_angle, sin_angle, p0);
1579 }
1580
1581 wxImage wxImage::Rotate(double angle, const wxPoint & centre_of_rotation, bool interpolating, wxPoint * offset_after_rotation) const
1582 {
1583 int i;
1584 angle = -angle; // screen coordinates are a mirror image of "real" coordinates
1585
1586 // Create pointer-based array to accelerate access to wxImage's data
1587 unsigned char ** data = new unsigned char * [GetHeight()];
1588
1589 data[0] = GetData();
1590
1591 for (i = 1; i < GetHeight(); i++)
1592 data[i] = data[i - 1] + (3 * GetWidth());
1593
1594 // precompute coefficients for rotation formula
1595 // (sine and cosine of the angle)
1596 const double cos_angle = cos(angle);
1597 const double sin_angle = sin(angle);
1598
1599 // Create new Image to store the result
1600 // First, find rectangle that covers the rotated image; to do that,
1601 // rotate the four corners
1602
1603 const wxRealPoint p0(centre_of_rotation.x, centre_of_rotation.y);
1604
1605 wxRealPoint p1 = rotated_point (0, 0, cos_angle, sin_angle, p0);
1606 wxRealPoint p2 = rotated_point (0, GetHeight(), cos_angle, sin_angle, p0);
1607 wxRealPoint p3 = rotated_point (GetWidth(), 0, cos_angle, sin_angle, p0);
1608 wxRealPoint p4 = rotated_point (GetWidth(), GetHeight(), cos_angle, sin_angle, p0);
1609
1610 int x1 = (int) floor (wxMin (wxMin(p1.x, p2.x), wxMin(p3.x, p4.x)));
1611 int y1 = (int) floor (wxMin (wxMin(p1.y, p2.y), wxMin(p3.y, p4.y)));
1612 int x2 = (int) ceil (wxMax (wxMax(p1.x, p2.x), wxMax(p3.x, p4.x)));
1613 int y2 = (int) ceil (wxMax (wxMax(p1.y, p2.y), wxMax(p3.y, p4.y)));
1614
1615 wxImage rotated (x2 - x1 + 1, y2 - y1 + 1);
1616
1617 if (offset_after_rotation != NULL)
1618 {
1619 *offset_after_rotation = wxPoint (x1, y1);
1620 }
1621
1622 // GRG: The rotated (destination) image is always accessed
1623 // sequentially, so there is no need for a pointer-based
1624 // array here (and in fact it would be slower).
1625 //
1626 unsigned char * dst = rotated.GetData();
1627
1628 // GRG: if the original image has a mask, use its RGB values
1629 // as the blank pixel, else, fall back to default (black).
1630 //
1631 unsigned char blank_r = 0;
1632 unsigned char blank_g = 0;
1633 unsigned char blank_b = 0;
1634
1635 if (HasMask())
1636 {
1637 blank_r = GetMaskRed();
1638 blank_g = GetMaskGreen();
1639 blank_b = GetMaskBlue();
1640 rotated.SetMaskColour( blank_r, blank_g, blank_b );
1641 }
1642
1643 // Now, for each point of the rotated image, find where it came from, by
1644 // performing an inverse rotation (a rotation of -angle) and getting the
1645 // pixel at those coordinates
1646
1647 // GRG: I've taken the (interpolating) test out of the loops, so that
1648 // it is done only once, instead of repeating it for each pixel.
1649
1650 int x;
1651 if (interpolating)
1652 {
1653 for (int y = 0; y < rotated.GetHeight(); y++)
1654 {
1655 for (x = 0; x < rotated.GetWidth(); x++)
1656 {
1657 wxRealPoint src = rotated_point (x + x1, y + y1, cos_angle, -sin_angle, p0);
1658
1659 if (-0.25 < src.x && src.x < GetWidth() - 0.75 &&
1660 -0.25 < src.y && src.y < GetHeight() - 0.75)
1661 {
1662 // interpolate using the 4 enclosing grid-points. Those
1663 // points can be obtained using floor and ceiling of the
1664 // exact coordinates of the point
1665 // C.M. 2000-02-17: when the point is near the border, special care is required.
1666
1667 int x1, y1, x2, y2;
1668
1669 if (0 < src.x && src.x < GetWidth() - 1)
1670 {
1671 x1 = wxCint(floor(src.x));
1672 x2 = wxCint(ceil(src.x));
1673 }
1674 else // else means that x is near one of the borders (0 or width-1)
1675 {
1676 x1 = x2 = wxCint (src.x);
1677 }
1678
1679 if (0 < src.y && src.y < GetHeight() - 1)
1680 {
1681 y1 = wxCint(floor(src.y));
1682 y2 = wxCint(ceil(src.y));
1683 }
1684 else
1685 {
1686 y1 = y2 = wxCint (src.y);
1687 }
1688
1689 // get four points and the distances (square of the distance,
1690 // for efficiency reasons) for the interpolation formula
1691
1692 // GRG: Do not calculate the points until they are
1693 // really needed -- this way we can calculate
1694 // just one, instead of four, if d1, d2, d3
1695 // or d4 are < gs_Epsilon
1696
1697 const double d1 = (src.x - x1) * (src.x - x1) + (src.y - y1) * (src.y - y1);
1698 const double d2 = (src.x - x2) * (src.x - x2) + (src.y - y1) * (src.y - y1);
1699 const double d3 = (src.x - x2) * (src.x - x2) + (src.y - y2) * (src.y - y2);
1700 const double d4 = (src.x - x1) * (src.x - x1) + (src.y - y2) * (src.y - y2);
1701
1702 // Now interpolate as a weighted average of the four surrounding
1703 // points, where the weights are the distances to each of those points
1704
1705 // If the point is exactly at one point of the grid of the source
1706 // image, then don't interpolate -- just assign the pixel
1707
1708 if (d1 < gs_Epsilon) // d1,d2,d3,d4 are positive -- no need for abs()
1709 {
1710 unsigned char *p = data[y1] + (3 * x1);
1711 *(dst++) = *(p++);
1712 *(dst++) = *(p++);
1713 *(dst++) = *(p++);
1714 }
1715 else if (d2 < gs_Epsilon)
1716 {
1717 unsigned char *p = data[y1] + (3 * x2);
1718 *(dst++) = *(p++);
1719 *(dst++) = *(p++);
1720 *(dst++) = *(p++);
1721 }
1722 else if (d3 < gs_Epsilon)
1723 {
1724 unsigned char *p = data[y2] + (3 * x2);
1725 *(dst++) = *(p++);
1726 *(dst++) = *(p++);
1727 *(dst++) = *(p++);
1728 }
1729 else if (d4 < gs_Epsilon)
1730 {
1731 unsigned char *p = data[y2] + (3 * x1);
1732 *(dst++) = *(p++);
1733 *(dst++) = *(p++);
1734 *(dst++) = *(p++);
1735 }
1736 else
1737 {
1738 // weights for the weighted average are proportional to the inverse of the distance
1739 unsigned char *v1 = data[y1] + (3 * x1);
1740 unsigned char *v2 = data[y1] + (3 * x2);
1741 unsigned char *v3 = data[y2] + (3 * x2);
1742 unsigned char *v4 = data[y2] + (3 * x1);
1743
1744 const double w1 = 1/d1, w2 = 1/d2, w3 = 1/d3, w4 = 1/d4;
1745
1746 // GRG: Unrolled.
1747
1748 *(dst++) = (unsigned char)
1749 ( (w1 * *(v1++) + w2 * *(v2++) +
1750 w3 * *(v3++) + w4 * *(v4++)) /
1751 (w1 + w2 + w3 + w4) );
1752 *(dst++) = (unsigned char)
1753 ( (w1 * *(v1++) + w2 * *(v2++) +
1754 w3 * *(v3++) + w4 * *(v4++)) /
1755 (w1 + w2 + w3 + w4) );
1756 *(dst++) = (unsigned char)
1757 ( (w1 * *(v1++) + w2 * *(v2++) +
1758 w3 * *(v3++) + w4 * *(v4++)) /
1759 (w1 + w2 + w3 + w4) );
1760 }
1761 }
1762 else
1763 {
1764 *(dst++) = blank_r;
1765 *(dst++) = blank_g;
1766 *(dst++) = blank_b;
1767 }
1768 }
1769 }
1770 }
1771 else // not interpolating
1772 {
1773 for (int y = 0; y < rotated.GetHeight(); y++)
1774 {
1775 for (x = 0; x < rotated.GetWidth(); x++)
1776 {
1777 wxRealPoint src = rotated_point (x + x1, y + y1, cos_angle, -sin_angle, p0);
1778
1779 const int xs = wxCint (src.x); // wxCint rounds to the
1780 const int ys = wxCint (src.y); // closest integer
1781
1782 if (0 <= xs && xs < GetWidth() &&
1783 0 <= ys && ys < GetHeight())
1784 {
1785 unsigned char *p = data[ys] + (3 * xs);
1786 *(dst++) = *(p++);
1787 *(dst++) = *(p++);
1788 *(dst++) = *(p++);
1789 }
1790 else
1791 {
1792 *(dst++) = blank_r;
1793 *(dst++) = blank_g;
1794 *(dst++) = blank_b;
1795 }
1796 }
1797 }
1798 }
1799
1800 delete [] data;
1801
1802 return rotated;
1803 }
1804
1805
1806
1807
1808
1809 // A module to allow wxImage initialization/cleanup
1810 // without calling these functions from app.cpp or from
1811 // the user's application.
1812
1813 class wxImageModule: public wxModule
1814 {
1815 DECLARE_DYNAMIC_CLASS(wxImageModule)
1816 public:
1817 wxImageModule() {}
1818 bool OnInit() { wxImage::InitStandardHandlers(); return TRUE; };
1819 void OnExit() { wxImage::CleanUpHandlers(); };
1820 };
1821
1822 IMPLEMENT_DYNAMIC_CLASS(wxImageModule, wxModule)
1823
1824
1825 #endif // wxUSE_IMAGE