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