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