1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/common/image.cpp
3 // Purpose:     wxImage
4 // Author:      Robert Roebling
5 // RCS-ID:      $Id: image.cpp 59197 2009-02-28 15:44:53Z VZ $
6 // Copyright:   (c) Robert Roebling
7 // Licence:     wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9 
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12 
13 #ifdef __BORLANDC__
14     #pragma hdrstop
15 #endif
16 
17 #if wxUSE_IMAGE
18 
19 #include "wx/image.h"
20 
21 #ifndef WX_PRECOMP
22     #include "wx/log.h"
23     #include "wx/hash.h"
24     #include "wx/utils.h"
25     #include "wx/math.h"
26     #include "wx/module.h"
27     #include "wx/palette.h"
28     #include "wx/intl.h"
29 #endif
30 
31 #include "wx/filefn.h"
32 #include "wx/wfstream.h"
33 #include "wx/xpmdecod.h"
34 
35 // For memcpy
36 #include <string.h>
37 
38 // make the code compile with either wxFile*Stream or wxFFile*Stream:
39 #define HAS_FILE_STREAMS (wxUSE_STREAMS && (wxUSE_FILE || wxUSE_FFILE))
40 
41 #if HAS_FILE_STREAMS
42     #if wxUSE_FFILE
43         typedef wxFFileInputStream wxImageFileInputStream;
44         typedef wxFFileOutputStream wxImageFileOutputStream;
45     #elif wxUSE_FILE
46         typedef wxFileInputStream wxImageFileInputStream;
47         typedef wxFileOutputStream wxImageFileOutputStream;
48     #endif // wxUSE_FILE/wxUSE_FFILE
49 #endif // HAS_FILE_STREAMS
50 
51 #if wxUSE_VARIANT
52 IMPLEMENT_VARIANT_OBJECT_EXPORTED_SHALLOWCMP(wxImage,WXDLLEXPORT)
53 #endif
54 
55 //-----------------------------------------------------------------------------
56 // wxImage
57 //-----------------------------------------------------------------------------
58 
59 class wxImageRefData: public wxObjectRefData
60 {
61 public:
62     wxImageRefData();
63     virtual ~wxImageRefData();
64 
65     int             m_width;
66     int             m_height;
67     unsigned char  *m_data;
68 
69     bool            m_hasMask;
70     unsigned char   m_maskRed,m_maskGreen,m_maskBlue;
71 
72     // alpha channel data, may be NULL for the formats without alpha support
73     unsigned char  *m_alpha;
74 
75     bool            m_ok;
76 
77     // if true, m_data is pointer to static data and shouldn't be freed
78     bool            m_static;
79 
80     // same as m_static but for m_alpha
81     bool            m_staticAlpha;
82 
83 #if wxUSE_PALETTE
84     wxPalette       m_palette;
85 #endif // wxUSE_PALETTE
86 
87     wxArrayString   m_optionNames;
88     wxArrayString   m_optionValues;
89 
90     DECLARE_NO_COPY_CLASS(wxImageRefData)
91 };
92 
wxImageRefData()93 wxImageRefData::wxImageRefData()
94 {
95     m_width = 0;
96     m_height = 0;
97     m_data =
98     m_alpha = (unsigned char *) NULL;
99 
100     m_maskRed = 0;
101     m_maskGreen = 0;
102     m_maskBlue = 0;
103     m_hasMask = false;
104 
105     m_ok = false;
106     m_static =
107     m_staticAlpha = false;
108 }
109 
~wxImageRefData()110 wxImageRefData::~wxImageRefData()
111 {
112     if ( !m_static )
113         free( m_data );
114     if ( !m_staticAlpha )
115         free( m_alpha );
116 }
117 
118 wxList wxImage::sm_handlers;
119 
120 wxImage wxNullImage;
121 
122 //-----------------------------------------------------------------------------
123 
124 #define M_IMGDATA wx_static_cast(wxImageRefData*, m_refData)
125 
IMPLEMENT_DYNAMIC_CLASS(wxImage,wxObject)126 IMPLEMENT_DYNAMIC_CLASS(wxImage, wxObject)
127 
128 wxImage::wxImage( int width, int height, bool clear )
129 {
130     Create( width, height, clear );
131 }
132 
wxImage(int width,int height,unsigned char * data,bool static_data)133 wxImage::wxImage( int width, int height, unsigned char* data, bool static_data )
134 {
135     Create( width, height, data, static_data );
136 }
137 
wxImage(int width,int height,unsigned char * data,unsigned char * alpha,bool static_data)138 wxImage::wxImage( int width, int height, unsigned char* data, unsigned char* alpha, bool static_data )
139 {
140     Create( width, height, data, alpha, static_data );
141 }
142 
wxImage(const wxString & name,long type,int index)143 wxImage::wxImage( const wxString& name, long type, int index )
144 {
145     LoadFile( name, type, index );
146 }
147 
wxImage(const wxString & name,const wxString & mimetype,int index)148 wxImage::wxImage( const wxString& name, const wxString& mimetype, int index )
149 {
150     LoadFile( name, mimetype, index );
151 }
152 
153 #if wxUSE_STREAMS
wxImage(wxInputStream & stream,long type,int index)154 wxImage::wxImage( wxInputStream& stream, long type, int index )
155 {
156     LoadFile( stream, type, index );
157 }
158 
wxImage(wxInputStream & stream,const wxString & mimetype,int index)159 wxImage::wxImage( wxInputStream& stream, const wxString& mimetype, int index )
160 {
161     LoadFile( stream, mimetype, index );
162 }
163 #endif // wxUSE_STREAMS
164 
wxImage(const char * const * xpmData)165 wxImage::wxImage(const char* const* xpmData)
166 {
167     Create(xpmData);
168 }
169 
Create(const char * const * xpmData)170 bool wxImage::Create(const char* const* xpmData)
171 {
172 #if wxUSE_XPM
173     UnRef();
174 
175     wxXPMDecoder decoder;
176     (*this) = decoder.ReadData(xpmData);
177     return Ok();
178 #else
179     return false;
180 #endif
181 }
182 
Create(int width,int height,bool clear)183 bool wxImage::Create( int width, int height, bool clear )
184 {
185     UnRef();
186 
187     m_refData = new wxImageRefData();
188 
189     M_IMGDATA->m_data = (unsigned char *) malloc( width*height*3 );
190     if (!M_IMGDATA->m_data)
191     {
192         UnRef();
193         return false;
194     }
195 
196     if (clear)
197         memset(M_IMGDATA->m_data, 0, width*height*3);
198 
199     M_IMGDATA->m_width = width;
200     M_IMGDATA->m_height = height;
201     M_IMGDATA->m_ok = true;
202 
203     return true;
204 }
205 
Create(int width,int height,unsigned char * data,bool static_data)206 bool wxImage::Create( int width, int height, unsigned char* data, bool static_data )
207 {
208     UnRef();
209 
210     wxCHECK_MSG( data, false, _T("NULL data in wxImage::Create") );
211 
212     m_refData = new wxImageRefData();
213 
214     M_IMGDATA->m_data = data;
215     M_IMGDATA->m_width = width;
216     M_IMGDATA->m_height = height;
217     M_IMGDATA->m_ok = true;
218     M_IMGDATA->m_static = static_data;
219 
220     return true;
221 }
222 
Create(int width,int height,unsigned char * data,unsigned char * alpha,bool static_data)223 bool wxImage::Create( int width, int height, unsigned char* data, unsigned char* alpha, bool static_data )
224 {
225     UnRef();
226 
227     wxCHECK_MSG( data, false, _T("NULL data in wxImage::Create") );
228 
229     m_refData = new wxImageRefData();
230 
231     M_IMGDATA->m_data = data;
232     M_IMGDATA->m_alpha = alpha;
233     M_IMGDATA->m_width = width;
234     M_IMGDATA->m_height = height;
235     M_IMGDATA->m_ok = true;
236     M_IMGDATA->m_static = static_data;
237     M_IMGDATA->m_staticAlpha = static_data;
238 
239     return true;
240 }
241 
Destroy()242 void wxImage::Destroy()
243 {
244     UnRef();
245 }
246 
CreateRefData() const247 wxObjectRefData* wxImage::CreateRefData() const
248 {
249     return new wxImageRefData;
250 }
251 
CloneRefData(const wxObjectRefData * that) const252 wxObjectRefData* wxImage::CloneRefData(const wxObjectRefData* that) const
253 {
254     const wxImageRefData* refData = wx_static_cast(const wxImageRefData*, that);
255     wxCHECK_MSG(refData->m_ok, NULL, wxT("invalid image") );
256 
257     wxImageRefData* refData_new = new wxImageRefData;
258     refData_new->m_width = refData->m_width;
259     refData_new->m_height = refData->m_height;
260     refData_new->m_maskRed = refData->m_maskRed;
261     refData_new->m_maskGreen = refData->m_maskGreen;
262     refData_new->m_maskBlue = refData->m_maskBlue;
263     refData_new->m_hasMask = refData->m_hasMask;
264     refData_new->m_ok = true;
265     unsigned size = unsigned(refData->m_width) * unsigned(refData->m_height);
266     if (refData->m_alpha != NULL)
267     {
268         refData_new->m_alpha = (unsigned char*)malloc(size);
269         memcpy(refData_new->m_alpha, refData->m_alpha, size);
270     }
271     size *= 3;
272     refData_new->m_data = (unsigned char*)malloc(size);
273     memcpy(refData_new->m_data, refData->m_data, size);
274 #if wxUSE_PALETTE
275     refData_new->m_palette = refData->m_palette;
276 #endif
277     refData_new->m_optionNames = refData->m_optionNames;
278     refData_new->m_optionValues = refData->m_optionValues;
279     return refData_new;
280 }
281 
Copy() const282 wxImage wxImage::Copy() const
283 {
284     wxImage image;
285 
286     wxCHECK_MSG( Ok(), image, wxT("invalid image") );
287 
288     image.m_refData = CloneRefData(m_refData);
289 
290     return image;
291 }
292 
ShrinkBy(int xFactor,int yFactor) const293 wxImage wxImage::ShrinkBy( int xFactor , int yFactor ) const
294 {
295     if( xFactor == 1 && yFactor == 1 )
296         return *this;
297 
298     wxImage image;
299 
300     wxCHECK_MSG( Ok(), image, wxT("invalid image") );
301 
302     // can't scale to/from 0 size
303     wxCHECK_MSG( (xFactor > 0) && (yFactor > 0), image,
304                  wxT("invalid new image size") );
305 
306     long old_height = M_IMGDATA->m_height,
307          old_width  = M_IMGDATA->m_width;
308 
309     wxCHECK_MSG( (old_height > 0) && (old_width > 0), image,
310                  wxT("invalid old image size") );
311 
312     long width = old_width / xFactor ;
313     long height = old_height / yFactor ;
314 
315     image.Create( width, height, false );
316 
317     char unsigned *data = image.GetData();
318 
319     wxCHECK_MSG( data, image, wxT("unable to create image") );
320 
321     bool hasMask = false ;
322     unsigned char maskRed = 0;
323     unsigned char maskGreen = 0;
324     unsigned char maskBlue =0 ;
325 
326     unsigned char *source_data = M_IMGDATA->m_data;
327     unsigned char *target_data = data;
328     unsigned char *source_alpha = 0 ;
329     unsigned char *target_alpha = 0 ;
330     if (M_IMGDATA->m_hasMask)
331     {
332         hasMask = true ;
333         maskRed = M_IMGDATA->m_maskRed;
334         maskGreen = M_IMGDATA->m_maskGreen;
335         maskBlue =M_IMGDATA->m_maskBlue ;
336 
337         image.SetMaskColour( M_IMGDATA->m_maskRed,
338                              M_IMGDATA->m_maskGreen,
339                              M_IMGDATA->m_maskBlue );
340     }
341     else
342     {
343         source_alpha = M_IMGDATA->m_alpha ;
344         if ( source_alpha )
345         {
346             image.SetAlpha() ;
347             target_alpha = image.GetAlpha() ;
348         }
349     }
350 
351     for (long y = 0; y < height; y++)
352     {
353         for (long x = 0; x < width; x++)
354         {
355             unsigned long avgRed = 0 ;
356             unsigned long avgGreen = 0;
357             unsigned long avgBlue = 0;
358             unsigned long avgAlpha = 0 ;
359             unsigned long counter = 0 ;
360             // determine average
361             for ( int y1 = 0 ; y1 < yFactor ; ++y1 )
362             {
363                 long y_offset = (y * yFactor + y1) * old_width;
364                 for ( int x1 = 0 ; x1 < xFactor ; ++x1 )
365                 {
366                     unsigned char *pixel = source_data + 3 * ( y_offset + x * xFactor + x1 ) ;
367                     unsigned char red = pixel[0] ;
368                     unsigned char green = pixel[1] ;
369                     unsigned char blue = pixel[2] ;
370                     unsigned char alpha = 255  ;
371                     if ( source_alpha )
372                         alpha = *(source_alpha + y_offset + x * xFactor + x1) ;
373                     if ( !hasMask || red != maskRed || green != maskGreen || blue != maskBlue )
374                     {
375                         if ( alpha > 0 )
376                         {
377                             avgRed += red ;
378                             avgGreen += green ;
379                             avgBlue += blue ;
380                         }
381                         avgAlpha += alpha ;
382                         counter++ ;
383                     }
384                 }
385             }
386             if ( counter == 0 )
387             {
388                 *(target_data++) = M_IMGDATA->m_maskRed ;
389                 *(target_data++) = M_IMGDATA->m_maskGreen ;
390                 *(target_data++) = M_IMGDATA->m_maskBlue ;
391             }
392             else
393             {
394                 if ( source_alpha )
395                     *(target_alpha++) = (unsigned char)(avgAlpha / counter ) ;
396                 *(target_data++) = (unsigned char)(avgRed / counter);
397                 *(target_data++) = (unsigned char)(avgGreen / counter);
398                 *(target_data++) = (unsigned char)(avgBlue / counter);
399             }
400         }
401     }
402 
403     // In case this is a cursor, make sure the hotspot is scaled accordingly:
404     if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X) )
405         image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X,
406                 (GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X))/xFactor);
407     if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y) )
408         image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y,
409                 (GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y))/yFactor);
410 
411     return image;
412 }
413 
Scale(int width,int height,int quality) const414 wxImage wxImage::Scale( int width, int height, int quality ) const
415 {
416     wxImage image;
417 
418     wxCHECK_MSG( Ok(), image, wxT("invalid image") );
419 
420     // can't scale to/from 0 size
421     wxCHECK_MSG( (width > 0) && (height > 0), image,
422                  wxT("invalid new image size") );
423 
424     long old_height = M_IMGDATA->m_height,
425          old_width  = M_IMGDATA->m_width;
426     wxCHECK_MSG( (old_height > 0) && (old_width > 0), image,
427                  wxT("invalid old image size") );
428 
429     // If the image's new width and height are the same as the original, no
430     // need to waste time or CPU cycles
431     if ( old_width == width && old_height == height )
432         return *this;
433 
434     // Scale the image (...or more appropriately, resample the image) using
435     // either the high-quality or normal method as specified
436     if ( quality == wxIMAGE_QUALITY_HIGH )
437     {
438         // We need to check whether we are downsampling or upsampling the image
439         if ( width < old_width && height < old_height )
440         {
441             // Downsample the image using the box averaging method for best results
442             image = ResampleBox(width, height);
443         }
444         else
445         {
446             // For upsampling or other random/wierd image dimensions we'll use
447             // a bicubic b-spline scaling method
448             image = ResampleBicubic(width, height);
449         }
450     }
451     else    // Default scaling method == simple pixel replication
452     {
453         if ( old_width % width == 0 && old_width >= width &&
454             old_height % height == 0 && old_height >= height )
455         {
456             return ShrinkBy( old_width / width , old_height / height ) ;
457         }
458         image.Create( width, height, false );
459 
460         unsigned char *data = image.GetData();
461 
462         wxCHECK_MSG( data, image, wxT("unable to create image") );
463 
464         unsigned char *source_data = M_IMGDATA->m_data;
465         unsigned char *target_data = data;
466         unsigned char *source_alpha = 0 ;
467         unsigned char *target_alpha = 0 ;
468 
469         if ( !M_IMGDATA->m_hasMask )
470         {
471             source_alpha = M_IMGDATA->m_alpha ;
472             if ( source_alpha )
473             {
474                 image.SetAlpha() ;
475                 target_alpha = image.GetAlpha() ;
476             }
477         }
478 
479         long x_delta = (old_width<<16) / width;
480         long y_delta = (old_height<<16) / height;
481 
482         unsigned char* dest_pixel = target_data;
483 
484         long y = 0;
485         for ( long j = 0; j < height; j++ )
486         {
487             unsigned char* src_line = &source_data[(y>>16)*old_width*3];
488             unsigned char* src_alpha_line = source_alpha ? &source_alpha[(y>>16)*old_width] : 0 ;
489 
490             long x = 0;
491             for ( long i = 0; i < width; i++ )
492             {
493                 unsigned char* src_pixel = &src_line[(x>>16)*3];
494                 unsigned char* src_alpha_pixel = source_alpha ? &src_alpha_line[(x>>16)] : 0 ;
495                 dest_pixel[0] = src_pixel[0];
496                 dest_pixel[1] = src_pixel[1];
497                 dest_pixel[2] = src_pixel[2];
498                 dest_pixel += 3;
499                 if ( source_alpha )
500                     *(target_alpha++) = *src_alpha_pixel ;
501                 x += x_delta;
502             }
503 
504             y += y_delta;
505         }
506     }
507 
508     // If the original image has a mask, apply the mask to the new image
509     if (M_IMGDATA->m_hasMask)
510     {
511         image.SetMaskColour( M_IMGDATA->m_maskRed,
512                             M_IMGDATA->m_maskGreen,
513                             M_IMGDATA->m_maskBlue );
514     }
515 
516     // In case this is a cursor, make sure the hotspot is scaled accordingly:
517     if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X) )
518         image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X,
519                 (GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X)*width)/old_width);
520     if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y) )
521         image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y,
522                 (GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y)*height)/old_height);
523 
524     return image;
525 }
526 
ResampleBox(int width,int height) const527 wxImage wxImage::ResampleBox(int width, int height) const
528 {
529     // This function implements a simple pre-blur/box averaging method for
530     // downsampling that gives reasonably smooth results To scale the image
531     // down we will need to gather a grid of pixels of the size of the scale
532     // factor in each direction and then do an averaging of the pixels.
533 
534     wxImage ret_image(width, height, false);
535 
536     const double scale_factor_x = double(M_IMGDATA->m_width) / width;
537     const double scale_factor_y = double(M_IMGDATA->m_height) / height;
538 
539     const int scale_factor_x_2 = (int)(scale_factor_x / 2);
540     const int scale_factor_y_2 = (int)(scale_factor_y / 2);
541 
542     unsigned char* src_data = M_IMGDATA->m_data;
543     unsigned char* src_alpha = M_IMGDATA->m_alpha;
544     unsigned char* dst_data = ret_image.GetData();
545     unsigned char* dst_alpha = NULL;
546 
547     if ( src_alpha )
548     {
549         ret_image.SetAlpha();
550         dst_alpha = ret_image.GetAlpha();
551     }
552 
553     int averaged_pixels, src_pixel_index;
554     double sum_r, sum_g, sum_b, sum_a;
555 
556     for ( int y = 0; y < height; y++ )         // Destination image - Y direction
557     {
558         // Source pixel in the Y direction
559         int src_y = (int)(y * scale_factor_y);
560 
561         for ( int x = 0; x < width; x++ )      // Destination image - X direction
562         {
563             // Source pixel in the X direction
564             int src_x = (int)(x * scale_factor_x);
565 
566             // Box of pixels to average
567             averaged_pixels = 0;
568             sum_r = sum_g = sum_b = sum_a = 0.0;
569 
570             for ( int j = int(src_y - scale_factor_y/2.0 + 1);
571                   j <= int(src_y + scale_factor_y_2);
572                   j++ )
573             {
574                 // We don't care to average pixels that don't exist (edges)
575                 if ( j < 0 || j > M_IMGDATA->m_height - 1 )
576                     continue;
577 
578                 for ( int i = int(src_x - scale_factor_x/2.0 + 1);
579                       i <= src_x + scale_factor_x_2;
580                       i++ )
581                 {
582                     // Don't average edge pixels
583                     if ( i < 0 || i > M_IMGDATA->m_width - 1 )
584                         continue;
585 
586                     // Calculate the actual index in our source pixels
587                     src_pixel_index = j * M_IMGDATA->m_width + i;
588 
589                     sum_r += src_data[src_pixel_index * 3 + 0];
590                     sum_g += src_data[src_pixel_index * 3 + 1];
591                     sum_b += src_data[src_pixel_index * 3 + 2];
592                     if ( src_alpha )
593                         sum_a += src_alpha[src_pixel_index];
594 
595                     averaged_pixels++;
596                 }
597             }
598 
599             // Calculate the average from the sum and number of averaged pixels
600             dst_data[0] = (unsigned char)(sum_r / averaged_pixels);
601             dst_data[1] = (unsigned char)(sum_g / averaged_pixels);
602             dst_data[2] = (unsigned char)(sum_b / averaged_pixels);
603             dst_data += 3;
604             if ( src_alpha )
605                 *dst_alpha++ = (unsigned char)(sum_a / averaged_pixels);
606         }
607     }
608 
609     return ret_image;
610 }
611 
612 // The following two local functions are for the B-spline weighting of the
613 // bicubic sampling algorithm
spline_cube(double value)614 static inline double spline_cube(double value)
615 {
616     return value <= 0.0 ? 0.0 : value * value * value;
617 }
618 
spline_weight(double value)619 static inline double spline_weight(double value)
620 {
621     return (spline_cube(value + 2) -
622             4 * spline_cube(value + 1) +
623             6 * spline_cube(value) -
624             4 * spline_cube(value - 1)) / 6;
625 }
626 
627 // This is the bicubic resampling algorithm
ResampleBicubic(int width,int height) const628 wxImage wxImage::ResampleBicubic(int width, int height) const
629 {
630     // This function implements a Bicubic B-Spline algorithm for resampling.
631     // This method is certainly a little slower than wxImage's default pixel
632     // replication method, however for most reasonably sized images not being
633     // upsampled too much on a fairly average CPU this difference is hardly
634     // noticeable and the results are far more pleasing to look at.
635     //
636     // This particular bicubic algorithm does pixel weighting according to a
637     // B-Spline that basically implements a Gaussian bell-like weighting
638     // kernel. Because of this method the results may appear a bit blurry when
639     // upsampling by large factors.  This is basically because a slight
640     // gaussian blur is being performed to get the smooth look of the upsampled
641     // image.
642 
643     // Edge pixels: 3-4 possible solutions
644     // - (Wrap/tile) Wrap the image, take the color value from the opposite
645     // side of the image.
646     // - (Mirror)    Duplicate edge pixels, so that pixel at coordinate (2, n),
647     // where n is nonpositive, will have the value of (2, 1).
648     // - (Ignore)    Simply ignore the edge pixels and apply the kernel only to
649     // pixels which do have all neighbours.
650     // - (Clamp)     Choose the nearest pixel along the border. This takes the
651     // border pixels and extends them out to infinity.
652     //
653     // NOTE: below the y_offset and x_offset variables are being set for edge
654     // pixels using the "Mirror" method mentioned above
655 
656     wxImage ret_image;
657 
658     ret_image.Create(width, height, false);
659 
660     unsigned char* src_data = M_IMGDATA->m_data;
661     unsigned char* src_alpha = M_IMGDATA->m_alpha;
662     unsigned char* dst_data = ret_image.GetData();
663     unsigned char* dst_alpha = NULL;
664 
665     if ( src_alpha )
666     {
667         ret_image.SetAlpha();
668         dst_alpha = ret_image.GetAlpha();
669     }
670 
671     for ( int dsty = 0; dsty < height; dsty++ )
672     {
673         // We need to calculate the source pixel to interpolate from - Y-axis
674         double srcpixy = double(dsty * M_IMGDATA->m_height) / height;
675         double dy = srcpixy - (int)srcpixy;
676 
677         for ( int dstx = 0; dstx < width; dstx++ )
678         {
679             // X-axis of pixel to interpolate from
680             double srcpixx = double(dstx * M_IMGDATA->m_width) / width;
681             double dx = srcpixx - (int)srcpixx;
682 
683             // Sums for each color channel
684             double sum_r = 0, sum_g = 0, sum_b = 0, sum_a = 0;
685 
686             // Here we actually determine the RGBA values for the destination pixel
687             for ( int k = -1; k <= 2; k++ )
688             {
689                 // Y offset
690                 int y_offset = srcpixy + k < 0.0
691                                 ? 0
692                                 : srcpixy + k >= M_IMGDATA->m_height
693                                        ? M_IMGDATA->m_height - 1
694                                        : (int)(srcpixy + k);
695 
696                 // Loop across the X axis
697                 for ( int i = -1; i <= 2; i++ )
698                 {
699                     // X offset
700                     int x_offset = srcpixx + i < 0.0
701                                     ? 0
702                                     : srcpixx + i >= M_IMGDATA->m_width
703                                             ? M_IMGDATA->m_width - 1
704                                             : (int)(srcpixx + i);
705 
706                     // Calculate the exact position where the source data
707                     // should be pulled from based on the x_offset and y_offset
708                     int src_pixel_index = y_offset*M_IMGDATA->m_width + x_offset;
709 
710                     // Calculate the weight for the specified pixel according
711                     // to the bicubic b-spline kernel we're using for
712                     // interpolation
713                     double
714                         pixel_weight = spline_weight(i - dx)*spline_weight(k - dy);
715 
716                     // Create a sum of all velues for each color channel
717                     // adjusted for the pixel's calculated weight
718                     sum_r += src_data[src_pixel_index * 3 + 0] * pixel_weight;
719                     sum_g += src_data[src_pixel_index * 3 + 1] * pixel_weight;
720                     sum_b += src_data[src_pixel_index * 3 + 2] * pixel_weight;
721                     if ( src_alpha )
722                         sum_a += src_alpha[src_pixel_index] * pixel_weight;
723                 }
724             }
725 
726             // Put the data into the destination image.  The summed values are
727             // of double data type and are rounded here for accuracy
728             dst_data[0] = (unsigned char)(sum_r + 0.5);
729             dst_data[1] = (unsigned char)(sum_g + 0.5);
730             dst_data[2] = (unsigned char)(sum_b + 0.5);
731             dst_data += 3;
732 
733             if ( src_alpha )
734                 *dst_alpha++ = (unsigned char)sum_a;
735         }
736     }
737 
738     return ret_image;
739 }
740 
741 // Blur in the horizontal direction
BlurHorizontal(int blurRadius)742 wxImage wxImage::BlurHorizontal(int blurRadius)
743 {
744     wxImage ret_image;
745     ret_image.Create(M_IMGDATA->m_width, M_IMGDATA->m_height, false);
746 
747     unsigned char* src_data = M_IMGDATA->m_data;
748     unsigned char* dst_data = ret_image.GetData();
749     unsigned char* src_alpha = M_IMGDATA->m_alpha;
750     unsigned char* dst_alpha = NULL;
751 
752     // Check for a mask or alpha
753     if ( M_IMGDATA->m_hasMask )
754     {
755         ret_image.SetMaskColour(M_IMGDATA->m_maskRed,
756                                 M_IMGDATA->m_maskGreen,
757                                 M_IMGDATA->m_maskBlue);
758     }
759     else
760     {
761         if ( src_alpha )
762         {
763             ret_image.SetAlpha();
764             dst_alpha = ret_image.GetAlpha();
765         }
766     }
767 
768     // number of pixels we average over
769     const int blurArea = blurRadius*2 + 1;
770 
771     // Horizontal blurring algorithm - average all pixels in the specified blur
772     // radius in the X or horizontal direction
773     for ( int y = 0; y < M_IMGDATA->m_height; y++ )
774     {
775         // Variables used in the blurring algorithm
776         long sum_r = 0,
777              sum_g = 0,
778              sum_b = 0,
779              sum_a = 0;
780 
781         long pixel_idx;
782         const unsigned char *src;
783         unsigned char *dst;
784 
785         // Calculate the average of all pixels in the blur radius for the first
786         // pixel of the row
787         for ( int kernel_x = -blurRadius; kernel_x <= blurRadius; kernel_x++ )
788         {
789             // To deal with the pixels at the start of a row so it's not
790             // grabbing GOK values from memory at negative indices of the
791             // image's data or grabbing from the previous row
792             if ( kernel_x < 0 )
793                 pixel_idx = y * M_IMGDATA->m_width;
794             else
795                 pixel_idx = kernel_x + y * M_IMGDATA->m_width;
796 
797             src = src_data + pixel_idx*3;
798             sum_r += src[0];
799             sum_g += src[1];
800             sum_b += src[2];
801             if ( src_alpha )
802                 sum_a += src_alpha[pixel_idx];
803         }
804 
805         dst = dst_data + y * M_IMGDATA->m_width*3;
806         dst[0] = (unsigned char)(sum_r / blurArea);
807         dst[1] = (unsigned char)(sum_g / blurArea);
808         dst[2] = (unsigned char)(sum_b / blurArea);
809         if ( src_alpha )
810             dst_alpha[y * M_IMGDATA->m_width] = (unsigned char)(sum_a / blurArea);
811 
812         // Now average the values of the rest of the pixels by just moving the
813         // blur radius box along the row
814         for ( int x = 1; x < M_IMGDATA->m_width; x++ )
815         {
816             // Take care of edge pixels on the left edge by essentially
817             // duplicating the edge pixel
818             if ( x - blurRadius - 1 < 0 )
819                 pixel_idx = y * M_IMGDATA->m_width;
820             else
821                 pixel_idx = (x - blurRadius - 1) + y * M_IMGDATA->m_width;
822 
823             // Subtract the value of the pixel at the left side of the blur
824             // radius box
825             src = src_data + pixel_idx*3;
826             sum_r -= src[0];
827             sum_g -= src[1];
828             sum_b -= src[2];
829             if ( src_alpha )
830                 sum_a -= src_alpha[pixel_idx];
831 
832             // Take care of edge pixels on the right edge
833             if ( x + blurRadius > M_IMGDATA->m_width - 1 )
834                 pixel_idx = M_IMGDATA->m_width - 1 + y * M_IMGDATA->m_width;
835             else
836                 pixel_idx = x + blurRadius + y * M_IMGDATA->m_width;
837 
838             // Add the value of the pixel being added to the end of our box
839             src = src_data + pixel_idx*3;
840             sum_r += src[0];
841             sum_g += src[1];
842             sum_b += src[2];
843             if ( src_alpha )
844                 sum_a += src_alpha[pixel_idx];
845 
846             // Save off the averaged data
847             dst = dst_data + x*3 + y*M_IMGDATA->m_width*3;
848             dst[0] = (unsigned char)(sum_r / blurArea);
849             dst[1] = (unsigned char)(sum_g / blurArea);
850             dst[2] = (unsigned char)(sum_b / blurArea);
851             if ( src_alpha )
852                 dst_alpha[x + y * M_IMGDATA->m_width] = (unsigned char)(sum_a / blurArea);
853         }
854     }
855 
856     return ret_image;
857 }
858 
859 // Blur in the vertical direction
BlurVertical(int blurRadius)860 wxImage wxImage::BlurVertical(int blurRadius)
861 {
862     wxImage ret_image;
863     ret_image.Create(M_IMGDATA->m_width, M_IMGDATA->m_height, false);
864 
865     unsigned char* src_data = M_IMGDATA->m_data;
866     unsigned char* dst_data = ret_image.GetData();
867     unsigned char* src_alpha = M_IMGDATA->m_alpha;
868     unsigned char* dst_alpha = NULL;
869 
870     // Check for a mask or alpha
871     if ( M_IMGDATA->m_hasMask )
872     {
873         ret_image.SetMaskColour(M_IMGDATA->m_maskRed,
874                                 M_IMGDATA->m_maskGreen,
875                                 M_IMGDATA->m_maskBlue);
876     }
877     else
878     {
879         if ( src_alpha )
880         {
881             ret_image.SetAlpha();
882             dst_alpha = ret_image.GetAlpha();
883         }
884     }
885 
886     // number of pixels we average over
887     const int blurArea = blurRadius*2 + 1;
888 
889     // Vertical blurring algorithm - same as horizontal but switched the
890     // opposite direction
891     for ( int x = 0; x < M_IMGDATA->m_width; x++ )
892     {
893         // Variables used in the blurring algorithm
894         long sum_r = 0,
895              sum_g = 0,
896              sum_b = 0,
897              sum_a = 0;
898 
899         long pixel_idx;
900         const unsigned char *src;
901         unsigned char *dst;
902 
903         // Calculate the average of all pixels in our blur radius box for the
904         // first pixel of the column
905         for ( int kernel_y = -blurRadius; kernel_y <= blurRadius; kernel_y++ )
906         {
907             // To deal with the pixels at the start of a column so it's not
908             // grabbing GOK values from memory at negative indices of the
909             // image's data or grabbing from the previous column
910             if ( kernel_y < 0 )
911                 pixel_idx = x;
912             else
913                 pixel_idx = x + kernel_y * M_IMGDATA->m_width;
914 
915             src = src_data + pixel_idx*3;
916             sum_r += src[0];
917             sum_g += src[1];
918             sum_b += src[2];
919             if ( src_alpha )
920                 sum_a += src_alpha[pixel_idx];
921         }
922 
923         dst = dst_data + x*3;
924         dst[0] = (unsigned char)(sum_r / blurArea);
925         dst[1] = (unsigned char)(sum_g / blurArea);
926         dst[2] = (unsigned char)(sum_b / blurArea);
927         if ( src_alpha )
928             dst_alpha[x] = (unsigned char)(sum_a / blurArea);
929 
930         // Now average the values of the rest of the pixels by just moving the
931         // box along the column from top to bottom
932         for ( int y = 1; y < M_IMGDATA->m_height; y++ )
933         {
934             // Take care of pixels that would be beyond the top edge by
935             // duplicating the top edge pixel for the column
936             if ( y - blurRadius - 1 < 0 )
937                 pixel_idx = x;
938             else
939                 pixel_idx = x + (y - blurRadius - 1) * M_IMGDATA->m_width;
940 
941             // Subtract the value of the pixel at the top of our blur radius box
942             src = src_data + pixel_idx*3;
943             sum_r -= src[0];
944             sum_g -= src[1];
945             sum_b -= src[2];
946             if ( src_alpha )
947                 sum_a -= src_alpha[pixel_idx];
948 
949             // Take care of the pixels that would be beyond the bottom edge of
950             // the image similar to the top edge
951             if ( y + blurRadius > M_IMGDATA->m_height - 1 )
952                 pixel_idx = x + (M_IMGDATA->m_height - 1) * M_IMGDATA->m_width;
953             else
954                 pixel_idx = x + (blurRadius + y) * M_IMGDATA->m_width;
955 
956             // Add the value of the pixel being added to the end of our box
957             src = src_data + pixel_idx*3;
958             sum_r += src[0];
959             sum_g += src[1];
960             sum_b += src[2];
961             if ( src_alpha )
962                 sum_a += src_alpha[pixel_idx];
963 
964             // Save off the averaged data
965             dst = dst_data + (x + y * M_IMGDATA->m_width) * 3;
966             dst[0] = (unsigned char)(sum_r / blurArea);
967             dst[1] = (unsigned char)(sum_g / blurArea);
968             dst[2] = (unsigned char)(sum_b / blurArea);
969             if ( src_alpha )
970                 dst_alpha[x + y * M_IMGDATA->m_width] = (unsigned char)(sum_a / blurArea);
971         }
972     }
973 
974     return ret_image;
975 }
976 
977 // The new blur function
Blur(int blurRadius)978 wxImage wxImage::Blur(int blurRadius)
979 {
980     wxImage ret_image;
981     ret_image.Create(M_IMGDATA->m_width, M_IMGDATA->m_height, false);
982 
983     // Blur the image in each direction
984     ret_image = BlurHorizontal(blurRadius);
985     ret_image = ret_image.BlurVertical(blurRadius);
986 
987     return ret_image;
988 }
989 
Rotate90(bool clockwise) const990 wxImage wxImage::Rotate90( bool clockwise ) const
991 {
992     wxImage image;
993 
994     wxCHECK_MSG( Ok(), image, wxT("invalid image") );
995 
996     image.Create( M_IMGDATA->m_height, M_IMGDATA->m_width, false );
997 
998     unsigned char *data = image.GetData();
999 
1000     wxCHECK_MSG( data, image, wxT("unable to create image") );
1001 
1002     unsigned char *source_data = M_IMGDATA->m_data;
1003     unsigned char *target_data;
1004     unsigned char *alpha_data = 0 ;
1005     unsigned char *source_alpha = 0 ;
1006     unsigned char *target_alpha = 0 ;
1007 
1008     if (M_IMGDATA->m_hasMask)
1009     {
1010         image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
1011     }
1012     else
1013     {
1014         source_alpha = M_IMGDATA->m_alpha ;
1015         if ( source_alpha )
1016         {
1017             image.SetAlpha() ;
1018             alpha_data = image.GetAlpha() ;
1019         }
1020     }
1021 
1022     long height = M_IMGDATA->m_height;
1023     long width  = M_IMGDATA->m_width;
1024 
1025     for (long j = 0; j < height; j++)
1026     {
1027         for (long i = 0; i < width; i++)
1028         {
1029             if (clockwise)
1030             {
1031                 target_data = data + (((i+1)*height) - j - 1)*3;
1032                 if(source_alpha)
1033                     target_alpha = alpha_data + (((i+1)*height) - j - 1);
1034             }
1035             else
1036             {
1037                 target_data = data + ((height*(width-1)) + j - (i*height))*3;
1038                 if(source_alpha)
1039                     target_alpha = alpha_data + ((height*(width-1)) + j - (i*height));
1040             }
1041             memcpy( target_data, source_data, 3 );
1042             source_data += 3;
1043 
1044             if(source_alpha)
1045             {
1046                 memcpy( target_alpha, source_alpha, 1 );
1047                 source_alpha += 1;
1048             }
1049         }
1050     }
1051 
1052     return image;
1053 }
1054 
Mirror(bool horizontally) const1055 wxImage wxImage::Mirror( bool horizontally ) const
1056 {
1057     wxImage image;
1058 
1059     wxCHECK_MSG( Ok(), image, wxT("invalid image") );
1060 
1061     image.Create( M_IMGDATA->m_width, M_IMGDATA->m_height, false );
1062 
1063     unsigned char *data = image.GetData();
1064     unsigned char *alpha = NULL;
1065 
1066     wxCHECK_MSG( data, image, wxT("unable to create image") );
1067 
1068     if (M_IMGDATA->m_alpha != NULL) {
1069         image.SetAlpha();
1070         alpha = image.GetAlpha();
1071         wxCHECK_MSG( alpha, image, wxT("unable to create alpha channel") );
1072     }
1073 
1074     if (M_IMGDATA->m_hasMask)
1075         image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
1076 
1077     long height = M_IMGDATA->m_height;
1078     long width  = M_IMGDATA->m_width;
1079 
1080     unsigned char *source_data = M_IMGDATA->m_data;
1081     unsigned char *target_data;
1082 
1083     if (horizontally)
1084     {
1085         for (long j = 0; j < height; j++)
1086         {
1087             data += width*3;
1088             target_data = data-3;
1089             for (long i = 0; i < width; i++)
1090             {
1091                 memcpy( target_data, source_data, 3 );
1092                 source_data += 3;
1093                 target_data -= 3;
1094             }
1095         }
1096 
1097         if (alpha != NULL)
1098         {
1099             // src_alpha starts at the first pixel and increases by 1 after each step
1100             // (a step here is the copy of the alpha value of one pixel)
1101             const unsigned char *src_alpha = M_IMGDATA->m_alpha;
1102             // dest_alpha starts just beyond the first line, decreases before each step,
1103             // and after each line is finished, increases by 2 widths (skipping the line
1104             // just copied and the line that will be copied next)
1105             unsigned char *dest_alpha = alpha + width;
1106 
1107             for (long jj = 0; jj < height; ++jj)
1108             {
1109                 for (long i = 0; i < width; ++i) {
1110                     *(--dest_alpha) = *(src_alpha++); // copy one pixel
1111                 }
1112                 dest_alpha += 2 * width; // advance beyond the end of the next line
1113             }
1114         }
1115     }
1116     else
1117     {
1118         for (long i = 0; i < height; i++)
1119         {
1120             target_data = data + 3*width*(height-1-i);
1121             memcpy( target_data, source_data, (size_t)3*width );
1122             source_data += 3*width;
1123         }
1124 
1125         if (alpha != NULL)
1126         {
1127             // src_alpha starts at the first pixel and increases by 1 width after each step
1128             // (a step here is the copy of the alpha channel of an entire line)
1129             const unsigned char *src_alpha = M_IMGDATA->m_alpha;
1130             // dest_alpha starts just beyond the last line (beyond the whole image)
1131             // and decreases by 1 width before each step
1132             unsigned char *dest_alpha = alpha + width * height;
1133 
1134             for (long jj = 0; jj < height; ++jj)
1135             {
1136                 dest_alpha -= width;
1137                 memcpy( dest_alpha, src_alpha, (size_t)width );
1138                 src_alpha += width;
1139             }
1140         }
1141     }
1142 
1143     return image;
1144 }
1145 
GetSubImage(const wxRect & rect) const1146 wxImage wxImage::GetSubImage( const wxRect &rect ) const
1147 {
1148     wxImage image;
1149 
1150     wxCHECK_MSG( Ok(), image, wxT("invalid image") );
1151 
1152     wxCHECK_MSG( (rect.GetLeft()>=0) && (rect.GetTop()>=0) &&
1153                  (rect.GetRight()<=GetWidth()) && (rect.GetBottom()<=GetHeight()),
1154                  image, wxT("invalid subimage size") );
1155 
1156     const int subwidth = rect.GetWidth();
1157     const int subheight = rect.GetHeight();
1158 
1159     image.Create( subwidth, subheight, false );
1160 
1161     const unsigned char *src_data = GetData();
1162     const unsigned char *src_alpha = M_IMGDATA->m_alpha;
1163     unsigned char *subdata = image.GetData();
1164     unsigned char *subalpha = NULL;
1165 
1166     wxCHECK_MSG( subdata, image, wxT("unable to create image") );
1167 
1168     if (src_alpha != NULL) {
1169         image.SetAlpha();
1170         subalpha = image.GetAlpha();
1171         wxCHECK_MSG( subalpha, image, wxT("unable to create alpha channel"));
1172     }
1173 
1174     if (M_IMGDATA->m_hasMask)
1175         image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
1176 
1177     const int width = GetWidth();
1178     const int pixsoff = rect.GetLeft() + width * rect.GetTop();
1179 
1180     src_data += 3 * pixsoff;
1181     src_alpha += pixsoff; // won't be used if was NULL, so this is ok
1182 
1183     for (long j = 0; j < subheight; ++j)
1184     {
1185         memcpy( subdata, src_data, 3 * subwidth );
1186         subdata += 3 * subwidth;
1187         src_data += 3 * width;
1188         if (subalpha != NULL) {
1189             memcpy( subalpha, src_alpha, subwidth );
1190             subalpha += subwidth;
1191             src_alpha += width;
1192         }
1193     }
1194 
1195     return image;
1196 }
1197 
Size(const wxSize & size,const wxPoint & pos,int r_,int g_,int b_) const1198 wxImage wxImage::Size( const wxSize& size, const wxPoint& pos,
1199                        int r_, int g_, int b_ ) const
1200 {
1201     wxImage image;
1202 
1203     wxCHECK_MSG( Ok(), image, wxT("invalid image") );
1204     wxCHECK_MSG( (size.GetWidth() > 0) && (size.GetHeight() > 0), image, wxT("invalid size") );
1205 
1206     int width = GetWidth(), height = GetHeight();
1207     image.Create(size.GetWidth(), size.GetHeight(), false);
1208 
1209     unsigned char r = (unsigned char)r_;
1210     unsigned char g = (unsigned char)g_;
1211     unsigned char b = (unsigned char)b_;
1212     if ((r_ == -1) && (g_ == -1) && (b_ == -1))
1213     {
1214         GetOrFindMaskColour( &r, &g, &b );
1215         image.SetMaskColour(r, g, b);
1216     }
1217 
1218     image.SetRGB(wxRect(), r, g, b);
1219 
1220     wxRect subRect(pos.x, pos.y, width, height);
1221     wxRect finalRect(0, 0, size.GetWidth(), size.GetHeight());
1222     if (pos.x < 0)
1223         finalRect.width -= pos.x;
1224     if (pos.y < 0)
1225         finalRect.height -= pos.y;
1226 
1227     subRect.Intersect(finalRect);
1228 
1229     if (!subRect.IsEmpty())
1230     {
1231         if ((subRect.GetWidth() == width) && (subRect.GetHeight() == height))
1232             image.Paste(*this, pos.x, pos.y);
1233         else
1234             image.Paste(GetSubImage(subRect), pos.x, pos.y);
1235     }
1236 
1237     return image;
1238 }
1239 
Paste(const wxImage & image,int x,int y)1240 void wxImage::Paste( const wxImage &image, int x, int y )
1241 {
1242     wxCHECK_RET( Ok(), wxT("invalid image") );
1243     wxCHECK_RET( image.Ok(), wxT("invalid image") );
1244 
1245     AllocExclusive();
1246 
1247     int xx = 0;
1248     int yy = 0;
1249     int width = image.GetWidth();
1250     int height = image.GetHeight();
1251 
1252     if (x < 0)
1253     {
1254         xx = -x;
1255         width += x;
1256     }
1257     if (y < 0)
1258     {
1259         yy = -y;
1260         height += y;
1261     }
1262 
1263     if ((x+xx)+width > M_IMGDATA->m_width)
1264         width = M_IMGDATA->m_width - (x+xx);
1265     if ((y+yy)+height > M_IMGDATA->m_height)
1266         height = M_IMGDATA->m_height - (y+yy);
1267 
1268     if (width < 1) return;
1269     if (height < 1) return;
1270 
1271     if ((!HasMask() && !image.HasMask()) ||
1272         (HasMask() && !image.HasMask()) ||
1273        ((HasMask() && image.HasMask() &&
1274          (GetMaskRed()==image.GetMaskRed()) &&
1275          (GetMaskGreen()==image.GetMaskGreen()) &&
1276          (GetMaskBlue()==image.GetMaskBlue()))))
1277     {
1278         unsigned char* source_data = image.GetData() + xx*3 + yy*3*image.GetWidth();
1279         int source_step = image.GetWidth()*3;
1280 
1281         unsigned char* target_data = GetData() + (x+xx)*3 + (y+yy)*3*M_IMGDATA->m_width;
1282         int target_step = M_IMGDATA->m_width*3;
1283         for (int j = 0; j < height; j++)
1284         {
1285             memcpy( target_data, source_data, width*3 );
1286             source_data += source_step;
1287             target_data += target_step;
1288         }
1289     }
1290 
1291     // Copy over the alpha channel from the original image
1292     if ( image.HasAlpha() )
1293     {
1294         if ( !HasAlpha() )
1295             InitAlpha();
1296 
1297         unsigned char* source_data = image.GetAlpha() + xx + yy*image.GetWidth();
1298         int source_step = image.GetWidth();
1299 
1300         unsigned char* target_data = GetAlpha() + (x+xx) + (y+yy)*M_IMGDATA->m_width;
1301         int target_step = M_IMGDATA->m_width;
1302 
1303         for (int j = 0; j < height; j++,
1304                                     source_data += source_step,
1305                                     target_data += target_step)
1306         {
1307             memcpy( target_data, source_data, width );
1308         }
1309     }
1310 
1311     if (!HasMask() && image.HasMask())
1312     {
1313         unsigned char r = image.GetMaskRed();
1314         unsigned char g = image.GetMaskGreen();
1315         unsigned char b = image.GetMaskBlue();
1316 
1317         unsigned char* source_data = image.GetData() + xx*3 + yy*3*image.GetWidth();
1318         int source_step = image.GetWidth()*3;
1319 
1320         unsigned char* target_data = GetData() + (x+xx)*3 + (y+yy)*3*M_IMGDATA->m_width;
1321         int target_step = M_IMGDATA->m_width*3;
1322 
1323         for (int j = 0; j < height; j++)
1324         {
1325             for (int i = 0; i < width*3; i+=3)
1326             {
1327                 if ((source_data[i]   != r) ||
1328                     (source_data[i+1] != g) ||
1329                     (source_data[i+2] != b))
1330                 {
1331                     memcpy( target_data+i, source_data+i, 3 );
1332                 }
1333             }
1334             source_data += source_step;
1335             target_data += target_step;
1336         }
1337     }
1338 }
1339 
Replace(unsigned char r1,unsigned char g1,unsigned char b1,unsigned char r2,unsigned char g2,unsigned char b2)1340 void wxImage::Replace( unsigned char r1, unsigned char g1, unsigned char b1,
1341                        unsigned char r2, unsigned char g2, unsigned char b2 )
1342 {
1343     wxCHECK_RET( Ok(), wxT("invalid image") );
1344 
1345     AllocExclusive();
1346 
1347     unsigned char *data = GetData();
1348 
1349     const int w = GetWidth();
1350     const int h = GetHeight();
1351 
1352     for (int j = 0; j < h; j++)
1353         for (int i = 0; i < w; i++)
1354         {
1355             if ((data[0] == r1) && (data[1] == g1) && (data[2] == b1))
1356             {
1357                 data[0] = r2;
1358                 data[1] = g2;
1359                 data[2] = b2;
1360             }
1361             data += 3;
1362         }
1363 }
1364 
ConvertToGreyscale(double lr,double lg,double lb) const1365 wxImage wxImage::ConvertToGreyscale( double lr, double lg, double lb ) const
1366 {
1367     wxImage image;
1368 
1369     wxCHECK_MSG( Ok(), image, wxT("invalid image") );
1370 
1371     image.Create(M_IMGDATA->m_width, M_IMGDATA->m_height, false);
1372 
1373     unsigned char *dest = image.GetData();
1374 
1375     wxCHECK_MSG( dest, image, wxT("unable to create image") );
1376 
1377     unsigned char *src = M_IMGDATA->m_data;
1378     bool hasMask = M_IMGDATA->m_hasMask;
1379     unsigned char maskRed = M_IMGDATA->m_maskRed;
1380     unsigned char maskGreen = M_IMGDATA->m_maskGreen;
1381     unsigned char maskBlue = M_IMGDATA->m_maskBlue;
1382 
1383     if ( hasMask )
1384         image.SetMaskColour(maskRed, maskGreen, maskBlue);
1385 
1386     const long size = M_IMGDATA->m_width * M_IMGDATA->m_height;
1387     for ( long i = 0; i < size; i++, src += 3, dest += 3 )
1388     {
1389         // don't modify the mask
1390         if ( hasMask && src[0] == maskRed && src[1] == maskGreen && src[2] == maskBlue )
1391         {
1392             memcpy(dest, src, 3);
1393         }
1394         else
1395         {
1396             // calculate the luma
1397             double luma = (src[0] * lr + src[1] * lg + src[2] * lb) + 0.5;
1398             dest[0] = dest[1] = dest[2] = wx_static_cast(unsigned char, luma);
1399         }
1400     }
1401 
1402     // copy the alpha channel, if any
1403     if (HasAlpha())
1404     {
1405         const size_t alphaSize = GetWidth() * GetHeight();
1406         unsigned char *alpha = (unsigned char*)malloc(alphaSize);
1407         memcpy(alpha, GetAlpha(), alphaSize);
1408         image.InitAlpha();
1409         image.SetAlpha(alpha);
1410     }
1411 
1412     return image;
1413 }
1414 
ConvertToMono(unsigned char r,unsigned char g,unsigned char b) const1415 wxImage wxImage::ConvertToMono( unsigned char r, unsigned char g, unsigned char b ) const
1416 {
1417     wxImage image;
1418 
1419     wxCHECK_MSG( Ok(), image, wxT("invalid image") );
1420 
1421     image.Create( M_IMGDATA->m_width, M_IMGDATA->m_height, false );
1422 
1423     unsigned char *data = image.GetData();
1424 
1425     wxCHECK_MSG( data, image, wxT("unable to create image") );
1426 
1427     if (M_IMGDATA->m_hasMask)
1428     {
1429         if (M_IMGDATA->m_maskRed == r && M_IMGDATA->m_maskGreen == g &&
1430                                          M_IMGDATA->m_maskBlue == b)
1431             image.SetMaskColour( 255, 255, 255 );
1432         else
1433             image.SetMaskColour( 0, 0, 0 );
1434     }
1435 
1436     long size = M_IMGDATA->m_height * M_IMGDATA->m_width;
1437 
1438     unsigned char *srcd = M_IMGDATA->m_data;
1439     unsigned char *tard = image.GetData();
1440 
1441     for ( long i = 0; i < size; i++, srcd += 3, tard += 3 )
1442     {
1443         if (srcd[0] == r && srcd[1] == g && srcd[2] == b)
1444             tard[0] = tard[1] = tard[2] = 255;
1445         else
1446             tard[0] = tard[1] = tard[2] = 0;
1447     }
1448 
1449     return image;
1450 }
1451 
GetWidth() const1452 int wxImage::GetWidth() const
1453 {
1454     wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
1455 
1456     return M_IMGDATA->m_width;
1457 }
1458 
GetHeight() const1459 int wxImage::GetHeight() const
1460 {
1461     wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
1462 
1463     return M_IMGDATA->m_height;
1464 }
1465 
XYToIndex(int x,int y) const1466 long wxImage::XYToIndex(int x, int y) const
1467 {
1468     if ( Ok() &&
1469             x >= 0 && y >= 0 &&
1470                 x < M_IMGDATA->m_width && y < M_IMGDATA->m_height )
1471     {
1472         return y*M_IMGDATA->m_width + x;
1473     }
1474 
1475     return -1;
1476 }
1477 
SetRGB(int x,int y,unsigned char r,unsigned char g,unsigned char b)1478 void wxImage::SetRGB( int x, int y, unsigned char r, unsigned char g, unsigned char b )
1479 {
1480     long pos = XYToIndex(x, y);
1481     wxCHECK_RET( pos != -1, wxT("invalid image coordinates") );
1482 
1483     AllocExclusive();
1484 
1485     pos *= 3;
1486 
1487     M_IMGDATA->m_data[ pos   ] = r;
1488     M_IMGDATA->m_data[ pos+1 ] = g;
1489     M_IMGDATA->m_data[ pos+2 ] = b;
1490 }
1491 
SetRGB(const wxRect & rect_,unsigned char r,unsigned char g,unsigned char b)1492 void wxImage::SetRGB( const wxRect& rect_, unsigned char r, unsigned char g, unsigned char b )
1493 {
1494     wxCHECK_RET( Ok(), wxT("invalid image") );
1495 
1496     AllocExclusive();
1497 
1498     wxRect rect(rect_);
1499     wxRect imageRect(0, 0, GetWidth(), GetHeight());
1500     if ( rect == wxRect() )
1501     {
1502         rect = imageRect;
1503     }
1504     else
1505     {
1506         wxCHECK_RET( imageRect.Contains(rect.GetTopLeft()) &&
1507                      imageRect.Contains(rect.GetBottomRight()),
1508                      wxT("invalid bounding rectangle") );
1509     }
1510 
1511     int x1 = rect.GetLeft(),
1512         y1 = rect.GetTop(),
1513         x2 = rect.GetRight() + 1,
1514         y2 = rect.GetBottom() + 1;
1515 
1516     unsigned char *data wxDUMMY_INITIALIZE(NULL);
1517     int x, y, width = GetWidth();
1518     for (y = y1; y < y2; y++)
1519     {
1520         data = M_IMGDATA->m_data + (y*width + x1)*3;
1521         for (x = x1; x < x2; x++)
1522         {
1523             *data++ = r;
1524             *data++ = g;
1525             *data++ = b;
1526         }
1527     }
1528 }
1529 
GetRed(int x,int y) const1530 unsigned char wxImage::GetRed( int x, int y ) const
1531 {
1532     long pos = XYToIndex(x, y);
1533     wxCHECK_MSG( pos != -1, 0, wxT("invalid image coordinates") );
1534 
1535     pos *= 3;
1536 
1537     return M_IMGDATA->m_data[pos];
1538 }
1539 
GetGreen(int x,int y) const1540 unsigned char wxImage::GetGreen( int x, int y ) const
1541 {
1542     long pos = XYToIndex(x, y);
1543     wxCHECK_MSG( pos != -1, 0, wxT("invalid image coordinates") );
1544 
1545     pos *= 3;
1546 
1547     return M_IMGDATA->m_data[pos+1];
1548 }
1549 
GetBlue(int x,int y) const1550 unsigned char wxImage::GetBlue( int x, int y ) const
1551 {
1552     long pos = XYToIndex(x, y);
1553     wxCHECK_MSG( pos != -1, 0, wxT("invalid image coordinates") );
1554 
1555     pos *= 3;
1556 
1557     return M_IMGDATA->m_data[pos+2];
1558 }
1559 
IsOk() const1560 bool wxImage::IsOk() const
1561 {
1562     // image of 0 width or height can't be considered ok - at least because it
1563     // causes crashes in ConvertToBitmap() if we don't catch it in time
1564     wxImageRefData *data = M_IMGDATA;
1565     return data && data->m_ok && data->m_width && data->m_height;
1566 }
1567 
GetData() const1568 unsigned char *wxImage::GetData() const
1569 {
1570     wxCHECK_MSG( Ok(), (unsigned char *)NULL, wxT("invalid image") );
1571 
1572     return M_IMGDATA->m_data;
1573 }
1574 
SetData(unsigned char * data,bool static_data)1575 void wxImage::SetData( unsigned char *data, bool static_data  )
1576 {
1577     wxCHECK_RET( Ok(), wxT("invalid image") );
1578 
1579     wxImageRefData *newRefData = new wxImageRefData();
1580 
1581     newRefData->m_width = M_IMGDATA->m_width;
1582     newRefData->m_height = M_IMGDATA->m_height;
1583     newRefData->m_data = data;
1584     newRefData->m_ok = true;
1585     newRefData->m_maskRed = M_IMGDATA->m_maskRed;
1586     newRefData->m_maskGreen = M_IMGDATA->m_maskGreen;
1587     newRefData->m_maskBlue = M_IMGDATA->m_maskBlue;
1588     newRefData->m_hasMask = M_IMGDATA->m_hasMask;
1589     newRefData->m_static = static_data;
1590 
1591     UnRef();
1592 
1593     m_refData = newRefData;
1594 }
1595 
SetData(unsigned char * data,int new_width,int new_height,bool static_data)1596 void wxImage::SetData( unsigned char *data, int new_width, int new_height, bool static_data )
1597 {
1598     wxImageRefData *newRefData = new wxImageRefData();
1599 
1600     if (m_refData)
1601     {
1602         newRefData->m_width = new_width;
1603         newRefData->m_height = new_height;
1604         newRefData->m_data = data;
1605         newRefData->m_ok = true;
1606         newRefData->m_maskRed = M_IMGDATA->m_maskRed;
1607         newRefData->m_maskGreen = M_IMGDATA->m_maskGreen;
1608         newRefData->m_maskBlue = M_IMGDATA->m_maskBlue;
1609         newRefData->m_hasMask = M_IMGDATA->m_hasMask;
1610     }
1611     else
1612     {
1613         newRefData->m_width = new_width;
1614         newRefData->m_height = new_height;
1615         newRefData->m_data = data;
1616         newRefData->m_ok = true;
1617     }
1618     newRefData->m_static = static_data;
1619 
1620     UnRef();
1621 
1622     m_refData = newRefData;
1623 }
1624 
1625 // ----------------------------------------------------------------------------
1626 // alpha channel support
1627 // ----------------------------------------------------------------------------
1628 
SetAlpha(int x,int y,unsigned char alpha)1629 void wxImage::SetAlpha(int x, int y, unsigned char alpha)
1630 {
1631     wxCHECK_RET( HasAlpha(), wxT("no alpha channel") );
1632 
1633     long pos = XYToIndex(x, y);
1634     wxCHECK_RET( pos != -1, wxT("invalid image coordinates") );
1635 
1636     AllocExclusive();
1637 
1638     M_IMGDATA->m_alpha[pos] = alpha;
1639 }
1640 
GetAlpha(int x,int y) const1641 unsigned char wxImage::GetAlpha(int x, int y) const
1642 {
1643     wxCHECK_MSG( HasAlpha(), 0, wxT("no alpha channel") );
1644 
1645     long pos = XYToIndex(x, y);
1646     wxCHECK_MSG( pos != -1, 0, wxT("invalid image coordinates") );
1647 
1648     return M_IMGDATA->m_alpha[pos];
1649 }
1650 
1651 bool
ConvertColourToAlpha(unsigned char r,unsigned char g,unsigned char b)1652 wxImage::ConvertColourToAlpha(unsigned char r, unsigned char g, unsigned char b)
1653 {
1654     SetAlpha(NULL);
1655 
1656     const int w = M_IMGDATA->m_width;
1657     const int h = M_IMGDATA->m_height;
1658 
1659     unsigned char *alpha = GetAlpha();
1660     unsigned char *data = GetData();
1661 
1662     for ( int y = 0; y < h; y++ )
1663     {
1664         for ( int x = 0; x < w; x++ )
1665         {
1666             *alpha++ = *data;
1667             *data++ = r;
1668             *data++ = g;
1669             *data++ = b;
1670         }
1671     }
1672 
1673     return true;
1674 }
1675 
SetAlpha(unsigned char * alpha,bool static_data)1676 void wxImage::SetAlpha( unsigned char *alpha, bool static_data )
1677 {
1678     wxCHECK_RET( Ok(), wxT("invalid image") );
1679 
1680     AllocExclusive();
1681 
1682     if ( !alpha )
1683     {
1684         alpha = (unsigned char *)malloc(M_IMGDATA->m_width*M_IMGDATA->m_height);
1685     }
1686 
1687     if( !M_IMGDATA->m_staticAlpha )
1688         free(M_IMGDATA->m_alpha);
1689 
1690     M_IMGDATA->m_alpha = alpha;
1691     M_IMGDATA->m_staticAlpha = static_data;
1692 }
1693 
GetAlpha() const1694 unsigned char *wxImage::GetAlpha() const
1695 {
1696     wxCHECK_MSG( Ok(), (unsigned char *)NULL, wxT("invalid image") );
1697 
1698     return M_IMGDATA->m_alpha;
1699 }
1700 
InitAlpha()1701 void wxImage::InitAlpha()
1702 {
1703     wxCHECK_RET( !HasAlpha(), wxT("image already has an alpha channel") );
1704 
1705     // initialize memory for alpha channel
1706     SetAlpha();
1707 
1708     unsigned char *alpha = M_IMGDATA->m_alpha;
1709     const size_t lenAlpha = M_IMGDATA->m_width * M_IMGDATA->m_height;
1710 
1711     if ( HasMask() )
1712     {
1713         // use the mask to initialize the alpha channel.
1714         const unsigned char * const alphaEnd = alpha + lenAlpha;
1715 
1716         const unsigned char mr = M_IMGDATA->m_maskRed;
1717         const unsigned char mg = M_IMGDATA->m_maskGreen;
1718         const unsigned char mb = M_IMGDATA->m_maskBlue;
1719         for ( unsigned char *src = M_IMGDATA->m_data;
1720               alpha < alphaEnd;
1721               src += 3, alpha++ )
1722         {
1723             *alpha = (src[0] == mr && src[1] == mg && src[2] == mb)
1724                             ? wxIMAGE_ALPHA_TRANSPARENT
1725                             : wxIMAGE_ALPHA_OPAQUE;
1726         }
1727 
1728         M_IMGDATA->m_hasMask = false;
1729     }
1730     else // no mask
1731     {
1732         // make the image fully opaque
1733         memset(alpha, wxIMAGE_ALPHA_OPAQUE, lenAlpha);
1734     }
1735 }
1736 
1737 // ----------------------------------------------------------------------------
1738 // mask support
1739 // ----------------------------------------------------------------------------
1740 
SetMaskColour(unsigned char r,unsigned char g,unsigned char b)1741 void wxImage::SetMaskColour( unsigned char r, unsigned char g, unsigned char b )
1742 {
1743     wxCHECK_RET( Ok(), wxT("invalid image") );
1744 
1745     AllocExclusive();
1746 
1747     M_IMGDATA->m_maskRed = r;
1748     M_IMGDATA->m_maskGreen = g;
1749     M_IMGDATA->m_maskBlue = b;
1750     M_IMGDATA->m_hasMask = true;
1751 }
1752 
GetOrFindMaskColour(unsigned char * r,unsigned char * g,unsigned char * b) const1753 bool wxImage::GetOrFindMaskColour( unsigned char *r, unsigned char *g, unsigned char *b ) const
1754 {
1755     wxCHECK_MSG( Ok(), false, wxT("invalid image") );
1756 
1757     if (M_IMGDATA->m_hasMask)
1758     {
1759         if (r) *r = M_IMGDATA->m_maskRed;
1760         if (g) *g = M_IMGDATA->m_maskGreen;
1761         if (b) *b = M_IMGDATA->m_maskBlue;
1762         return true;
1763     }
1764     else
1765     {
1766         FindFirstUnusedColour(r, g, b);
1767         return false;
1768     }
1769 }
1770 
GetMaskRed() const1771 unsigned char wxImage::GetMaskRed() const
1772 {
1773     wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
1774 
1775     return M_IMGDATA->m_maskRed;
1776 }
1777 
GetMaskGreen() const1778 unsigned char wxImage::GetMaskGreen() const
1779 {
1780     wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
1781 
1782     return M_IMGDATA->m_maskGreen;
1783 }
1784 
GetMaskBlue() const1785 unsigned char wxImage::GetMaskBlue() const
1786 {
1787     wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
1788 
1789     return M_IMGDATA->m_maskBlue;
1790 }
1791 
SetMask(bool mask)1792 void wxImage::SetMask( bool mask )
1793 {
1794     wxCHECK_RET( Ok(), wxT("invalid image") );
1795 
1796     AllocExclusive();
1797 
1798     M_IMGDATA->m_hasMask = mask;
1799 }
1800 
HasMask() const1801 bool wxImage::HasMask() const
1802 {
1803     wxCHECK_MSG( Ok(), false, wxT("invalid image") );
1804 
1805     return M_IMGDATA->m_hasMask;
1806 }
1807 
IsTransparent(int x,int y,unsigned char threshold) const1808 bool wxImage::IsTransparent(int x, int y, unsigned char threshold) const
1809 {
1810     long pos = XYToIndex(x, y);
1811     wxCHECK_MSG( pos != -1, false, wxT("invalid image coordinates") );
1812 
1813     // check mask
1814     if ( M_IMGDATA->m_hasMask )
1815     {
1816         const unsigned char *p = M_IMGDATA->m_data + 3*pos;
1817         if ( p[0] == M_IMGDATA->m_maskRed &&
1818                 p[1] == M_IMGDATA->m_maskGreen &&
1819                     p[2] == M_IMGDATA->m_maskBlue )
1820         {
1821             return true;
1822         }
1823     }
1824 
1825     // then check alpha
1826     if ( M_IMGDATA->m_alpha )
1827     {
1828         if ( M_IMGDATA->m_alpha[pos] < threshold )
1829         {
1830             // transparent enough
1831             return true;
1832         }
1833     }
1834 
1835     // not transparent
1836     return false;
1837 }
1838 
SetMaskFromImage(const wxImage & mask,unsigned char mr,unsigned char mg,unsigned char mb)1839 bool wxImage::SetMaskFromImage(const wxImage& mask,
1840                                unsigned char mr, unsigned char mg, unsigned char mb)
1841 {
1842     // check that the images are the same size
1843     if ( (M_IMGDATA->m_height != mask.GetHeight() ) || (M_IMGDATA->m_width != mask.GetWidth () ) )
1844     {
1845         wxLogError( _("Image and mask have different sizes.") );
1846         return false;
1847     }
1848 
1849     // find unused colour
1850     unsigned char r,g,b ;
1851     if (!FindFirstUnusedColour(&r, &g, &b))
1852     {
1853         wxLogError( _("No unused colour in image being masked.") );
1854         return false ;
1855     }
1856 
1857     AllocExclusive();
1858 
1859     unsigned char *imgdata = GetData();
1860     unsigned char *maskdata = mask.GetData();
1861 
1862     const int w = GetWidth();
1863     const int h = GetHeight();
1864 
1865     for (int j = 0; j < h; j++)
1866     {
1867         for (int i = 0; i < w; i++)
1868         {
1869             if ((maskdata[0] == mr) && (maskdata[1]  == mg) && (maskdata[2] == mb))
1870             {
1871                 imgdata[0] = r;
1872                 imgdata[1] = g;
1873                 imgdata[2] = b;
1874             }
1875             imgdata  += 3;
1876             maskdata += 3;
1877         }
1878     }
1879 
1880     SetMaskColour(r, g, b);
1881     SetMask(true);
1882 
1883     return true;
1884 }
1885 
ConvertAlphaToMask(unsigned char threshold)1886 bool wxImage::ConvertAlphaToMask(unsigned char threshold)
1887 {
1888     if (!HasAlpha())
1889         return true;
1890 
1891     unsigned char mr, mg, mb;
1892     if (!FindFirstUnusedColour(&mr, &mg, &mb))
1893     {
1894         wxLogError( _("No unused colour in image being masked.") );
1895         return false;
1896     }
1897 
1898     AllocExclusive();
1899 
1900     SetMask(true);
1901     SetMaskColour(mr, mg, mb);
1902 
1903     unsigned char *imgdata = GetData();
1904     unsigned char *alphadata = GetAlpha();
1905 
1906     int w = GetWidth();
1907     int h = GetHeight();
1908 
1909     for (int y = 0; y < h; y++)
1910     {
1911         for (int x = 0; x < w; x++, imgdata += 3, alphadata++)
1912         {
1913             if (*alphadata < threshold)
1914             {
1915                 imgdata[0] = mr;
1916                 imgdata[1] = mg;
1917                 imgdata[2] = mb;
1918             }
1919         }
1920     }
1921 
1922     if( !M_IMGDATA->m_staticAlpha )
1923         free(M_IMGDATA->m_alpha);
1924 
1925     M_IMGDATA->m_alpha = NULL;
1926     M_IMGDATA->m_staticAlpha = false;
1927 
1928     return true;
1929 }
1930 
1931 // ----------------------------------------------------------------------------
1932 // Palette functions
1933 // ----------------------------------------------------------------------------
1934 
1935 #if wxUSE_PALETTE
1936 
HasPalette() const1937 bool wxImage::HasPalette() const
1938 {
1939     if (!Ok())
1940         return false;
1941 
1942     return M_IMGDATA->m_palette.Ok();
1943 }
1944 
GetPalette() const1945 const wxPalette& wxImage::GetPalette() const
1946 {
1947     wxCHECK_MSG( Ok(), wxNullPalette, wxT("invalid image") );
1948 
1949     return M_IMGDATA->m_palette;
1950 }
1951 
SetPalette(const wxPalette & palette)1952 void wxImage::SetPalette(const wxPalette& palette)
1953 {
1954     wxCHECK_RET( Ok(), wxT("invalid image") );
1955 
1956     AllocExclusive();
1957 
1958     M_IMGDATA->m_palette = palette;
1959 }
1960 
1961 #endif // wxUSE_PALETTE
1962 
1963 // ----------------------------------------------------------------------------
1964 // Option functions (arbitrary name/value mapping)
1965 // ----------------------------------------------------------------------------
1966 
SetOption(const wxString & name,const wxString & value)1967 void wxImage::SetOption(const wxString& name, const wxString& value)
1968 {
1969     wxCHECK_RET( Ok(), wxT("invalid image") );
1970 
1971     AllocExclusive();
1972 
1973     int idx = M_IMGDATA->m_optionNames.Index(name, false);
1974     if (idx == wxNOT_FOUND)
1975     {
1976         M_IMGDATA->m_optionNames.Add(name);
1977         M_IMGDATA->m_optionValues.Add(value);
1978     }
1979     else
1980     {
1981         M_IMGDATA->m_optionNames[idx] = name;
1982         M_IMGDATA->m_optionValues[idx] = value;
1983     }
1984 }
1985 
SetOption(const wxString & name,int value)1986 void wxImage::SetOption(const wxString& name, int value)
1987 {
1988     wxString valStr;
1989     valStr.Printf(wxT("%d"), value);
1990     SetOption(name, valStr);
1991 }
1992 
GetOption(const wxString & name) const1993 wxString wxImage::GetOption(const wxString& name) const
1994 {
1995     wxCHECK_MSG( Ok(), wxEmptyString, wxT("invalid image") );
1996 
1997     int idx = M_IMGDATA->m_optionNames.Index(name, false);
1998     if (idx == wxNOT_FOUND)
1999         return wxEmptyString;
2000     else
2001         return M_IMGDATA->m_optionValues[idx];
2002 }
2003 
GetOptionInt(const wxString & name) const2004 int wxImage::GetOptionInt(const wxString& name) const
2005 {
2006     return wxAtoi(GetOption(name));
2007 }
2008 
HasOption(const wxString & name) const2009 bool wxImage::HasOption(const wxString& name) const
2010 {
2011     wxCHECK_MSG( Ok(), false, wxT("invalid image") );
2012 
2013     return (M_IMGDATA->m_optionNames.Index(name, false) != wxNOT_FOUND);
2014 }
2015 
2016 // ----------------------------------------------------------------------------
2017 // image I/O
2018 // ----------------------------------------------------------------------------
2019 
LoadFile(const wxString & WXUNUSED_UNLESS_STREAMS (filename),long WXUNUSED_UNLESS_STREAMS (type),int WXUNUSED_UNLESS_STREAMS (index))2020 bool wxImage::LoadFile( const wxString& WXUNUSED_UNLESS_STREAMS(filename),
2021                         long WXUNUSED_UNLESS_STREAMS(type),
2022                         int WXUNUSED_UNLESS_STREAMS(index) )
2023 {
2024 #if HAS_FILE_STREAMS
2025     if (wxFileExists(filename))
2026     {
2027         wxImageFileInputStream stream(filename);
2028         wxBufferedInputStream bstream( stream );
2029         return LoadFile(bstream, type, index);
2030     }
2031     else
2032     {
2033         wxLogError( _("Can't load image from file '%s': file does not exist."), filename.c_str() );
2034 
2035         return false;
2036     }
2037 #else // !HAS_FILE_STREAMS
2038     return false;
2039 #endif // HAS_FILE_STREAMS
2040 }
2041 
LoadFile(const wxString & WXUNUSED_UNLESS_STREAMS (filename),const wxString & WXUNUSED_UNLESS_STREAMS (mimetype),int WXUNUSED_UNLESS_STREAMS (index))2042 bool wxImage::LoadFile( const wxString& WXUNUSED_UNLESS_STREAMS(filename),
2043                         const wxString& WXUNUSED_UNLESS_STREAMS(mimetype),
2044                         int WXUNUSED_UNLESS_STREAMS(index) )
2045 {
2046 #if HAS_FILE_STREAMS
2047     if (wxFileExists(filename))
2048     {
2049         wxImageFileInputStream stream(filename);
2050         wxBufferedInputStream bstream( stream );
2051         return LoadFile(bstream, mimetype, index);
2052     }
2053     else
2054     {
2055         wxLogError( _("Can't load image from file '%s': file does not exist."), filename.c_str() );
2056 
2057         return false;
2058     }
2059 #else // !HAS_FILE_STREAMS
2060     return false;
2061 #endif // HAS_FILE_STREAMS
2062 }
2063 
2064 
2065 
SaveFile(const wxString & filename) const2066 bool wxImage::SaveFile( const wxString& filename ) const
2067 {
2068     wxString ext = filename.AfterLast('.').Lower();
2069 
2070     wxImageHandler * pHandler = FindHandler(ext, -1);
2071     if (pHandler)
2072     {
2073         return SaveFile(filename, pHandler->GetType());
2074     }
2075 
2076     wxLogError(_("Can't save image to file '%s': unknown extension."), filename.c_str());
2077 
2078     return false;
2079 }
2080 
SaveFile(const wxString & WXUNUSED_UNLESS_STREAMS (filename),int WXUNUSED_UNLESS_STREAMS (type)) const2081 bool wxImage::SaveFile( const wxString& WXUNUSED_UNLESS_STREAMS(filename),
2082                         int WXUNUSED_UNLESS_STREAMS(type) ) const
2083 {
2084 #if HAS_FILE_STREAMS
2085     wxCHECK_MSG( Ok(), false, wxT("invalid image") );
2086 
2087     ((wxImage*)this)->SetOption(wxIMAGE_OPTION_FILENAME, filename);
2088 
2089     wxImageFileOutputStream stream(filename);
2090 
2091     if ( stream.IsOk() )
2092     {
2093         wxBufferedOutputStream bstream( stream );
2094         return SaveFile(bstream, type);
2095     }
2096 #endif // HAS_FILE_STREAMS
2097 
2098     return false;
2099 }
2100 
SaveFile(const wxString & WXUNUSED_UNLESS_STREAMS (filename),const wxString & WXUNUSED_UNLESS_STREAMS (mimetype)) const2101 bool wxImage::SaveFile( const wxString& WXUNUSED_UNLESS_STREAMS(filename),
2102                         const wxString& WXUNUSED_UNLESS_STREAMS(mimetype) ) const
2103 {
2104 #if HAS_FILE_STREAMS
2105     wxCHECK_MSG( Ok(), false, wxT("invalid image") );
2106 
2107     ((wxImage*)this)->SetOption(wxIMAGE_OPTION_FILENAME, filename);
2108 
2109     wxImageFileOutputStream stream(filename);
2110 
2111     if ( stream.IsOk() )
2112     {
2113         wxBufferedOutputStream bstream( stream );
2114         return SaveFile(bstream, mimetype);
2115     }
2116 #endif // HAS_FILE_STREAMS
2117 
2118     return false;
2119 }
2120 
CanRead(const wxString & WXUNUSED_UNLESS_STREAMS (name))2121 bool wxImage::CanRead( const wxString& WXUNUSED_UNLESS_STREAMS(name) )
2122 {
2123 #if HAS_FILE_STREAMS
2124     wxImageFileInputStream stream(name);
2125     return CanRead(stream);
2126 #else
2127     return false;
2128 #endif
2129 }
2130 
GetImageCount(const wxString & WXUNUSED_UNLESS_STREAMS (name),long WXUNUSED_UNLESS_STREAMS (type))2131 int wxImage::GetImageCount( const wxString& WXUNUSED_UNLESS_STREAMS(name),
2132                             long WXUNUSED_UNLESS_STREAMS(type) )
2133 {
2134 #if HAS_FILE_STREAMS
2135     wxImageFileInputStream stream(name);
2136     if (stream.Ok())
2137         return GetImageCount(stream, type);
2138 #endif
2139 
2140   return 0;
2141 }
2142 
2143 #if wxUSE_STREAMS
2144 
CanRead(wxInputStream & stream)2145 bool wxImage::CanRead( wxInputStream &stream )
2146 {
2147     const wxList& list = GetHandlers();
2148 
2149     for ( wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext() )
2150     {
2151         wxImageHandler *handler=(wxImageHandler*)node->GetData();
2152         if (handler->CanRead( stream ))
2153             return true;
2154     }
2155 
2156     return false;
2157 }
2158 
GetImageCount(wxInputStream & stream,long type)2159 int wxImage::GetImageCount( wxInputStream &stream, long type )
2160 {
2161     wxImageHandler *handler;
2162 
2163     if ( type == wxBITMAP_TYPE_ANY )
2164     {
2165         wxList &list=GetHandlers();
2166 
2167         for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
2168         {
2169              handler=(wxImageHandler*)node->GetData();
2170              if ( handler->CanRead(stream) )
2171                  return handler->GetImageCount(stream);
2172 
2173         }
2174 
2175         wxLogWarning(_("No handler found for image type."));
2176         return 0;
2177     }
2178 
2179     handler = FindHandler(type);
2180 
2181     if ( !handler )
2182     {
2183         wxLogWarning(_("No image handler for type %ld defined."), type);
2184         return false;
2185     }
2186 
2187     if ( handler->CanRead(stream) )
2188     {
2189         return handler->GetImageCount(stream);
2190     }
2191     else
2192     {
2193         wxLogError(_("Image file is not of type %ld."), type);
2194         return 0;
2195     }
2196 }
2197 
LoadFile(wxInputStream & stream,long type,int index)2198 bool wxImage::LoadFile( wxInputStream& stream, long type, int index )
2199 {
2200     UnRef();
2201 
2202     m_refData = new wxImageRefData;
2203 
2204     wxImageHandler *handler;
2205 
2206     if ( type == wxBITMAP_TYPE_ANY )
2207     {
2208         wxList &list=GetHandlers();
2209 
2210         for ( wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext() )
2211         {
2212              handler=(wxImageHandler*)node->GetData();
2213              if ( handler->CanRead(stream) )
2214                  return handler->LoadFile(this, stream, true/*verbose*/, index);
2215 
2216         }
2217 
2218         wxLogWarning( _("No handler found for image type.") );
2219         return false;
2220     }
2221 
2222     handler = FindHandler(type);
2223 
2224     if (handler == 0)
2225     {
2226         wxLogWarning( _("No image handler for type %ld defined."), type );
2227 
2228         return false;
2229     }
2230 
2231     if (stream.IsSeekable() && !handler->CanRead(stream))
2232     {
2233         wxLogError(_("Image file is not of type %ld."), type);
2234         return false;
2235     }
2236     else
2237         return handler->LoadFile(this, stream, true/*verbose*/, index);
2238 }
2239 
LoadFile(wxInputStream & stream,const wxString & mimetype,int index)2240 bool wxImage::LoadFile( wxInputStream& stream, const wxString& mimetype, int index )
2241 {
2242     UnRef();
2243 
2244     m_refData = new wxImageRefData;
2245 
2246     wxImageHandler *handler = FindHandlerMime(mimetype);
2247 
2248     if (handler == 0)
2249     {
2250         wxLogWarning( _("No image handler for type %s defined."), mimetype.GetData() );
2251 
2252         return false;
2253     }
2254 
2255     if (stream.IsSeekable() && !handler->CanRead(stream))
2256     {
2257         wxLogError(_("Image file is not of type %s."), (const wxChar*) mimetype);
2258         return false;
2259     }
2260     else
2261         return handler->LoadFile( this, stream, true/*verbose*/, index );
2262 }
2263 
SaveFile(wxOutputStream & stream,int type) const2264 bool wxImage::SaveFile( wxOutputStream& stream, int type ) const
2265 {
2266     wxCHECK_MSG( Ok(), false, wxT("invalid image") );
2267 
2268     wxImageHandler *handler = FindHandler(type);
2269     if ( !handler )
2270     {
2271         wxLogWarning( _("No image handler for type %d defined."), type );
2272 
2273         return false;
2274     }
2275 
2276     return handler->SaveFile( (wxImage*)this, stream );
2277 }
2278 
SaveFile(wxOutputStream & stream,const wxString & mimetype) const2279 bool wxImage::SaveFile( wxOutputStream& stream, const wxString& mimetype ) const
2280 {
2281     wxCHECK_MSG( Ok(), false, wxT("invalid image") );
2282 
2283     wxImageHandler *handler = FindHandlerMime(mimetype);
2284     if ( !handler )
2285     {
2286         wxLogWarning( _("No image handler for type %s defined."), mimetype.GetData() );
2287 
2288         return false;
2289     }
2290 
2291     return handler->SaveFile( (wxImage*)this, stream );
2292 }
2293 #endif // wxUSE_STREAMS
2294 
2295 // ----------------------------------------------------------------------------
2296 // image I/O handlers
2297 // ----------------------------------------------------------------------------
2298 
AddHandler(wxImageHandler * handler)2299 void wxImage::AddHandler( wxImageHandler *handler )
2300 {
2301     // Check for an existing handler of the type being added.
2302     if (FindHandler( handler->GetType() ) == 0)
2303     {
2304         sm_handlers.Append( handler );
2305     }
2306     else
2307     {
2308         // This is not documented behaviour, merely the simplest 'fix'
2309         // for preventing duplicate additions.  If someone ever has
2310         // a good reason to add and remove duplicate handlers (and they
2311         // may) we should probably refcount the duplicates.
2312         //   also an issue in InsertHandler below.
2313 
2314         wxLogDebug( _T("Adding duplicate image handler for '%s'"),
2315                     handler->GetName().c_str() );
2316         delete handler;
2317     }
2318 }
2319 
InsertHandler(wxImageHandler * handler)2320 void wxImage::InsertHandler( wxImageHandler *handler )
2321 {
2322     // Check for an existing handler of the type being added.
2323     if (FindHandler( handler->GetType() ) == 0)
2324     {
2325         sm_handlers.Insert( handler );
2326     }
2327     else
2328     {
2329         // see AddHandler for additional comments.
2330         wxLogDebug( _T("Inserting duplicate image handler for '%s'"),
2331                     handler->GetName().c_str() );
2332         delete handler;
2333     }
2334 }
2335 
RemoveHandler(const wxString & name)2336 bool wxImage::RemoveHandler( const wxString& name )
2337 {
2338     wxImageHandler *handler = FindHandler(name);
2339     if (handler)
2340     {
2341         sm_handlers.DeleteObject(handler);
2342         delete handler;
2343         return true;
2344     }
2345     else
2346         return false;
2347 }
2348 
FindHandler(const wxString & name)2349 wxImageHandler *wxImage::FindHandler( const wxString& name )
2350 {
2351     wxList::compatibility_iterator node = sm_handlers.GetFirst();
2352     while (node)
2353     {
2354         wxImageHandler *handler = (wxImageHandler*)node->GetData();
2355         if (handler->GetName().Cmp(name) == 0) return handler;
2356 
2357         node = node->GetNext();
2358     }
2359     return 0;
2360 }
2361 
FindHandler(const wxString & extension,long bitmapType)2362 wxImageHandler *wxImage::FindHandler( const wxString& extension, long bitmapType )
2363 {
2364     wxList::compatibility_iterator node = sm_handlers.GetFirst();
2365     while (node)
2366     {
2367         wxImageHandler *handler = (wxImageHandler*)node->GetData();
2368         if ( (handler->GetExtension().Cmp(extension) == 0) &&
2369             (bitmapType == -1 || handler->GetType() == bitmapType) )
2370             return handler;
2371         node = node->GetNext();
2372     }
2373     return 0;
2374 }
2375 
FindHandler(long bitmapType)2376 wxImageHandler *wxImage::FindHandler( long bitmapType )
2377 {
2378     wxList::compatibility_iterator node = sm_handlers.GetFirst();
2379     while (node)
2380     {
2381         wxImageHandler *handler = (wxImageHandler *)node->GetData();
2382         if (handler->GetType() == bitmapType) return handler;
2383         node = node->GetNext();
2384     }
2385     return 0;
2386 }
2387 
FindHandlerMime(const wxString & mimetype)2388 wxImageHandler *wxImage::FindHandlerMime( const wxString& mimetype )
2389 {
2390     wxList::compatibility_iterator node = sm_handlers.GetFirst();
2391     while (node)
2392     {
2393         wxImageHandler *handler = (wxImageHandler *)node->GetData();
2394         if (handler->GetMimeType().IsSameAs(mimetype, false)) return handler;
2395         node = node->GetNext();
2396     }
2397     return 0;
2398 }
2399 
InitStandardHandlers()2400 void wxImage::InitStandardHandlers()
2401 {
2402 #if wxUSE_STREAMS
2403     AddHandler(new wxBMPHandler);
2404 #endif // wxUSE_STREAMS
2405 }
2406 
CleanUpHandlers()2407 void wxImage::CleanUpHandlers()
2408 {
2409     wxList::compatibility_iterator node = sm_handlers.GetFirst();
2410     while (node)
2411     {
2412         wxImageHandler *handler = (wxImageHandler *)node->GetData();
2413         wxList::compatibility_iterator next = node->GetNext();
2414         delete handler;
2415         node = next;
2416     }
2417 
2418     sm_handlers.Clear();
2419 }
2420 
GetImageExtWildcard()2421 wxString wxImage::GetImageExtWildcard()
2422 {
2423     wxString fmts;
2424 
2425     wxList& Handlers = wxImage::GetHandlers();
2426     wxList::compatibility_iterator Node = Handlers.GetFirst();
2427     while ( Node )
2428     {
2429         wxImageHandler* Handler = (wxImageHandler*)Node->GetData();
2430         fmts += wxT("*.") + Handler->GetExtension();
2431         Node = Node->GetNext();
2432         if ( Node ) fmts += wxT(";");
2433     }
2434 
2435     return wxT("(") + fmts + wxT(")|") + fmts;
2436 }
2437 
RGBtoHSV(const RGBValue & rgb)2438 wxImage::HSVValue wxImage::RGBtoHSV(const RGBValue& rgb)
2439 {
2440     const double red = rgb.red / 255.0,
2441                  green = rgb.green / 255.0,
2442                  blue = rgb.blue / 255.0;
2443 
2444     // find the min and max intensity (and remember which one was it for the
2445     // latter)
2446     double minimumRGB = red;
2447     if ( green < minimumRGB )
2448         minimumRGB = green;
2449     if ( blue < minimumRGB )
2450         minimumRGB = blue;
2451 
2452     enum { RED, GREEN, BLUE } chMax = RED;
2453     double maximumRGB = red;
2454     if ( green > maximumRGB )
2455     {
2456         chMax = GREEN;
2457         maximumRGB = green;
2458     }
2459     if ( blue > maximumRGB )
2460     {
2461         chMax = BLUE;
2462         maximumRGB = blue;
2463     }
2464 
2465     const double value = maximumRGB;
2466 
2467     double hue = 0.0, saturation;
2468     const double deltaRGB = maximumRGB - minimumRGB;
2469     if ( wxIsNullDouble(deltaRGB) )
2470     {
2471         // Gray has no color
2472         hue = 0.0;
2473         saturation = 0.0;
2474     }
2475     else
2476     {
2477         switch ( chMax )
2478         {
2479             case RED:
2480                 hue = (green - blue) / deltaRGB;
2481                 break;
2482 
2483             case GREEN:
2484                 hue = 2.0 + (blue - red) / deltaRGB;
2485                 break;
2486 
2487             case BLUE:
2488                 hue = 4.0 + (red - green) / deltaRGB;
2489                 break;
2490 
2491             default:
2492                 wxFAIL_MSG(wxT("hue not specified"));
2493                 break;
2494         }
2495 
2496         hue /= 6.0;
2497 
2498         if ( hue < 0.0 )
2499             hue += 1.0;
2500 
2501         saturation = deltaRGB / maximumRGB;
2502     }
2503 
2504     return HSVValue(hue, saturation, value);
2505 }
2506 
HSVtoRGB(const HSVValue & hsv)2507 wxImage::RGBValue wxImage::HSVtoRGB(const HSVValue& hsv)
2508 {
2509     double red, green, blue;
2510 
2511     if ( wxIsNullDouble(hsv.saturation) )
2512     {
2513         // Grey
2514         red = hsv.value;
2515         green = hsv.value;
2516         blue = hsv.value;
2517     }
2518     else // not grey
2519     {
2520         double hue = hsv.hue * 6.0;      // sector 0 to 5
2521         int i = (int)floor(hue);
2522         double f = hue - i;          // fractional part of h
2523         double p = hsv.value * (1.0 - hsv.saturation);
2524 
2525         switch (i)
2526         {
2527             case 0:
2528                 red = hsv.value;
2529                 green = hsv.value * (1.0 - hsv.saturation * (1.0 - f));
2530                 blue = p;
2531                 break;
2532 
2533             case 1:
2534                 red = hsv.value * (1.0 - hsv.saturation * f);
2535                 green = hsv.value;
2536                 blue = p;
2537                 break;
2538 
2539             case 2:
2540                 red = p;
2541                 green = hsv.value;
2542                 blue = hsv.value * (1.0 - hsv.saturation * (1.0 - f));
2543                 break;
2544 
2545             case 3:
2546                 red = p;
2547                 green = hsv.value * (1.0 - hsv.saturation * f);
2548                 blue = hsv.value;
2549                 break;
2550 
2551             case 4:
2552                 red = hsv.value * (1.0 - hsv.saturation * (1.0 - f));
2553                 green = p;
2554                 blue = hsv.value;
2555                 break;
2556 
2557             default:    // case 5:
2558                 red = hsv.value;
2559                 green = p;
2560                 blue = hsv.value * (1.0 - hsv.saturation * f);
2561                 break;
2562         }
2563     }
2564 
2565     return RGBValue((unsigned char)(red * 255.0),
2566                     (unsigned char)(green * 255.0),
2567                     (unsigned char)(blue * 255.0));
2568 }
2569 
2570 /*
2571  * Rotates the hue of each pixel of the image. angle is a double in the range
2572  * -1.0..1.0 where -1.0 is -360 degrees and 1.0 is 360 degrees
2573  */
RotateHue(double angle)2574 void wxImage::RotateHue(double angle)
2575 {
2576     AllocExclusive();
2577 
2578     unsigned char *srcBytePtr;
2579     unsigned char *dstBytePtr;
2580     unsigned long count;
2581     wxImage::HSVValue hsv;
2582     wxImage::RGBValue rgb;
2583 
2584     wxASSERT (angle >= -1.0 && angle <= 1.0);
2585     count = M_IMGDATA->m_width * M_IMGDATA->m_height;
2586     if ( count > 0 && !wxIsNullDouble(angle) )
2587     {
2588         srcBytePtr = M_IMGDATA->m_data;
2589         dstBytePtr = srcBytePtr;
2590         do
2591         {
2592             rgb.red = *srcBytePtr++;
2593             rgb.green = *srcBytePtr++;
2594             rgb.blue = *srcBytePtr++;
2595             hsv = RGBtoHSV(rgb);
2596 
2597             hsv.hue = hsv.hue + angle;
2598             if (hsv.hue > 1.0)
2599                 hsv.hue = hsv.hue - 1.0;
2600             else if (hsv.hue < 0.0)
2601                 hsv.hue = hsv.hue + 1.0;
2602 
2603             rgb = HSVtoRGB(hsv);
2604             *dstBytePtr++ = rgb.red;
2605             *dstBytePtr++ = rgb.green;
2606             *dstBytePtr++ = rgb.blue;
2607         } while (--count != 0);
2608     }
2609 }
2610 
2611 //-----------------------------------------------------------------------------
2612 // wxImageHandler
2613 //-----------------------------------------------------------------------------
2614 
IMPLEMENT_ABSTRACT_CLASS(wxImageHandler,wxObject)2615 IMPLEMENT_ABSTRACT_CLASS(wxImageHandler,wxObject)
2616 
2617 #if wxUSE_STREAMS
2618 bool wxImageHandler::LoadFile( wxImage *WXUNUSED(image), wxInputStream& WXUNUSED(stream), bool WXUNUSED(verbose), int WXUNUSED(index) )
2619 {
2620     return false;
2621 }
2622 
SaveFile(wxImage * WXUNUSED (image),wxOutputStream & WXUNUSED (stream),bool WXUNUSED (verbose))2623 bool wxImageHandler::SaveFile( wxImage *WXUNUSED(image), wxOutputStream& WXUNUSED(stream), bool WXUNUSED(verbose) )
2624 {
2625     return false;
2626 }
2627 
GetImageCount(wxInputStream & WXUNUSED (stream))2628 int wxImageHandler::GetImageCount( wxInputStream& WXUNUSED(stream) )
2629 {
2630     return 1;
2631 }
2632 
CanRead(const wxString & name)2633 bool wxImageHandler::CanRead( const wxString& name )
2634 {
2635     if (wxFileExists(name))
2636     {
2637         wxImageFileInputStream stream(name);
2638         return CanRead(stream);
2639     }
2640 
2641     wxLogError( _("Can't check image format of file '%s': file does not exist."), name.c_str() );
2642 
2643     return false;
2644 }
2645 
CallDoCanRead(wxInputStream & stream)2646 bool wxImageHandler::CallDoCanRead(wxInputStream& stream)
2647 {
2648     wxFileOffset posOld = stream.TellI();
2649     if ( posOld == wxInvalidOffset )
2650     {
2651         // can't test unseekable stream
2652         return false;
2653     }
2654 
2655     bool ok = DoCanRead(stream);
2656 
2657     // restore the old position to be able to test other formats and so on
2658     if ( stream.SeekI(posOld) == wxInvalidOffset )
2659     {
2660         wxLogDebug(_T("Failed to rewind the stream in wxImageHandler!"));
2661 
2662         // reading would fail anyhow as we're not at the right position
2663         return false;
2664     }
2665 
2666     return ok;
2667 }
2668 
2669 #endif // wxUSE_STREAMS
2670 
2671 // ----------------------------------------------------------------------------
2672 // image histogram stuff
2673 // ----------------------------------------------------------------------------
2674 
2675 bool
FindFirstUnusedColour(unsigned char * r,unsigned char * g,unsigned char * b,unsigned char r2,unsigned char b2,unsigned char g2) const2676 wxImageHistogram::FindFirstUnusedColour(unsigned char *r,
2677                                         unsigned char *g,
2678                                         unsigned char *b,
2679                                         unsigned char r2,
2680                                         unsigned char b2,
2681                                         unsigned char g2) const
2682 {
2683     unsigned long key = MakeKey(r2, g2, b2);
2684 
2685     while ( find(key) != end() )
2686     {
2687         // color already used
2688         r2++;
2689         if ( r2 >= 255 )
2690         {
2691             r2 = 0;
2692             g2++;
2693             if ( g2 >= 255 )
2694             {
2695                 g2 = 0;
2696                 b2++;
2697                 if ( b2 >= 255 )
2698                 {
2699                     wxLogError(_("No unused colour in image.") );
2700                     return false;
2701                 }
2702             }
2703         }
2704 
2705         key = MakeKey(r2, g2, b2);
2706     }
2707 
2708     if ( r )
2709         *r = r2;
2710     if ( g )
2711         *g = g2;
2712     if ( b )
2713         *b = b2;
2714 
2715     return true;
2716 }
2717 
2718 bool
FindFirstUnusedColour(unsigned char * r,unsigned char * g,unsigned char * b,unsigned char r2,unsigned char b2,unsigned char g2) const2719 wxImage::FindFirstUnusedColour(unsigned char *r,
2720                                unsigned char *g,
2721                                unsigned char *b,
2722                                unsigned char r2,
2723                                unsigned char b2,
2724                                unsigned char g2) const
2725 {
2726     wxImageHistogram histogram;
2727 
2728     ComputeHistogram(histogram);
2729 
2730     return histogram.FindFirstUnusedColour(r, g, b, r2, g2, b2);
2731 }
2732 
2733 
2734 
2735 // GRG, Dic/99
2736 // Counts and returns the number of different colours. Optionally stops
2737 // when it exceeds 'stopafter' different colours. This is useful, for
2738 // example, to see if the image can be saved as 8-bit (256 colour or
2739 // less, in this case it would be invoked as CountColours(256)). Default
2740 // value for stopafter is -1 (don't care).
2741 //
CountColours(unsigned long stopafter) const2742 unsigned long wxImage::CountColours( unsigned long stopafter ) const
2743 {
2744     wxHashTable h;
2745     wxObject dummy;
2746     unsigned char r, g, b;
2747     unsigned char *p;
2748     unsigned long size, nentries, key;
2749 
2750     p = GetData();
2751     size = GetWidth() * GetHeight();
2752     nentries = 0;
2753 
2754     for (unsigned long j = 0; (j < size) && (nentries <= stopafter) ; j++)
2755     {
2756         r = *(p++);
2757         g = *(p++);
2758         b = *(p++);
2759         key = wxImageHistogram::MakeKey(r, g, b);
2760 
2761         if (h.Get(key) == NULL)
2762         {
2763             h.Put(key, &dummy);
2764             nentries++;
2765         }
2766     }
2767 
2768     return nentries;
2769 }
2770 
2771 
ComputeHistogram(wxImageHistogram & h) const2772 unsigned long wxImage::ComputeHistogram( wxImageHistogram &h ) const
2773 {
2774     unsigned char *p = GetData();
2775     unsigned long nentries = 0;
2776 
2777     h.clear();
2778 
2779     const unsigned long size = GetWidth() * GetHeight();
2780 
2781     unsigned char r, g, b;
2782     for ( unsigned long n = 0; n < size; n++ )
2783     {
2784         r = *p++;
2785         g = *p++;
2786         b = *p++;
2787 
2788         wxImageHistogramEntry& entry = h[wxImageHistogram::MakeKey(r, g, b)];
2789 
2790         if ( entry.value++ == 0 )
2791             entry.index = nentries++;
2792     }
2793 
2794     return nentries;
2795 }
2796 
2797 /*
2798  * Rotation code by Carlos Moreno
2799  */
2800 
2801 static const double wxROTATE_EPSILON = 1e-10;
2802 
2803 // Auxiliary function to rotate a point (x,y) with respect to point p0
2804 // make it inline and use a straight return to facilitate optimization
2805 // also, the function receives the sine and cosine of the angle to avoid
2806 // repeating the time-consuming calls to these functions -- sin/cos can
2807 // be computed and stored in the calling function.
2808 
2809 static inline wxRealPoint
wxRotatePoint(const wxRealPoint & p,double cos_angle,double sin_angle,const wxRealPoint & p0)2810 wxRotatePoint(const wxRealPoint& p, double cos_angle, double sin_angle,
2811               const wxRealPoint& p0)
2812 {
2813     return wxRealPoint(p0.x + (p.x - p0.x) * cos_angle - (p.y - p0.y) * sin_angle,
2814                        p0.y + (p.y - p0.y) * cos_angle + (p.x - p0.x) * sin_angle);
2815 }
2816 
2817 static inline wxRealPoint
wxRotatePoint(double x,double y,double cos_angle,double sin_angle,const wxRealPoint & p0)2818 wxRotatePoint(double x, double y, double cos_angle, double sin_angle,
2819               const wxRealPoint & p0)
2820 {
2821     return wxRotatePoint (wxRealPoint(x,y), cos_angle, sin_angle, p0);
2822 }
2823 
Rotate(double angle,const wxPoint & centre_of_rotation,bool interpolating,wxPoint * offset_after_rotation) const2824 wxImage wxImage::Rotate(double angle, const wxPoint & centre_of_rotation, bool interpolating, wxPoint * offset_after_rotation) const
2825 {
2826     int i;
2827     angle = -angle;     // screen coordinates are a mirror image of "real" coordinates
2828 
2829     bool has_alpha = HasAlpha();
2830 
2831     const int w = GetWidth(),
2832               h = GetHeight();
2833 
2834     // Create pointer-based array to accelerate access to wxImage's data
2835     unsigned char ** data = new unsigned char * [h];
2836     data[0] = GetData();
2837     for (i = 1; i < h; i++)
2838         data[i] = data[i - 1] + (3 * w);
2839 
2840     // Same for alpha channel
2841     unsigned char ** alpha = NULL;
2842     if (has_alpha)
2843     {
2844         alpha = new unsigned char * [h];
2845         alpha[0] = GetAlpha();
2846         for (i = 1; i < h; i++)
2847             alpha[i] = alpha[i - 1] + w;
2848     }
2849 
2850     // precompute coefficients for rotation formula
2851     // (sine and cosine of the angle)
2852     const double cos_angle = cos(angle);
2853     const double sin_angle = sin(angle);
2854 
2855     // Create new Image to store the result
2856     // First, find rectangle that covers the rotated image;  to do that,
2857     // rotate the four corners
2858 
2859     const wxRealPoint p0(centre_of_rotation.x, centre_of_rotation.y);
2860 
2861     wxRealPoint p1 = wxRotatePoint (0, 0, cos_angle, sin_angle, p0);
2862     wxRealPoint p2 = wxRotatePoint (0, h, cos_angle, sin_angle, p0);
2863     wxRealPoint p3 = wxRotatePoint (w, 0, cos_angle, sin_angle, p0);
2864     wxRealPoint p4 = wxRotatePoint (w, h, cos_angle, sin_angle, p0);
2865 
2866     int x1a = (int) floor (wxMin (wxMin(p1.x, p2.x), wxMin(p3.x, p4.x)));
2867     int y1a = (int) floor (wxMin (wxMin(p1.y, p2.y), wxMin(p3.y, p4.y)));
2868     int x2a = (int) ceil (wxMax (wxMax(p1.x, p2.x), wxMax(p3.x, p4.x)));
2869     int y2a = (int) ceil (wxMax (wxMax(p1.y, p2.y), wxMax(p3.y, p4.y)));
2870 
2871     // Create rotated image
2872     wxImage rotated (x2a - x1a + 1, y2a - y1a + 1, false);
2873     // With alpha channel
2874     if (has_alpha)
2875         rotated.SetAlpha();
2876 
2877     if (offset_after_rotation != NULL)
2878     {
2879         *offset_after_rotation = wxPoint (x1a, y1a);
2880     }
2881 
2882     // GRG: The rotated (destination) image is always accessed
2883     //      sequentially, so there is no need for a pointer-based
2884     //      array here (and in fact it would be slower).
2885     //
2886     unsigned char * dst = rotated.GetData();
2887 
2888     unsigned char * alpha_dst = NULL;
2889     if (has_alpha)
2890         alpha_dst = rotated.GetAlpha();
2891 
2892     // GRG: if the original image has a mask, use its RGB values
2893     //      as the blank pixel, else, fall back to default (black).
2894     //
2895     unsigned char blank_r = 0;
2896     unsigned char blank_g = 0;
2897     unsigned char blank_b = 0;
2898 
2899     if (HasMask())
2900     {
2901         blank_r = GetMaskRed();
2902         blank_g = GetMaskGreen();
2903         blank_b = GetMaskBlue();
2904         rotated.SetMaskColour( blank_r, blank_g, blank_b );
2905     }
2906 
2907     // Now, for each point of the rotated image, find where it came from, by
2908     // performing an inverse rotation (a rotation of -angle) and getting the
2909     // pixel at those coordinates
2910 
2911     const int rH = rotated.GetHeight();
2912     const int rW = rotated.GetWidth();
2913 
2914     // GRG: I've taken the (interpolating) test out of the loops, so that
2915     //      it is done only once, instead of repeating it for each pixel.
2916 
2917     if (interpolating)
2918     {
2919         for (int y = 0; y < rH; y++)
2920         {
2921             for (int x = 0; x < rW; x++)
2922             {
2923                 wxRealPoint src = wxRotatePoint (x + x1a, y + y1a, cos_angle, -sin_angle, p0);
2924 
2925                 if (-0.25 < src.x && src.x < w - 0.75 &&
2926                     -0.25 < src.y && src.y < h - 0.75)
2927                 {
2928                     // interpolate using the 4 enclosing grid-points.  Those
2929                     // points can be obtained using floor and ceiling of the
2930                     // exact coordinates of the point
2931                     int x1, y1, x2, y2;
2932 
2933                     if (0 < src.x && src.x < w - 1)
2934                     {
2935                         x1 = wxRound(floor(src.x));
2936                         x2 = wxRound(ceil(src.x));
2937                     }
2938                     else    // else means that x is near one of the borders (0 or width-1)
2939                     {
2940                         x1 = x2 = wxRound (src.x);
2941                     }
2942 
2943                     if (0 < src.y && src.y < h - 1)
2944                     {
2945                         y1 = wxRound(floor(src.y));
2946                         y2 = wxRound(ceil(src.y));
2947                     }
2948                     else
2949                     {
2950                         y1 = y2 = wxRound (src.y);
2951                     }
2952 
2953                     // get four points and the distances (square of the distance,
2954                     // for efficiency reasons) for the interpolation formula
2955 
2956                     // GRG: Do not calculate the points until they are
2957                     //      really needed -- this way we can calculate
2958                     //      just one, instead of four, if d1, d2, d3
2959                     //      or d4 are < wxROTATE_EPSILON
2960 
2961                     const double d1 = (src.x - x1) * (src.x - x1) + (src.y - y1) * (src.y - y1);
2962                     const double d2 = (src.x - x2) * (src.x - x2) + (src.y - y1) * (src.y - y1);
2963                     const double d3 = (src.x - x2) * (src.x - x2) + (src.y - y2) * (src.y - y2);
2964                     const double d4 = (src.x - x1) * (src.x - x1) + (src.y - y2) * (src.y - y2);
2965 
2966                     // Now interpolate as a weighted average of the four surrounding
2967                     // points, where the weights are the distances to each of those points
2968 
2969                     // If the point is exactly at one point of the grid of the source
2970                     // image, then don't interpolate -- just assign the pixel
2971 
2972                     // d1,d2,d3,d4 are positive -- no need for abs()
2973                     if (d1 < wxROTATE_EPSILON)
2974                     {
2975                         unsigned char *p = data[y1] + (3 * x1);
2976                         *(dst++) = *(p++);
2977                         *(dst++) = *(p++);
2978                         *(dst++) = *p;
2979 
2980                         if (has_alpha)
2981                             *(alpha_dst++) = *(alpha[y1] + x1);
2982                     }
2983                     else if (d2 < wxROTATE_EPSILON)
2984                     {
2985                         unsigned char *p = data[y1] + (3 * x2);
2986                         *(dst++) = *(p++);
2987                         *(dst++) = *(p++);
2988                         *(dst++) = *p;
2989 
2990                         if (has_alpha)
2991                             *(alpha_dst++) = *(alpha[y1] + x2);
2992                     }
2993                     else if (d3 < wxROTATE_EPSILON)
2994                     {
2995                         unsigned char *p = data[y2] + (3 * x2);
2996                         *(dst++) = *(p++);
2997                         *(dst++) = *(p++);
2998                         *(dst++) = *p;
2999 
3000                         if (has_alpha)
3001                             *(alpha_dst++) = *(alpha[y2] + x2);
3002                     }
3003                     else if (d4 < wxROTATE_EPSILON)
3004                     {
3005                         unsigned char *p = data[y2] + (3 * x1);
3006                         *(dst++) = *(p++);
3007                         *(dst++) = *(p++);
3008                         *(dst++) = *p;
3009 
3010                         if (has_alpha)
3011                             *(alpha_dst++) = *(alpha[y2] + x1);
3012                     }
3013                     else
3014                     {
3015                         // weights for the weighted average are proportional to the inverse of the distance
3016                         unsigned char *v1 = data[y1] + (3 * x1);
3017                         unsigned char *v2 = data[y1] + (3 * x2);
3018                         unsigned char *v3 = data[y2] + (3 * x2);
3019                         unsigned char *v4 = data[y2] + (3 * x1);
3020 
3021                         const double w1 = 1/d1, w2 = 1/d2, w3 = 1/d3, w4 = 1/d4;
3022 
3023                         // GRG: Unrolled.
3024 
3025                         *(dst++) = (unsigned char)
3026                             ( (w1 * *(v1++) + w2 * *(v2++) +
3027                                w3 * *(v3++) + w4 * *(v4++)) /
3028                               (w1 + w2 + w3 + w4) );
3029                         *(dst++) = (unsigned char)
3030                             ( (w1 * *(v1++) + w2 * *(v2++) +
3031                                w3 * *(v3++) + w4 * *(v4++)) /
3032                               (w1 + w2 + w3 + w4) );
3033                         *(dst++) = (unsigned char)
3034                             ( (w1 * *v1 + w2 * *v2 +
3035                                w3 * *v3 + w4 * *v4) /
3036                               (w1 + w2 + w3 + w4) );
3037 
3038                         if (has_alpha)
3039                         {
3040                             v1 = alpha[y1] + (x1);
3041                             v2 = alpha[y1] + (x2);
3042                             v3 = alpha[y2] + (x2);
3043                             v4 = alpha[y2] + (x1);
3044 
3045                             *(alpha_dst++) = (unsigned char)
3046                                 ( (w1 * *v1 + w2 * *v2 +
3047                                    w3 * *v3 + w4 * *v4) /
3048                                   (w1 + w2 + w3 + w4) );
3049                         }
3050                     }
3051                 }
3052                 else
3053                 {
3054                     *(dst++) = blank_r;
3055                     *(dst++) = blank_g;
3056                     *(dst++) = blank_b;
3057 
3058                     if (has_alpha)
3059                         *(alpha_dst++) = 0;
3060                 }
3061             }
3062         }
3063     }
3064     else    // not interpolating
3065     {
3066         for (int y = 0; y < rH; y++)
3067         {
3068             for (int x = 0; x < rW; x++)
3069             {
3070                 wxRealPoint src = wxRotatePoint (x + x1a, y + y1a, cos_angle, -sin_angle, p0);
3071 
3072                 const int xs = wxRound (src.x);      // wxRound rounds to the
3073                 const int ys = wxRound (src.y);      // closest integer
3074 
3075                 if (0 <= xs && xs < w && 0 <= ys && ys < h)
3076                 {
3077                     unsigned char *p = data[ys] + (3 * xs);
3078                     *(dst++) = *(p++);
3079                     *(dst++) = *(p++);
3080                     *(dst++) = *p;
3081 
3082                     if (has_alpha)
3083                         *(alpha_dst++) = *(alpha[ys] + (xs));
3084                 }
3085                 else
3086                 {
3087                     *(dst++) = blank_r;
3088                     *(dst++) = blank_g;
3089                     *(dst++) = blank_b;
3090 
3091                     if (has_alpha)
3092                         *(alpha_dst++) = 255;
3093                 }
3094             }
3095         }
3096     }
3097 
3098     delete [] data;
3099 
3100     if (has_alpha)
3101         delete [] alpha;
3102 
3103     return rotated;
3104 }
3105 
3106 
3107 
3108 
3109 
3110 // A module to allow wxImage initialization/cleanup
3111 // without calling these functions from app.cpp or from
3112 // the user's application.
3113 
3114 class wxImageModule: public wxModule
3115 {
3116 DECLARE_DYNAMIC_CLASS(wxImageModule)
3117 public:
wxImageModule()3118     wxImageModule() {}
OnInit()3119     bool OnInit() { wxImage::InitStandardHandlers(); return true; }
OnExit()3120     void OnExit() { wxImage::CleanUpHandlers(); }
3121 };
3122 
3123 IMPLEMENT_DYNAMIC_CLASS(wxImageModule, wxModule)
3124 
3125 
3126 #endif // wxUSE_IMAGE
3127