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