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