]> git.saurik.com Git - wxWidgets.git/blame - src/common/image.cpp
wxLogTrace doesn't give messages with two 'Trace' in the beginning
[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
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10#ifdef __GNUG__
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
01111366 21#include "wx/image.h"
99c67c77 22#include "wx/bitmap.h"
01111366
RR
23#include "wx/debug.h"
24#include "wx/log.h"
f6fcbb63 25#include "wx/app.h"
dc86cb34 26#include "wx/filefn.h"
3d05544e 27#include "wx/wfstream.h"
b75867a6 28#include "wx/intl.h"
a91b47e8 29#include "wx/module.h"
01111366 30
58a8ab88
JS
31// For memcpy
32#include <string.h>
7a632f10 33#include <math.h>
58a8ab88 34
a3ef5bf5 35#ifdef __SALFORDC__
edccf428 36 #undef FAR
a3ef5bf5
JS
37#endif
38
2432b92d 39#ifdef __WXMSW__
edccf428 40 #include "wx/msw/private.h"
2432b92d
JS
41#endif
42
01111366
RR
43//-----------------------------------------------------------------------------
44// wxImage
45//-----------------------------------------------------------------------------
46
47class wxImageRefData: public wxObjectRefData
48{
fd0eed64 49public:
edccf428
VZ
50 wxImageRefData();
51 ~wxImageRefData();
c7abc967 52
dbda9e86
JS
53 int m_width;
54 int m_height;
55 unsigned char *m_data;
56 bool m_hasMask;
57 unsigned char m_maskRed,m_maskGreen,m_maskBlue;
58 bool m_ok;
01111366
RR
59};
60
edccf428 61wxImageRefData::wxImageRefData()
01111366 62{
fd0eed64
RR
63 m_width = 0;
64 m_height = 0;
65 m_data = (unsigned char*) NULL;
66 m_ok = FALSE;
67 m_maskRed = 0;
68 m_maskGreen = 0;
69 m_maskBlue = 0;
70 m_hasMask = FALSE;
01111366
RR
71}
72
edccf428 73wxImageRefData::~wxImageRefData()
01111366 74{
97fdfcc9 75 if (m_data)
58c837a4 76 free( m_data );
01111366
RR
77}
78
79wxList wxImage::sm_handlers;
80
81//-----------------------------------------------------------------------------
82
83#define M_IMGDATA ((wxImageRefData *)m_refData)
84
edccf428 85 IMPLEMENT_DYNAMIC_CLASS(wxImage, wxObject)
01111366
RR
86
87wxImage::wxImage()
88{
89}
90
91wxImage::wxImage( int width, int height )
92{
fd0eed64 93 Create( width, height );
01111366
RR
94}
95
96wxImage::wxImage( const wxString& name, long type )
97{
fd0eed64 98 LoadFile( name, type );
01111366
RR
99}
100
9e9ee68e
VS
101wxImage::wxImage( const wxString& name, const wxString& mimetype )
102{
103 LoadFile( name, mimetype );
104}
105
e02afc7a 106#if wxUSE_STREAMS
3d05544e
JS
107wxImage::wxImage( wxInputStream& stream, long type )
108{
109 LoadFile( stream, type );
110}
9e9ee68e
VS
111
112wxImage::wxImage( wxInputStream& stream, const wxString& mimetype )
113{
114 LoadFile( stream, mimetype );
115}
e02afc7a 116#endif // wxUSE_STREAMS
3d05544e 117
4698648f
VZ
118wxImage::wxImage( const wxImage& image )
119{
120 Ref(image);
01111366
RR
121}
122
4698648f
VZ
123wxImage::wxImage( const wxImage* image )
124{
125 if (image) Ref(*image);
01111366
RR
126}
127
128void wxImage::Create( int width, int height )
129{
aadaf841
GRG
130 UnRef();
131
fd0eed64 132 m_refData = new wxImageRefData();
c7abc967 133
fd0eed64
RR
134 M_IMGDATA->m_data = (unsigned char *) malloc( width*height*3 );
135 if (M_IMGDATA->m_data)
136 {
137 for (int l = 0; l < width*height*3; l++) M_IMGDATA->m_data[l] = 0;
c7abc967 138
fd0eed64
RR
139 M_IMGDATA->m_width = width;
140 M_IMGDATA->m_height = height;
141 M_IMGDATA->m_ok = TRUE;
142 }
143 else
144 {
145 UnRef();
146 }
01111366
RR
147}
148
149void wxImage::Destroy()
150{
fd0eed64 151 UnRef();
01111366
RR
152}
153
ce9a75d2 154wxImage wxImage::Scale( int width, int height ) const
4bc67cc5
RR
155{
156 wxImage image;
c7abc967 157
223d09f6 158 wxCHECK_MSG( Ok(), image, wxT("invalid image") );
c7abc967 159
223d09f6 160 wxCHECK_MSG( (width > 0) && (height > 0), image, wxT("invalid image size") );
c7abc967 161
4bc67cc5 162 image.Create( width, height );
c7abc967 163
4bc67cc5 164 char unsigned *data = image.GetData();
c7abc967 165
223d09f6 166 wxCHECK_MSG( data, image, wxT("unable to create image") );
c7abc967 167
4bc67cc5
RR
168 if (M_IMGDATA->m_hasMask)
169 image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
c7abc967 170
6e13c196
RR
171 long old_height = M_IMGDATA->m_height;
172 long old_width = M_IMGDATA->m_width;
c7abc967 173
6e13c196
RR
174 char unsigned *source_data = M_IMGDATA->m_data;
175 char unsigned *target_data = data;
c7abc967 176
6e13c196 177 for (long j = 0; j < height; j++)
4bc67cc5 178 {
6e13c196 179 long y_offset = (j * old_height / height) * old_width;
c7abc967 180
6e13c196 181 for (long i = 0; i < width; i++)
4698648f 182 {
c7abc967
VZ
183 memcpy( target_data,
184 source_data + 3*(y_offset + ((i * old_width )/ width)),
dbda9e86 185 3 );
6e13c196 186 target_data += 3;
4698648f 187 }
4bc67cc5 188 }
c7abc967 189
4bc67cc5
RR
190 return image;
191}
4698648f 192
7b2471a0
SB
193wxImage wxImage::GetSubImage( const wxRect &rect ) const
194{
195 wxImage image;
196
223d09f6 197 wxCHECK_MSG( Ok(), image, wxT("invalid image") );
7b2471a0 198
58c837a4
RR
199 wxCHECK_MSG( (rect.GetLeft()>=0) && (rect.GetTop()>=0) && (rect.GetRight()<=GetWidth()) && (rect.GetBottom()<=GetHeight()),
200 image, wxT("invalid subimage size") );
7b2471a0
SB
201
202 int subwidth=rect.GetWidth();
203 const int subheight=rect.GetHeight();
204
205 image.Create( subwidth, subheight );
206
207 char unsigned *subdata = image.GetData(), *data=GetData();
208
223d09f6 209 wxCHECK_MSG( subdata, image, wxT("unable to create image") );
7b2471a0
SB
210
211 if (M_IMGDATA->m_hasMask)
212 image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
213
214 const int subleft=3*rect.GetLeft();
215 const int width=3*GetWidth();
216 subwidth*=3;
995612e2 217
7b2471a0
SB
218 data+=rect.GetTop()*width+subleft;
219
220 for (long j = 0; j < subheight; ++j)
221 {
222 memcpy( subdata, data, subwidth);
223 subdata+=subwidth;
995612e2 224 data+=width;
7b2471a0
SB
225 }
226
227 return image;
228}
229
be25e480
RR
230void wxImage::Replace( unsigned char r1, unsigned char g1, unsigned char b1,
231 unsigned char r2, unsigned char g2, unsigned char b2 )
232{
233 wxCHECK_RET( Ok(), wxT("invalid image") );
234
235 char unsigned *data = GetData();
06b466c7 236
be25e480
RR
237 const int w = GetWidth();
238 const int h = GetHeight();
239
240 for (int j = 0; j < h; j++)
241 for (int i = 0; i < w; i++)
069d0f27
VZ
242 {
243 if ((data[0] == r1) && (data[1] == g1) && (data[2] == b1))
244 {
245 data[0] = r2;
246 data[1] = g2;
247 data[2] = b2;
248 }
249 data += 3;
250 }
be25e480
RR
251}
252
ef539066
RR
253void wxImage::SetRGB( int x, int y, unsigned char r, unsigned char g, unsigned char b )
254{
223d09f6 255 wxCHECK_RET( Ok(), wxT("invalid image") );
c7abc967 256
ef539066
RR
257 int w = M_IMGDATA->m_width;
258 int h = M_IMGDATA->m_height;
c7abc967 259
223d09f6 260 wxCHECK_RET( (x>=0) && (y>=0) && (x<w) && (y<h), wxT("invalid image index") );
c7abc967 261
ef539066 262 long pos = (y * w + x) * 3;
c7abc967 263
ef539066
RR
264 M_IMGDATA->m_data[ pos ] = r;
265 M_IMGDATA->m_data[ pos+1 ] = g;
266 M_IMGDATA->m_data[ pos+2 ] = b;
267}
268
269unsigned char wxImage::GetRed( int x, int y )
270{
223d09f6 271 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 272
ef539066
RR
273 int w = M_IMGDATA->m_width;
274 int h = M_IMGDATA->m_height;
c7abc967 275
223d09f6 276 wxCHECK_MSG( (x>=0) && (y>=0) && (x<w) && (y<h), 0, wxT("invalid image index") );
c7abc967 277
ef539066 278 long pos = (y * w + x) * 3;
c7abc967 279
ef539066
RR
280 return M_IMGDATA->m_data[pos];
281}
282
283unsigned char wxImage::GetGreen( int x, int y )
284{
223d09f6 285 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 286
ef539066
RR
287 int w = M_IMGDATA->m_width;
288 int h = M_IMGDATA->m_height;
c7abc967 289
223d09f6 290 wxCHECK_MSG( (x>=0) && (y>=0) && (x<w) && (y<h), 0, wxT("invalid image index") );
c7abc967 291
ef539066 292 long pos = (y * w + x) * 3;
c7abc967 293
ef539066
RR
294 return M_IMGDATA->m_data[pos+1];
295}
296
297unsigned char wxImage::GetBlue( int x, int y )
298{
223d09f6 299 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 300
ef539066
RR
301 int w = M_IMGDATA->m_width;
302 int h = M_IMGDATA->m_height;
c7abc967 303
223d09f6 304 wxCHECK_MSG( (x>=0) && (y>=0) && (x<w) && (y<h), 0, wxT("invalid image index") );
c7abc967 305
ef539066 306 long pos = (y * w + x) * 3;
c7abc967 307
ef539066
RR
308 return M_IMGDATA->m_data[pos+2];
309}
4698648f
VZ
310
311bool wxImage::Ok() const
312{
313 return (M_IMGDATA && M_IMGDATA->m_ok);
01111366
RR
314}
315
316char unsigned *wxImage::GetData() const
317{
223d09f6 318 wxCHECK_MSG( Ok(), (char unsigned *)NULL, wxT("invalid image") );
c7abc967 319
fd0eed64 320 return M_IMGDATA->m_data;
01111366
RR
321}
322
58a8ab88 323void wxImage::SetData( char unsigned *data )
01111366 324{
223d09f6 325 wxCHECK_RET( Ok(), wxT("invalid image") );
58a8ab88 326
ed58dbea
RR
327 wxImageRefData *newRefData = new wxImageRefData();
328
329 newRefData->m_width = M_IMGDATA->m_width;
330 newRefData->m_height = M_IMGDATA->m_height;
331 newRefData->m_data = data;
332 newRefData->m_ok = TRUE;
333 newRefData->m_maskRed = M_IMGDATA->m_maskRed;
334 newRefData->m_maskGreen = M_IMGDATA->m_maskGreen;
335 newRefData->m_maskBlue = M_IMGDATA->m_maskBlue;
336 newRefData->m_hasMask = M_IMGDATA->m_hasMask;
995612e2 337
ed58dbea 338 UnRef();
995612e2 339
ed58dbea 340 m_refData = newRefData;
01111366
RR
341}
342
343void wxImage::SetMaskColour( unsigned char r, unsigned char g, unsigned char b )
344{
223d09f6 345 wxCHECK_RET( Ok(), wxT("invalid image") );
c7abc967 346
fd0eed64
RR
347 M_IMGDATA->m_maskRed = r;
348 M_IMGDATA->m_maskGreen = g;
349 M_IMGDATA->m_maskBlue = b;
350 M_IMGDATA->m_hasMask = TRUE;
01111366
RR
351}
352
353unsigned char wxImage::GetMaskRed() const
354{
223d09f6 355 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 356
fd0eed64 357 return M_IMGDATA->m_maskRed;
01111366
RR
358}
359
360unsigned char wxImage::GetMaskGreen() const
361{
223d09f6 362 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 363
fd0eed64 364 return M_IMGDATA->m_maskGreen;
01111366
RR
365}
366
367unsigned char wxImage::GetMaskBlue() const
368{
223d09f6 369 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 370
fd0eed64 371 return M_IMGDATA->m_maskBlue;
01111366 372}
4698648f 373
01111366
RR
374void wxImage::SetMask( bool mask )
375{
223d09f6 376 wxCHECK_RET( Ok(), wxT("invalid image") );
c7abc967 377
fd0eed64 378 M_IMGDATA->m_hasMask = mask;
01111366
RR
379}
380
381bool wxImage::HasMask() const
382{
223d09f6 383 wxCHECK_MSG( Ok(), FALSE, wxT("invalid image") );
c7abc967 384
fd0eed64 385 return M_IMGDATA->m_hasMask;
01111366
RR
386}
387
4698648f
VZ
388int wxImage::GetWidth() const
389{
223d09f6 390 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 391
4698648f 392 return M_IMGDATA->m_width;
01111366
RR
393}
394
4698648f
VZ
395int wxImage::GetHeight() const
396{
223d09f6 397 wxCHECK_MSG( Ok(), 0, wxT("invalid image") );
c7abc967 398
4698648f 399 return M_IMGDATA->m_height;
01111366
RR
400}
401
402bool wxImage::LoadFile( const wxString& filename, long type )
403{
e02afc7a 404#if wxUSE_STREAMS
3d05544e 405 if (wxFileExists(filename))
fd0eed64 406 {
3d05544e 407 wxFileInputStream stream(filename);
069d0f27 408 wxBufferedInputStream bstream( stream );
1b055864 409 return LoadFile(bstream, type);
3d05544e 410 }
97fdfcc9 411 else
58c837a4
RR
412 {
413 wxLogError( _("Can't load image from file '%s': file does not exist."), filename.c_str() );
9e9ee68e
VS
414
415 return FALSE;
416 }
417#else // !wxUSE_STREAMS
418 return FALSE;
419#endif // wxUSE_STREAMS
420}
421
422bool wxImage::LoadFile( const wxString& filename, const wxString& mimetype )
423{
424#if wxUSE_STREAMS
425 if (wxFileExists(filename))
426 {
427 wxFileInputStream stream(filename);
069d0f27 428 wxBufferedInputStream bstream( stream );
1b055864 429 return LoadFile(bstream, mimetype);
9e9ee68e 430 }
97fdfcc9 431 else
58c837a4
RR
432 {
433 wxLogError( _("Can't load image from file '%s': file does not exist."), filename.c_str() );
c7abc967 434
fd0eed64
RR
435 return FALSE;
436 }
e02afc7a 437#else // !wxUSE_STREAMS
dbda9e86 438 return FALSE;
e02afc7a 439#endif // wxUSE_STREAMS
1ccbb61a
VZ
440}
441
442bool wxImage::SaveFile( const wxString& filename, int type )
443{
e02afc7a 444#if wxUSE_STREAMS
1ccbb61a 445 wxFileOutputStream stream(filename);
9e9ee68e 446
1ccbb61a 447 if ( stream.LastError() == wxStream_NOERROR )
1b055864 448 {
069d0f27 449 wxBufferedOutputStream bstream( stream );
1b055864
RR
450 return SaveFile(bstream, type);
451 }
1ccbb61a 452 else
e02afc7a 453#endif // wxUSE_STREAMS
1ccbb61a 454 return FALSE;
3d05544e 455}
01111366 456
9e9ee68e
VS
457bool wxImage::SaveFile( const wxString& filename, const wxString& mimetype )
458{
459#if wxUSE_STREAMS
460 wxFileOutputStream stream(filename);
c7abc967 461
9e9ee68e 462 if ( stream.LastError() == wxStream_NOERROR )
1b055864 463 {
069d0f27 464 wxBufferedOutputStream bstream( stream );
1b055864
RR
465 return SaveFile(bstream, mimetype);
466 }
9e9ee68e
VS
467 else
468#endif // wxUSE_STREAMS
469 return FALSE;
470}
471
87202f78
SB
472bool wxImage::CanRead( const wxString &name )
473{
474#if wxUSE_STREAMS
475 wxFileInputStream stream(name);
476 return CanRead(stream);
477#else
478 return FALSE;
479#endif
480}
481
e02afc7a 482#if wxUSE_STREAMS
deb2fec0 483
87202f78
SB
484bool wxImage::CanRead( wxInputStream &stream )
485{
486 wxList &list=GetHandlers();
004fd0c8 487
87202f78 488 for ( wxList::Node *node = list.GetFirst(); node; node = node->GetNext() )
004fd0c8 489 {
87202f78
SB
490 wxImageHandler *handler=(wxImageHandler*)node->GetData();
491 if (handler->CanRead( stream ))
069d0f27 492 return TRUE;
87202f78
SB
493 }
494
495 return FALSE;
496}
497
3d05544e
JS
498bool wxImage::LoadFile( wxInputStream& stream, long type )
499{
500 UnRef();
c7abc967 501
fd0eed64 502 m_refData = new wxImageRefData;
c7abc967 503
deb2fec0
SB
504 wxImageHandler *handler;
505
506 if (type==wxBITMAP_TYPE_ANY)
507 {
995612e2 508 wxList &list=GetHandlers();
deb2fec0 509
995612e2
VZ
510 for ( wxList::Node *node = list.GetFirst(); node; node = node->GetNext() )
511 {
512 handler=(wxImageHandler*)node->GetData();
513 if (handler->CanRead( stream ))
514 return handler->LoadFile( this, stream );
7b2471a0 515
995612e2 516 }
deb2fec0 517
58c837a4 518 wxLogWarning( _("No handler found for image type.") );
995612e2 519 return FALSE;
deb2fec0
SB
520 }
521
522 handler = FindHandler(type);
c7abc967 523
4698648f 524 if (handler == NULL)
fd0eed64 525 {
58c837a4 526 wxLogWarning( _("No image handler for type %d defined."), type );
c7abc967 527
fd0eed64
RR
528 return FALSE;
529 }
c7abc967 530
3d05544e 531 return handler->LoadFile( this, stream );
01111366
RR
532}
533
9e9ee68e
VS
534bool wxImage::LoadFile( wxInputStream& stream, const wxString& mimetype )
535{
536 UnRef();
537
538 m_refData = new wxImageRefData;
539
540 wxImageHandler *handler = FindHandlerMime(mimetype);
541
542 if (handler == NULL)
543 {
58c837a4 544 wxLogWarning( _("No image handler for type %s defined."), mimetype.GetData() );
9e9ee68e
VS
545
546 return FALSE;
547 }
548
549 return handler->LoadFile( this, stream );
550}
551
3d05544e 552bool wxImage::SaveFile( wxOutputStream& stream, int type )
01111366 553{
223d09f6 554 wxCHECK_MSG( Ok(), FALSE, wxT("invalid image") );
c7abc967 555
fd0eed64 556 wxImageHandler *handler = FindHandler(type);
c7abc967 557
4698648f 558 if (handler == NULL)
fd0eed64 559 {
58c837a4 560 wxLogWarning( _("No image handler for type %d defined."), type );
9e9ee68e
VS
561
562 return FALSE;
563 }
564
565 return handler->SaveFile( this, stream );
566}
567
568bool wxImage::SaveFile( wxOutputStream& stream, const wxString& mimetype )
569{
223d09f6 570 wxCHECK_MSG( Ok(), FALSE, wxT("invalid image") );
c7abc967 571
9e9ee68e 572 wxImageHandler *handler = FindHandlerMime(mimetype);
c7abc967 573
9e9ee68e
VS
574 if (handler == NULL)
575 {
58c837a4 576 wxLogWarning( _("No image handler for type %s defined."), mimetype.GetData() );
c7abc967 577
dbda9e86 578 return FALSE;
fd0eed64 579 }
c7abc967 580
3d05544e 581 return handler->SaveFile( this, stream );
01111366 582}
e02afc7a 583#endif // wxUSE_STREAMS
01111366
RR
584
585void wxImage::AddHandler( wxImageHandler *handler )
586{
4698648f
VZ
587 // make sure that the memory will be freed at the program end
588 sm_handlers.DeleteContents(TRUE);
c7abc967 589
01111366
RR
590 sm_handlers.Append( handler );
591}
592
593void wxImage::InsertHandler( wxImageHandler *handler )
594{
4698648f
VZ
595 // make sure that the memory will be freed at the program end
596 sm_handlers.DeleteContents(TRUE);
c7abc967 597
01111366
RR
598 sm_handlers.Insert( handler );
599}
600
601bool wxImage::RemoveHandler( const wxString& name )
602{
fd0eed64
RR
603 wxImageHandler *handler = FindHandler(name);
604 if (handler)
605 {
606 sm_handlers.DeleteObject(handler);
607 return TRUE;
608 }
609 else
610 return FALSE;
01111366
RR
611}
612
613wxImageHandler *wxImage::FindHandler( const wxString& name )
614{
fd0eed64
RR
615 wxNode *node = sm_handlers.First();
616 while (node)
617 {
618 wxImageHandler *handler = (wxImageHandler*)node->Data();
ce3ed50d 619 if (handler->GetName().Cmp(name) == 0) return handler;
c7abc967 620
fd0eed64
RR
621 node = node->Next();
622 }
623 return (wxImageHandler *)NULL;
01111366
RR
624}
625
626wxImageHandler *wxImage::FindHandler( const wxString& extension, long bitmapType )
627{
fd0eed64
RR
628 wxNode *node = sm_handlers.First();
629 while (node)
630 {
631 wxImageHandler *handler = (wxImageHandler*)node->Data();
ce3ed50d 632 if ( (handler->GetExtension().Cmp(extension) == 0) &&
fd0eed64 633 (bitmapType == -1 || handler->GetType() == bitmapType) )
dbda9e86 634 return handler;
fd0eed64
RR
635 node = node->Next();
636 }
637 return (wxImageHandler*)NULL;
01111366
RR
638}
639
640wxImageHandler *wxImage::FindHandler( long bitmapType )
641{
fd0eed64
RR
642 wxNode *node = sm_handlers.First();
643 while (node)
644 {
645 wxImageHandler *handler = (wxImageHandler *)node->Data();
646 if (handler->GetType() == bitmapType) return handler;
647 node = node->Next();
648 }
649 return NULL;
650}
651
9e9ee68e
VS
652wxImageHandler *wxImage::FindHandlerMime( const wxString& mimetype )
653{
654 wxNode *node = sm_handlers.First();
655 while (node)
656 {
657 wxImageHandler *handler = (wxImageHandler *)node->Data();
658 if (handler->GetMimeType().IsSameAs(mimetype, FALSE)) return handler;
659 node = node->Next();
660 }
661 return NULL;
662}
663
fd0eed64
RR
664void wxImage::InitStandardHandlers()
665{
deb2fec0 666 AddHandler( new wxBMPHandler );
01111366
RR
667}
668
669void wxImage::CleanUpHandlers()
670{
fd0eed64
RR
671 wxNode *node = sm_handlers.First();
672 while (node)
673 {
674 wxImageHandler *handler = (wxImageHandler *)node->Data();
675 wxNode *next = node->Next();
676 delete handler;
677 delete node;
678 node = next;
679 }
01111366
RR
680}
681
682//-----------------------------------------------------------------------------
683// wxImageHandler
684//-----------------------------------------------------------------------------
685
63d963a1 686IMPLEMENT_ABSTRACT_CLASS(wxImageHandler,wxObject)
01111366 687
e02afc7a 688#if wxUSE_STREAMS
700ec454 689bool wxImageHandler::LoadFile( wxImage *WXUNUSED(image), wxInputStream& WXUNUSED(stream), bool WXUNUSED(verbose), int WXUNUSED(index) )
01111366 690{
fd0eed64 691 return FALSE;
01111366
RR
692}
693
deb2fec0 694bool wxImageHandler::SaveFile( wxImage *WXUNUSED(image), wxOutputStream& WXUNUSED(stream), bool WXUNUSED(verbose) )
01111366 695{
fd0eed64 696 return FALSE;
01111366 697}
0828c087 698
700ec454
RR
699int wxImageHandler::GetImageCount( wxInputStream& WXUNUSED(stream) )
700{
701 return 1;
702}
703
0828c087
VS
704bool wxImageHandler::CanRead( const wxString& name )
705{
0828c087
VS
706 if (wxFileExists(name))
707 {
708 wxFileInputStream stream(name);
709 return CanRead(stream);
710 }
711
712 else {
58c837a4 713 wxLogError( _("Can't check image format of file '%s': file does not exist."), name.c_str() );
0828c087
VS
714
715 return FALSE;
716 }
68874acf 717// return FALSE;
0828c087
VS
718}
719
e02afc7a 720#endif // wxUSE_STREAMS
01111366 721
01111366 722//-----------------------------------------------------------------------------
ce4169a4 723// MSW conversion routines
01111366
RR
724//-----------------------------------------------------------------------------
725
e3554471
JS
726#ifdef __WXMSW__
727
728wxBitmap wxImage::ConvertToBitmap() const
729{
0655ad29
VZ
730 if ( !Ok() )
731 return wxNullBitmap;
732
dbda9e86 733 // sizeLimit is the MS upper limit for the DIB size
48c12cb1 734#ifdef WIN32
c7abc967 735 int sizeLimit = 1024*768*3;
48c12cb1
PA
736#else
737 int sizeLimit = 0x7fff ;
738#endif
c7abc967 739
dbda9e86 740 // width and height of the device-dependent bitmap
bba6f3bd
UA
741 int width = GetWidth();
742 int bmpHeight = GetHeight();
c7abc967 743
dbda9e86 744 // calc the number of bytes per scanline and padding
bba6f3bd
UA
745 int bytePerLine = width*3;
746 int sizeDWORD = sizeof( DWORD );
bae41ce1 747 int lineBoundary = bytePerLine % sizeDWORD;
bba6f3bd 748 int padding = 0;
bae41ce1 749 if( lineBoundary > 0 )
bba6f3bd 750 {
bae41ce1 751 padding = sizeDWORD - lineBoundary;
bba6f3bd
UA
752 bytePerLine += padding;
753 }
dbda9e86 754 // calc the number of DIBs and heights of DIBs
bba6f3bd
UA
755 int numDIB = 1;
756 int hRemain = 0;
757 int height = sizeLimit/bytePerLine;
758 if( height >= bmpHeight )
c7abc967 759 height = bmpHeight;
bba6f3bd
UA
760 else
761 {
bae41ce1
UA
762 numDIB = bmpHeight / height;
763 hRemain = bmpHeight % height;
dbda9e86 764 if( hRemain >0 ) numDIB++;
bba6f3bd 765 }
c7abc967 766
dbda9e86 767 // set bitmap parameters
bba6f3bd 768 wxBitmap bitmap;
223d09f6 769 wxCHECK_MSG( Ok(), bitmap, wxT("invalid image") );
bba6f3bd
UA
770 bitmap.SetWidth( width );
771 bitmap.SetHeight( bmpHeight );
772 bitmap.SetDepth( wxDisplayDepth() );
c7abc967 773
dbda9e86 774 // create a DIB header
bba6f3bd 775 int headersize = sizeof(BITMAPINFOHEADER);
8f177c8e 776 BITMAPINFO *lpDIBh = (BITMAPINFO *) malloc( headersize );
223d09f6 777 wxCHECK_MSG( lpDIBh, bitmap, wxT("could not allocate memory for DIB header") );
dbda9e86 778 // Fill in the DIB header
bba6f3bd
UA
779 lpDIBh->bmiHeader.biSize = headersize;
780 lpDIBh->bmiHeader.biWidth = (DWORD)width;
781 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
782 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
dbda9e86
JS
783 // the general formula for biSizeImage:
784 // ( ( ( ((DWORD)width*24) +31 ) & ~31 ) >> 3 ) * height;
bba6f3bd
UA
785 lpDIBh->bmiHeader.biPlanes = 1;
786 lpDIBh->bmiHeader.biBitCount = 24;
787 lpDIBh->bmiHeader.biCompression = BI_RGB;
788 lpDIBh->bmiHeader.biClrUsed = 0;
dbda9e86 789 // These seem not really needed for our purpose here.
bba6f3bd
UA
790 lpDIBh->bmiHeader.biClrImportant = 0;
791 lpDIBh->bmiHeader.biXPelsPerMeter = 0;
792 lpDIBh->bmiHeader.biYPelsPerMeter = 0;
dbda9e86 793 // memory for DIB data
bba6f3bd
UA
794 unsigned char *lpBits;
795 lpBits = (unsigned char *)malloc( lpDIBh->bmiHeader.biSizeImage );
796 if( !lpBits )
797 {
223d09f6 798 wxFAIL_MSG( wxT("could not allocate memory for DIB") );
bba6f3bd
UA
799 free( lpDIBh );
800 return bitmap;
801 }
c7abc967 802
dbda9e86 803 // create and set the device-dependent bitmap
bba6f3bd
UA
804 HDC hdc = ::GetDC(NULL);
805 HDC memdc = ::CreateCompatibleDC( hdc );
806 HBITMAP hbitmap;
807 hbitmap = ::CreateCompatibleBitmap( hdc, width, bmpHeight );
c7abc967
VZ
808 ::SelectObject( memdc, hbitmap);
809
dbda9e86 810 // copy image data into DIB data and then into DDB (in a loop)
bba6f3bd
UA
811 unsigned char *data = GetData();
812 int i, j, n;
813 int origin = 0;
814 unsigned char *ptdata = data;
815 unsigned char *ptbits;
c7abc967 816
bba6f3bd
UA
817 for( n=0; n<numDIB; n++ )
818 {
dbda9e86
JS
819 if( numDIB > 1 && n == numDIB-1 && hRemain > 0 )
820 {
821 // redefine height and size of the (possibly) last smaller DIB
822 // memory is not reallocated
c7abc967 823 height = hRemain;
bba6f3bd
UA
824 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
825 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
dbda9e86 826 }
bba6f3bd 827 ptbits = lpBits;
c7abc967 828
bba6f3bd
UA
829 for( j=0; j<height; j++ )
830 {
831 for( i=0; i<width; i++ )
832 {
833 *(ptbits++) = *(ptdata+2);
834 *(ptbits++) = *(ptdata+1);
835 *(ptbits++) = *(ptdata );
836 ptdata += 3;
dbda9e86
JS
837 }
838 for( i=0; i< padding; i++ ) *(ptbits++) = 0;
bba6f3bd
UA
839 }
840 ::StretchDIBits( memdc, 0, origin, width, height,\
dbda9e86
JS
841 0, 0, width, height, lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
842 origin += height;
843 // if numDIB = 1, lines below can also be used
844 // hbitmap = CreateDIBitmap( hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits, lpDIBh, DIB_RGB_COLORS );
845 // The above line is equivalent to the following two lines.
846 // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
847 // ::SetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS);
848 // or the following lines
849 // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
850 // HDC memdc = ::CreateCompatibleDC( hdc );
c7abc967 851 // ::SelectObject( memdc, hbitmap);
dbda9e86 852 // ::SetDIBitsToDevice( memdc, 0, 0, width, height,
995612e2 853 // 0, 0, 0, height, (void *)lpBits, lpDIBh, DIB_RGB_COLORS);
c7abc967
VZ
854 // ::SelectObject( memdc, 0 );
855 // ::DeleteDC( memdc );
e3554471 856 }
bba6f3bd 857 bitmap.SetHBITMAP( (WXHBITMAP) hbitmap );
c7abc967 858
dbda9e86 859 // similarly, created an mono-bitmap for the possible mask
bba6f3bd
UA
860 if( HasMask() )
861 {
862 hbitmap = ::CreateBitmap( (WORD)width, (WORD)bmpHeight, 1, 1, NULL );
0bafad0c 863 HGDIOBJ hbmpOld = ::SelectObject( memdc, hbitmap);
c7abc967 864 if( numDIB == 1 ) height = bmpHeight;
bba6f3bd
UA
865 else height = sizeLimit/bytePerLine;
866 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
867 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
868 origin = 0;
869 unsigned char r = GetMaskRed();
870 unsigned char g = GetMaskGreen();
871 unsigned char b = GetMaskBlue();
872 unsigned char zero = 0, one = 255;
873 ptdata = data;
874 for( n=0; n<numDIB; n++ )
875 {
876 if( numDIB > 1 && n == numDIB - 1 && hRemain > 0 )
877 {
dbda9e86
JS
878 // redefine height and size of the (possibly) last smaller DIB
879 // memory is not reallocated
c7abc967 880 height = hRemain;
bba6f3bd
UA
881 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
882 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
dbda9e86 883 }
bba6f3bd
UA
884 ptbits = lpBits;
885 for( int j=0; j<height; j++ )
886 {
dbda9e86 887 for(i=0; i<width; i++ )
bba6f3bd 888 {
8208e181 889 // was causing a code gen bug in cw : if( ( cr !=r) || (cg!=g) || (cb!=b) )
ad30de59
GRG
890 unsigned char cr = (*(ptdata++)) ;
891 unsigned char cg = (*(ptdata++)) ;
892 unsigned char cb = (*(ptdata++)) ;
06b466c7 893
8208e181 894 if( ( cr !=r) || (cg!=g) || (cb!=b) )
bba6f3bd
UA
895 {
896 *(ptbits++) = one;
897 *(ptbits++) = one;
898 *(ptbits++) = one;
899 }
900 else
901 {
902 *(ptbits++) = zero;
903 *(ptbits++) = zero;
904 *(ptbits++) = zero;
905 }
906 }
dbda9e86 907 for( i=0; i< padding; i++ ) *(ptbits++) = zero;
bba6f3bd
UA
908 }
909 ::StretchDIBits( memdc, 0, origin, width, height,\
dbda9e86
JS
910 0, 0, width, height, lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
911 origin += height;
912 }
913 // create a wxMask object
bba6f3bd
UA
914 wxMask *mask = new wxMask();
915 mask->SetMaskBitmap( (WXHBITMAP) hbitmap );
916 bitmap.SetMask( mask );
dbda9e86
JS
917 // It will be deleted when the wxBitmap object is deleted (as of 01/1999)
918 /* The following can also be used but is slow to run
bba6f3bd
UA
919 wxColour colour( GetMaskRed(), GetMaskGreen(), GetMaskBlue());
920 wxMask *mask = new wxMask( bitmap, colour );
921 bitmap.SetMask( mask );
dbda9e86 922 */
0bafad0c
VZ
923
924 ::SelectObject( memdc, hbmpOld );
bba6f3bd 925 }
c7abc967
VZ
926
927 // free allocated resources
c7abc967
VZ
928 ::DeleteDC( memdc );
929 ::ReleaseDC(NULL, hdc);
bba6f3bd
UA
930 free(lpDIBh);
931 free(lpBits);
c7abc967 932
6d167489 933#if WXWIN_COMPATIBILITY_2
dbda9e86 934 // check the wxBitmap object
6d167489
VZ
935 bitmap.GetBitmapData()->SetOk();
936#endif // WXWIN_COMPATIBILITY_2
c7abc967 937
bba6f3bd 938 return bitmap;
e3554471
JS
939}
940
e3554471
JS
941wxImage::wxImage( const wxBitmap &bitmap )
942{
dbda9e86 943 // check the bitmap
bba6f3bd
UA
944 if( !bitmap.Ok() )
945 {
223d09f6 946 wxFAIL_MSG( wxT("invalid bitmap") );
bba6f3bd
UA
947 return;
948 }
c7abc967 949
dbda9e86 950 // create an wxImage object
bba6f3bd
UA
951 int width = bitmap.GetWidth();
952 int height = bitmap.GetHeight();
c7abc967 953 Create( width, height );
bba6f3bd
UA
954 unsigned char *data = GetData();
955 if( !data )
956 {
223d09f6 957 wxFAIL_MSG( wxT("could not allocate data for image") );
bba6f3bd
UA
958 return;
959 }
c7abc967 960
dbda9e86 961 // calc the number of bytes per scanline and padding in the DIB
bba6f3bd
UA
962 int bytePerLine = width*3;
963 int sizeDWORD = sizeof( DWORD );
bae41ce1 964 int lineBoundary = bytePerLine % sizeDWORD;
bba6f3bd 965 int padding = 0;
bae41ce1 966 if( lineBoundary > 0 )
bba6f3bd 967 {
bae41ce1 968 padding = sizeDWORD - lineBoundary;
bba6f3bd
UA
969 bytePerLine += padding;
970 }
c7abc967 971
dbda9e86 972 // create a DIB header
bba6f3bd 973 int headersize = sizeof(BITMAPINFOHEADER);
8f177c8e 974 BITMAPINFO *lpDIBh = (BITMAPINFO *) malloc( headersize );
bba6f3bd
UA
975 if( !lpDIBh )
976 {
223d09f6 977 wxFAIL_MSG( wxT("could not allocate data for DIB header") );
bba6f3bd
UA
978 free( data );
979 return;
980 }
dbda9e86 981 // Fill in the DIB header
bba6f3bd
UA
982 lpDIBh->bmiHeader.biSize = headersize;
983 lpDIBh->bmiHeader.biWidth = width;
984 lpDIBh->bmiHeader.biHeight = -height;
985 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
986 lpDIBh->bmiHeader.biPlanes = 1;
987 lpDIBh->bmiHeader.biBitCount = 24;
988 lpDIBh->bmiHeader.biCompression = BI_RGB;
989 lpDIBh->bmiHeader.biClrUsed = 0;
dbda9e86 990 // These seem not really needed for our purpose here.
bba6f3bd
UA
991 lpDIBh->bmiHeader.biClrImportant = 0;
992 lpDIBh->bmiHeader.biXPelsPerMeter = 0;
993 lpDIBh->bmiHeader.biYPelsPerMeter = 0;
dbda9e86 994 // memory for DIB data
bba6f3bd
UA
995 unsigned char *lpBits;
996 lpBits = (unsigned char *) malloc( lpDIBh->bmiHeader.biSizeImage );
997 if( !lpBits )
e3554471 998 {
223d09f6 999 wxFAIL_MSG( wxT("could not allocate data for DIB") );
bba6f3bd
UA
1000 free( data );
1001 free( lpDIBh );
1002 return;
4698648f 1003 }
c7abc967 1004
dbda9e86 1005 // copy data from the device-dependent bitmap to the DIB
bba6f3bd
UA
1006 HDC hdc = ::GetDC(NULL);
1007 HBITMAP hbitmap;
1008 hbitmap = (HBITMAP) bitmap.GetHBITMAP();
1009 ::GetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS );
c7abc967 1010
dbda9e86 1011 // copy DIB data into the wxImage object
bba6f3bd
UA
1012 int i, j;
1013 unsigned char *ptdata = data;
1014 unsigned char *ptbits = lpBits;
1015 for( i=0; i<height; i++ )
1016 {
1017 for( j=0; j<width; j++ )
1018 {
1019 *(ptdata++) = *(ptbits+2);
1020 *(ptdata++) = *(ptbits+1);
1021 *(ptdata++) = *(ptbits );
1022 ptbits += 3;
dbda9e86 1023 }
bba6f3bd 1024 ptbits += padding;
c7abc967
VZ
1025 }
1026
dbda9e86 1027 // similarly, set data according to the possible mask bitmap
bba6f3bd
UA
1028 if( bitmap.GetMask() && bitmap.GetMask()->GetMaskBitmap() )
1029 {
1030 hbitmap = (HBITMAP) bitmap.GetMask()->GetMaskBitmap();
dbda9e86 1031 // memory DC created, color set, data copied, and memory DC deleted
bba6f3bd
UA
1032 HDC memdc = ::CreateCompatibleDC( hdc );
1033 ::SetTextColor( memdc, RGB( 0, 0, 0 ) );
1034 ::SetBkColor( memdc, RGB( 255, 255, 255 ) );
1035 ::GetDIBits( memdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS );
c7abc967 1036 ::DeleteDC( memdc );
dbda9e86 1037 // background color set to RGB(16,16,16) in consistent with wxGTK
c7abc967 1038 unsigned char r=16, g=16, b=16;
bba6f3bd
UA
1039 ptdata = data;
1040 ptbits = lpBits;
1041 for( i=0; i<height; i++ )
1042 {
1043 for( j=0; j<width; j++ )
1044 {
1045 if( *ptbits != 0 )
dbda9e86
JS
1046 ptdata += 3;
1047 else
bba6f3bd
UA
1048 {
1049 *(ptdata++) = r;
1050 *(ptdata++) = g;
1051 *(ptdata++) = b;
dbda9e86 1052 }
bba6f3bd
UA
1053 ptbits += 3;
1054 }
1055 ptbits += padding;
c7abc967 1056 }
bba6f3bd
UA
1057 SetMaskColour( r, g, b );
1058 SetMask( TRUE );
c7abc967 1059 }
bba6f3bd
UA
1060 else
1061 {
1062 SetMask( FALSE );
c7abc967
VZ
1063 }
1064 // free allocated resources
1065 ::ReleaseDC(NULL, hdc);
bba6f3bd
UA
1066 free(lpDIBh);
1067 free(lpBits);
e3554471
JS
1068}
1069
1070#endif
1071
7c74e7fe
SC
1072#ifdef __WXMAC__
1073
1074#include <PictUtils.h>
1075
1076extern CTabHandle wxMacCreateColorTable( int numColors ) ;
1077extern void wxMacDestroyColorTable( CTabHandle colors ) ;
1078extern void wxMacSetColorTableEntry( CTabHandle newColors , int index , int red , int green , int blue ) ;
1079extern GWorldPtr wxMacCreateGWorld( int height , int width , int depth ) ;
1080extern void wxMacDestroyGWorld( GWorldPtr gw ) ;
1081
1082wxBitmap wxImage::ConvertToBitmap() const
1083{
1084 // width and height of the device-dependent bitmap
1085 int width = GetWidth();
1086 int height = GetHeight();
1087
1088 // Create picture
97fdfcc9 1089
7c74e7fe 1090 wxBitmap bitmap( width , height , wxDisplayDepth() ) ;
97fdfcc9 1091
7c74e7fe 1092 // Create mask
97fdfcc9 1093
7c74e7fe
SC
1094 if (HasMask())
1095 {
069d0f27 1096 /*
7c74e7fe 1097 unsigned char *mask_data = (unsigned char*)malloc( ((width >> 3)+8) * height );
97fdfcc9 1098
7c74e7fe 1099 mask_image = gdk_image_new_bitmap( gdk_visual_get_system(), mask_data, width, height );
97fdfcc9 1100
7c74e7fe
SC
1101 wxMask *mask = new wxMask();
1102 mask->m_bitmap = gdk_pixmap_new( (GdkWindow*)&gdk_root_parent, width, height, 1 );
97fdfcc9 1103
7c74e7fe
SC
1104 bitmap.SetMask( mask );
1105 */
1106 }
97fdfcc9 1107
7c74e7fe 1108 // Render
97fdfcc9 1109
7c74e7fe
SC
1110 int r_mask = GetMaskRed();
1111 int g_mask = GetMaskGreen();
1112 int b_mask = GetMaskBlue();
97fdfcc9 1113
ad30de59
GRG
1114 CGrafPtr origPort ;
1115 GDHandle origDevice ;
06b466c7 1116
ad30de59
GRG
1117 GetGWorld( &origPort , &origDevice ) ;
1118 SetGWorld( bitmap.GetHBITMAP() , NULL ) ;
7c74e7fe
SC
1119
1120 register unsigned char* data = GetData();
97fdfcc9 1121
7c74e7fe
SC
1122 int index = 0;
1123 for (int y = 0; y < height; y++)
1124 {
7c74e7fe
SC
1125 for (int x = 0; x < width; x++)
1126 {
ad30de59
GRG
1127 unsigned char r = data[index++];
1128 unsigned char g = data[index++];
1129 unsigned char b = data[index++];
1130 RGBColor color ;
1131 color.red = ( r << 8 ) + r ;
1132 color.green = ( g << 8 ) + g ;
1133 color.blue = ( b << 8 ) + b ;
1134 SetCPixel( x , y , &color ) ;
1135 }
7c74e7fe
SC
1136 } // for height
1137
069d0f27 1138 SetGWorld( origPort , origDevice ) ;
97fdfcc9 1139
8208e181
SC
1140 if ( HasMask() )
1141 {
1142 wxColour colour( GetMaskRed(), GetMaskGreen(), GetMaskBlue());
1143 wxMask *mask = new wxMask( bitmap, colour );
1144 bitmap.SetMask( mask );
1145 }
7c74e7fe 1146 return bitmap;
97fdfcc9 1147
7c74e7fe
SC
1148}
1149
1150wxImage::wxImage( const wxBitmap &bitmap )
1151{
1152 // check the bitmap
1153 if( !bitmap.Ok() )
1154 {
1155 wxFAIL_MSG( "invalid bitmap" );
1156 return;
1157 }
97fdfcc9 1158
7c74e7fe
SC
1159 // create an wxImage object
1160 int width = bitmap.GetWidth();
1161 int height = bitmap.GetHeight();
97fdfcc9 1162 Create( width, height );
7c74e7fe
SC
1163 /*
1164 unsigned char *data = GetData();
1165 if( !data )
1166 {
1167 wxFAIL_MSG( "could not allocate data for image" );
1168 return;
1169 }
97fdfcc9 1170
7c74e7fe
SC
1171 // calc the number of bytes per scanline and padding in the DIB
1172 int bytePerLine = width*3;
1173 int sizeDWORD = sizeof( DWORD );
1174 div_t lineBoundary = div( bytePerLine, sizeDWORD );
1175 int padding = 0;
97fdfcc9 1176 if( lineBoundary.rem > 0 )
7c74e7fe
SC
1177 {
1178 padding = sizeDWORD - lineBoundary.rem;
1179 bytePerLine += padding;
1180 }
97fdfcc9 1181
7c74e7fe
SC
1182 // create a DIB header
1183 int headersize = sizeof(BITMAPINFOHEADER);
1184 LPBITMAPINFO lpDIBh = (BITMAPINFO *) malloc( headersize );
1185 if( !lpDIBh )
1186 {
1187 wxFAIL_MSG( "could not allocate data for DIB header" );
1188 free( data );
1189 return;
1190 }
1191 // Fill in the DIB header
1192 lpDIBh->bmiHeader.biSize = headersize;
1193 lpDIBh->bmiHeader.biWidth = width;
1194 lpDIBh->bmiHeader.biHeight = -height;
1195 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
1196 lpDIBh->bmiHeader.biPlanes = 1;
1197 lpDIBh->bmiHeader.biBitCount = 24;
1198 lpDIBh->bmiHeader.biCompression = BI_RGB;
1199 lpDIBh->bmiHeader.biClrUsed = 0;
1200 // These seem not really needed for our purpose here.
1201 lpDIBh->bmiHeader.biClrImportant = 0;
1202 lpDIBh->bmiHeader.biXPelsPerMeter = 0;
1203 lpDIBh->bmiHeader.biYPelsPerMeter = 0;
1204 // memory for DIB data
1205 unsigned char *lpBits;
1206 lpBits = (unsigned char *) malloc( lpDIBh->bmiHeader.biSizeImage );
1207 if( !lpBits )
1208 {
1209 wxFAIL_MSG( "could not allocate data for DIB" );
1210 free( data );
1211 free( lpDIBh );
1212 return;
1213 }
97fdfcc9 1214
7c74e7fe
SC
1215 // copy data from the device-dependent bitmap to the DIB
1216 HDC hdc = ::GetDC(NULL);
1217 HBITMAP hbitmap;
1218 hbitmap = (HBITMAP) bitmap.GetHBITMAP();
1219 ::GetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS );
97fdfcc9 1220
7c74e7fe
SC
1221 // copy DIB data into the wxImage object
1222 int i, j;
1223 unsigned char *ptdata = data;
1224 unsigned char *ptbits = lpBits;
1225 for( i=0; i<height; i++ )
1226 {
1227 for( j=0; j<width; j++ )
1228 {
1229 *(ptdata++) = *(ptbits+2);
1230 *(ptdata++) = *(ptbits+1);
1231 *(ptdata++) = *(ptbits );
1232 ptbits += 3;
1233 }
1234 ptbits += padding;
06b466c7 1235 }
97fdfcc9 1236
7c74e7fe
SC
1237 // similarly, set data according to the possible mask bitmap
1238 if( bitmap.GetMask() && bitmap.GetMask()->GetMaskBitmap() )
1239 {
1240 hbitmap = (HBITMAP) bitmap.GetMask()->GetMaskBitmap();
1241 // memory DC created, color set, data copied, and memory DC deleted
1242 HDC memdc = ::CreateCompatibleDC( hdc );
1243 ::SetTextColor( memdc, RGB( 0, 0, 0 ) );
1244 ::SetBkColor( memdc, RGB( 255, 255, 255 ) );
1245 ::GetDIBits( memdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS );
97fdfcc9 1246 ::DeleteDC( memdc );
7c74e7fe 1247 // background color set to RGB(16,16,16) in consistent with wxGTK
97fdfcc9 1248 unsigned char r=16, g=16, b=16;
7c74e7fe
SC
1249 ptdata = data;
1250 ptbits = lpBits;
1251 for( i=0; i<height; i++ )
1252 {
1253 for( j=0; j<width; j++ )
1254 {
1255 if( *ptbits != 0 )
1256 ptdata += 3;
1257 else
1258 {
1259 *(ptdata++) = r;
1260 *(ptdata++) = g;
1261 *(ptdata++) = b;
1262 }
1263 ptbits += 3;
1264 }
1265 ptbits += padding;
97fdfcc9 1266 }
7c74e7fe
SC
1267 SetMaskColour( r, g, b );
1268 SetMask( TRUE );
8f177c8e 1269 }
7c74e7fe
SC
1270 else
1271 {
1272 SetMask( FALSE );
8f177c8e 1273 }
97fdfcc9
DW
1274 // free allocated resources
1275 ::ReleaseDC(NULL, hdc);
7c74e7fe
SC
1276 free(lpDIBh);
1277 free(lpBits);
1278 */
1279}
1280
1281#endif
1282
ce4169a4
RR
1283//-----------------------------------------------------------------------------
1284// GTK conversion routines
1285//-----------------------------------------------------------------------------
1286
99c67c77
RR
1287#ifdef __WXGTK__
1288
20e05ffb
RR
1289#include <gtk/gtk.h>
1290#include <gdk/gdk.h>
1291#include <gdk/gdkx.h>
83624f79 1292
ba0730de 1293#if (GTK_MINOR_VERSION > 0)
20e05ffb 1294#include <gdk/gdkrgb.h>
ba0730de
RR
1295#endif
1296
d76fe38b
RR
1297extern GtkWidget *wxRootWindow;
1298
82ea63e6
RR
1299wxBitmap wxImage::ConvertToMonoBitmap( unsigned char red, unsigned char green, unsigned char blue )
1300{
1301 wxBitmap bitmap;
1302
1303 wxCHECK_MSG( Ok(), bitmap, wxT("invalid image") );
1304
1305 int width = GetWidth();
1306 int height = GetHeight();
1307
1308 bitmap.SetHeight( height );
1309 bitmap.SetWidth( width );
1310
d76fe38b 1311 bitmap.SetBitmap( gdk_pixmap_new( wxRootWindow->window, width, height, 1 ) );
06b466c7 1312
82ea63e6
RR
1313 bitmap.SetDepth( 1 );
1314
103aab26
RR
1315 GdkVisual *visual = gdk_window_get_visual( wxRootWindow->window );
1316 wxASSERT( visual );
06b466c7 1317
82ea63e6
RR
1318 // Create picture image
1319
1320 unsigned char *data_data = (unsigned char*)malloc( ((width >> 3)+8) * height );
06b466c7 1321
82ea63e6 1322 GdkImage *data_image =
103aab26 1323 gdk_image_new_bitmap( visual, data_data, width, height );
82ea63e6
RR
1324
1325 // Create mask image
1326
1327 GdkImage *mask_image = (GdkImage*) NULL;
1328
1329 if (HasMask())
1330 {
1331 unsigned char *mask_data = (unsigned char*)malloc( ((width >> 3)+8) * height );
1332
103aab26 1333 mask_image = gdk_image_new_bitmap( visual, mask_data, width, height );
82ea63e6
RR
1334
1335 wxMask *mask = new wxMask();
d76fe38b 1336 mask->m_bitmap = gdk_pixmap_new( wxRootWindow->window, width, height, 1 );
82ea63e6
RR
1337
1338 bitmap.SetMask( mask );
1339 }
1340
1341 int r_mask = GetMaskRed();
1342 int g_mask = GetMaskGreen();
1343 int b_mask = GetMaskBlue();
1344
1345 unsigned char* data = GetData();
1346
1347 int index = 0;
1348 for (int y = 0; y < height; y++)
1349 {
1350 for (int x = 0; x < width; x++)
1351 {
1352 int r = data[index];
1353 index++;
1354 int g = data[index];
1355 index++;
1356 int b = data[index];
1357 index++;
1358
1359 if (HasMask())
1360 {
1361 if ((r == r_mask) && (b == b_mask) && (g == g_mask))
1362 gdk_image_put_pixel( mask_image, x, y, 1 );
1363 else
1364 gdk_image_put_pixel( mask_image, x, y, 0 );
1365 }
06b466c7 1366
82ea63e6
RR
1367 if ((r == red) && (b == blue) && (g == green))
1368 gdk_image_put_pixel( data_image, x, y, 1 );
06b466c7 1369 else
82ea63e6
RR
1370 gdk_image_put_pixel( data_image, x, y, 0 );
1371
1372 } // for
1373 } // for
1374
1375 // Blit picture
1376
1377 GdkGC *data_gc = gdk_gc_new( bitmap.GetBitmap() );
1378
1379 gdk_draw_image( bitmap.GetBitmap(), data_gc, data_image, 0, 0, 0, 0, width, height );
1380
1381 gdk_image_destroy( data_image );
1382 gdk_gc_unref( data_gc );
1383
1384 // Blit mask
1385
1386 if (HasMask())
1387 {
1388 GdkGC *mask_gc = gdk_gc_new( bitmap.GetMask()->GetBitmap() );
1389
1390 gdk_draw_image( bitmap.GetMask()->GetBitmap(), mask_gc, mask_image, 0, 0, 0, 0, width, height );
1391
1392 gdk_image_destroy( mask_image );
1393 gdk_gc_unref( mask_gc );
1394 }
1395
1396 return bitmap;
1397}
1398
1399
99c67c77
RR
1400wxBitmap wxImage::ConvertToBitmap() const
1401{
1402 wxBitmap bitmap;
c7abc967 1403
223d09f6 1404 wxCHECK_MSG( Ok(), bitmap, wxT("invalid image") );
c7abc967 1405
99c67c77
RR
1406 int width = GetWidth();
1407 int height = GetHeight();
c7abc967 1408
99c67c77
RR
1409 bitmap.SetHeight( height );
1410 bitmap.SetWidth( width );
c7abc967 1411
d76fe38b 1412 bitmap.SetPixmap( gdk_pixmap_new( wxRootWindow->window, width, height, -1 ) );
ba0730de 1413
103aab26 1414 // Retrieve depth
c7abc967 1415
103aab26
RR
1416 GdkVisual *visual = gdk_window_get_visual( wxRootWindow->window );
1417 wxASSERT( visual );
06b466c7 1418
ba0730de 1419 int bpp = visual->depth;
c7abc967 1420
ba0730de 1421 bitmap.SetDepth( bpp );
c7abc967 1422
ba0730de
RR
1423 if ((bpp == 16) && (visual->red_mask != 0xf800)) bpp = 15;
1424 if (bpp < 8) bpp = 8;
c7abc967 1425
ba0730de
RR
1426#if (GTK_MINOR_VERSION > 0)
1427
1428 if (!HasMask() && (bpp > 8))
1429 {
1430 static bool s_hasInitialized = FALSE;
c7abc967 1431
995612e2
VZ
1432 if (!s_hasInitialized)
1433 {
1434 gdk_rgb_init();
1435 s_hasInitialized = TRUE;
1436 }
c7abc967 1437
ba0730de 1438 GdkGC *gc = gdk_gc_new( bitmap.GetPixmap() );
c7abc967 1439
995612e2
VZ
1440 gdk_draw_rgb_image( bitmap.GetPixmap(),
1441 gc,
1442 0, 0,
1443 width, height,
1444 GDK_RGB_DITHER_NONE,
1445 GetData(),
1446 width*3 );
c7abc967 1447
ba0730de 1448 gdk_gc_unref( gc );
c7abc967 1449
995612e2 1450 return bitmap;
ba0730de 1451 }
c7abc967 1452
ba0730de 1453#endif
c7abc967 1454
ba0730de 1455 // Create picture image
c7abc967 1456
99c67c77 1457 GdkImage *data_image =
103aab26 1458 gdk_image_new( GDK_IMAGE_FASTEST, visual, width, height );
c7abc967 1459
ba0730de 1460 // Create mask image
c7abc967 1461
99c67c77 1462 GdkImage *mask_image = (GdkImage*) NULL;
c7abc967 1463
99c67c77
RR
1464 if (HasMask())
1465 {
1466 unsigned char *mask_data = (unsigned char*)malloc( ((width >> 3)+8) * height );
c7abc967 1467
103aab26 1468 mask_image = gdk_image_new_bitmap( visual, mask_data, width, height );
c7abc967 1469
4698648f 1470 wxMask *mask = new wxMask();
d76fe38b 1471 mask->m_bitmap = gdk_pixmap_new( wxRootWindow->window, width, height, 1 );
c7abc967 1472
4698648f 1473 bitmap.SetMask( mask );
99c67c77 1474 }
c7abc967 1475
99c67c77 1476 // Render
c7abc967 1477
99c67c77
RR
1478 enum byte_order { RGB, RBG, BRG, BGR, GRB, GBR };
1479 byte_order b_o = RGB;
c7abc967 1480
99c67c77
RR
1481 if (bpp >= 24)
1482 {
99c67c77
RR
1483 if ((visual->red_mask > visual->green_mask) && (visual->green_mask > visual->blue_mask)) b_o = RGB;
1484 else if ((visual->red_mask > visual->blue_mask) && (visual->blue_mask > visual->green_mask)) b_o = RGB;
1485 else if ((visual->blue_mask > visual->red_mask) && (visual->red_mask > visual->green_mask)) b_o = BRG;
1486 else if ((visual->blue_mask > visual->green_mask) && (visual->green_mask > visual->red_mask)) b_o = BGR;
1487 else if ((visual->green_mask > visual->red_mask) && (visual->red_mask > visual->blue_mask)) b_o = GRB;
1488 else if ((visual->green_mask > visual->blue_mask) && (visual->blue_mask > visual->red_mask)) b_o = GBR;
1489 }
c7abc967 1490
99c67c77
RR
1491 int r_mask = GetMaskRed();
1492 int g_mask = GetMaskGreen();
1493 int b_mask = GetMaskBlue();
c7abc967 1494
99c67c77 1495 unsigned char* data = GetData();
c7abc967 1496
99c67c77
RR
1497 int index = 0;
1498 for (int y = 0; y < height; y++)
1499 {
1500 for (int x = 0; x < width; x++)
1501 {
1502 int r = data[index];
4698648f 1503 index++;
99c67c77 1504 int g = data[index];
4698648f 1505 index++;
99c67c77 1506 int b = data[index];
4698648f 1507 index++;
c7abc967 1508
4698648f
VZ
1509 if (HasMask())
1510 {
1511 if ((r == r_mask) && (b == b_mask) && (g == g_mask))
1512 gdk_image_put_pixel( mask_image, x, y, 1 );
1513 else
1514 gdk_image_put_pixel( mask_image, x, y, 0 );
1515 }
c7abc967 1516
4698648f
VZ
1517 switch (bpp)
1518 {
dbda9e86 1519 case 8:
4698648f 1520 {
f6fcbb63 1521 int pixel = -1;
4698648f
VZ
1522 if (wxTheApp->m_colorCube)
1523 {
38274997 1524 pixel = wxTheApp->m_colorCube[ ((r & 0xf8) << 7) + ((g & 0xf8) << 2) + ((b & 0xf8) >> 3) ];
4698648f 1525 }
f6fcbb63 1526 else
4698648f
VZ
1527 {
1528 GdkColormap *cmap = gtk_widget_get_default_colormap();
f6fcbb63
RR
1529 GdkColor *colors = cmap->colors;
1530 int max = 3 * (65536);
c7abc967 1531
f6fcbb63
RR
1532 for (int i = 0; i < cmap->size; i++)
1533 {
1534 int rdiff = (r << 8) - colors[i].red;
1535 int gdiff = (g << 8) - colors[i].green;
1536 int bdiff = (b << 8) - colors[i].blue;
1537 int sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
1538 if (sum < max) { pixel = i; max = sum; }
4698648f 1539 }
99c67c77 1540 }
c7abc967 1541
4698648f 1542 gdk_image_put_pixel( data_image, x, y, pixel );
c7abc967 1543
4698648f
VZ
1544 break;
1545 }
dbda9e86 1546 case 15:
4698648f
VZ
1547 {
1548 guint32 pixel = ((r & 0xf8) << 7) | ((g & 0xf8) << 2) | ((b & 0xf8) >> 3);
1549 gdk_image_put_pixel( data_image, x, y, pixel );
1550 break;
1551 }
dbda9e86 1552 case 16:
4698648f
VZ
1553 {
1554 guint32 pixel = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | ((b & 0xf8) >> 3);
1555 gdk_image_put_pixel( data_image, x, y, pixel );
1556 break;
1557 }
dbda9e86
JS
1558 case 32:
1559 case 24:
4698648f
VZ
1560 {
1561 guint32 pixel = 0;
1562 switch (b_o)
1563 {
dbda9e86
JS
1564 case RGB: pixel = (r << 16) | (g << 8) | b; break;
1565 case RBG: pixel = (r << 16) | (b << 8) | g; break;
1566 case BRG: pixel = (b << 16) | (r << 8) | g; break;
1567 case BGR: pixel = (b << 16) | (g << 8) | r; break;
1568 case GRB: pixel = (g << 16) | (r << 8) | b; break;
1569 case GBR: pixel = (g << 16) | (b << 8) | r; break;
4698648f
VZ
1570 }
1571 gdk_image_put_pixel( data_image, x, y, pixel );
1572 }
dbda9e86 1573 default: break;
4698648f 1574 }
99c67c77
RR
1575 } // for
1576 } // for
c7abc967 1577
99c67c77 1578 // Blit picture
c7abc967 1579
99c67c77 1580 GdkGC *data_gc = gdk_gc_new( bitmap.GetPixmap() );
c7abc967 1581
99c67c77 1582 gdk_draw_image( bitmap.GetPixmap(), data_gc, data_image, 0, 0, 0, 0, width, height );
c7abc967 1583
99c67c77
RR
1584 gdk_image_destroy( data_image );
1585 gdk_gc_unref( data_gc );
c7abc967 1586
99c67c77 1587 // Blit mask
c7abc967 1588
99c67c77
RR
1589 if (HasMask())
1590 {
1591 GdkGC *mask_gc = gdk_gc_new( bitmap.GetMask()->GetBitmap() );
c7abc967 1592
99c67c77 1593 gdk_draw_image( bitmap.GetMask()->GetBitmap(), mask_gc, mask_image, 0, 0, 0, 0, width, height );
c7abc967 1594
99c67c77
RR
1595 gdk_image_destroy( mask_image );
1596 gdk_gc_unref( mask_gc );
1597 }
c7abc967 1598
99c67c77
RR
1599 return bitmap;
1600}
1601
1602wxImage::wxImage( const wxBitmap &bitmap )
1603{
223d09f6 1604 wxCHECK_RET( bitmap.Ok(), wxT("invalid bitmap") );
c7abc967 1605
c6d73ef6
RR
1606 GdkImage *gdk_image = (GdkImage*) NULL;
1607 if (bitmap.GetPixmap())
1608 {
1609 gdk_image = gdk_image_get( bitmap.GetPixmap(),
1610 0, 0,
1611 bitmap.GetWidth(), bitmap.GetHeight() );
1612 } else
1613 if (bitmap.GetBitmap())
1614 {
1615 gdk_image = gdk_image_get( bitmap.GetBitmap(),
1616 0, 0,
1617 bitmap.GetWidth(), bitmap.GetHeight() );
1618 } else
1619 {
1620 wxFAIL_MSG( wxT("Ill-formed bitmap") );
1621 }
c7abc967 1622
223d09f6 1623 wxCHECK_RET( gdk_image, wxT("couldn't create image") );
c7abc967 1624
99c67c77
RR
1625 Create( bitmap.GetWidth(), bitmap.GetHeight() );
1626 char unsigned *data = GetData();
c7abc967 1627
99c67c77
RR
1628 if (!data)
1629 {
1630 gdk_image_destroy( gdk_image );
223d09f6 1631 wxFAIL_MSG( wxT("couldn't create image") );
4698648f 1632 return;
99c67c77 1633 }
c7abc967 1634
99c67c77
RR
1635 GdkImage *gdk_image_mask = (GdkImage*) NULL;
1636 if (bitmap.GetMask())
1637 {
1638 gdk_image_mask = gdk_image_get( bitmap.GetMask()->GetBitmap(),
dbda9e86
JS
1639 0, 0,
1640 bitmap.GetWidth(), bitmap.GetHeight() );
c7abc967 1641
4698648f 1642 SetMaskColour( 16, 16, 16 ); // anything unlikely and dividable
99c67c77 1643 }
c7abc967 1644
be25e480 1645 int bpp = -1;
cf3da716
RR
1646 int red_shift_right = 0;
1647 int green_shift_right = 0;
1648 int blue_shift_right = 0;
1649 int red_shift_left = 0;
1650 int green_shift_left = 0;
1651 int blue_shift_left = 0;
1652 bool use_shift = FALSE;
06b466c7 1653
c6d73ef6 1654 if (bitmap.GetPixmap())
be25e480
RR
1655 {
1656 GdkVisual *visual = gdk_window_get_visual( bitmap.GetPixmap() );
c6d73ef6 1657
d76fe38b 1658 if (visual == NULL) visual = gdk_window_get_visual( wxRootWindow->window );
be25e480 1659 bpp = visual->depth;
cf3da716
RR
1660 if (bpp == 16) bpp = visual->red_prec + visual->green_prec + visual->blue_prec;
1661 red_shift_right = visual->red_shift;
1662 red_shift_left = 8-visual->red_prec;
1663 green_shift_right = visual->green_shift;
1664 green_shift_left = 8-visual->green_prec;
1665 blue_shift_right = visual->blue_shift;
1666 blue_shift_left = 8-visual->blue_prec;
06b466c7 1667
cf3da716 1668 use_shift = (visual->type == GDK_VISUAL_TRUE_COLOR) || (visual->type == GDK_VISUAL_DIRECT_COLOR);
be25e480
RR
1669 }
1670 if (bitmap.GetBitmap())
1671 {
1672 bpp = 1;
1673 }
c7abc967 1674
06b466c7 1675
99c67c77 1676 GdkColormap *cmap = gtk_widget_get_default_colormap();
c7abc967 1677
99c67c77
RR
1678 long pos = 0;
1679 for (int j = 0; j < bitmap.GetHeight(); j++)
1680 {
1681 for (int i = 0; i < bitmap.GetWidth(); i++)
1682 {
cf3da716 1683 wxUint32 pixel = gdk_image_get_pixel( gdk_image, i, j );
069d0f27
VZ
1684 if (bpp == 1)
1685 {
1686 if (pixel == 0)
1687 {
cf3da716 1688 data[pos] = 0;
be25e480
RR
1689 data[pos+1] = 0;
1690 data[pos+2] = 0;
069d0f27
VZ
1691 }
1692 else
1693 {
cf3da716 1694 data[pos] = 255;
be25e480
RR
1695 data[pos+1] = 255;
1696 data[pos+2] = 255;
069d0f27 1697 }
cf3da716
RR
1698 }
1699 else if (use_shift)
99c67c77 1700 {
cf3da716
RR
1701 data[pos] = (pixel >> red_shift_right) << red_shift_left;
1702 data[pos+1] = (pixel >> green_shift_right) << green_shift_left;
1703 data[pos+2] = (pixel >> blue_shift_right) << blue_shift_left;
06b466c7 1704 }
cf3da716 1705 else if (cmap->colors)
99c67c77 1706 {
cf3da716
RR
1707 data[pos] = cmap->colors[pixel].red >> 8;
1708 data[pos+1] = cmap->colors[pixel].green >> 8;
1709 data[pos+2] = cmap->colors[pixel].blue >> 8;
06b466c7 1710 }
cf3da716 1711 else
99c67c77 1712 {
cf3da716 1713 wxFAIL_MSG( wxT("Image conversion failed. Unknown visual type.") );
06b466c7 1714 }
c7abc967 1715
4698648f
VZ
1716 if (gdk_image_mask)
1717 {
1718 int mask_pixel = gdk_image_get_pixel( gdk_image_mask, i, j );
1719 if (mask_pixel == 0)
1720 {
99c67c77
RR
1721 data[pos] = 16;
1722 data[pos+1] = 16;
1723 data[pos+2] = 16;
dbda9e86 1724 }
4698648f 1725 }
c7abc967 1726
99c67c77
RR
1727 pos += 3;
1728 }
1729 }
c7abc967 1730
99c67c77
RR
1731 gdk_image_destroy( gdk_image );
1732 if (gdk_image_mask) gdk_image_destroy( gdk_image_mask );
1733}
1734
1735#endif
ee4c6942 1736
ce4169a4
RR
1737//-----------------------------------------------------------------------------
1738// Motif conversion routines
1739//-----------------------------------------------------------------------------
1740
ee4c6942 1741#ifdef __WXMOTIF__
338dd992
JJ
1742#ifdef __VMS__
1743#pragma message disable nosimpint
1744#endif
b75867a6 1745#include <Xm/Xm.h>
338dd992
JJ
1746#ifdef __VMS__
1747#pragma message enable nosimpint
1748#endif
b75867a6 1749#include "wx/utils.h"
38274997 1750#include <math.h>
b75867a6 1751
88195b2b
JS
1752/*
1753
1754Date: Wed, 05 Jan 2000 11:45:40 +0100
1755From: Frits Boel <boel@niob.knaw.nl>
1756To: julian.smart@ukonline.co.uk
1757Subject: Patch for Motif ConvertToBitmap
1758
1759Hi Julian,
1760
1761I've been working on a wxWin application for image processing. From the
1762beginning, I was surprised by the (lack of) speed of ConvertToBitmap,
1763till I looked in the source code of image.cpp. I saw that converting a
1764wxImage to a bitmap with 8-bit pixels is done with comparing every pixel
1765to the 256 colors of the palet. A very time-consuming piece of code!
1766
1767Because I wanted a faster application, I've made a 'patch' for this. In
1768short: every pixel of the image is compared to a sorted list with
1769colors. If the color is found in the list, the palette entry is
1770returned; if the color is not found, the color palette is searched and
1771then the palette entry is returned and the color added to the sorted
1772list.
1773
1774Maybe there is another method for this, namely changing the palette
1775itself (if the colors are known, as is the case with tiffs with a
1776colormap). I did not look at this, maybe someone else did?
1777
1778The code of the patch is attached, have a look on it, and maybe you will
1779ship it with the next release of wxMotif?
1780
1781Regards,
1782
1783Frits Boel
1784Software engineer at Hubrecht Laboratory, The Netherlands.
1785
1786*/
1787
1788class wxSearchColor
1789{
1790public:
1791 wxSearchColor( void );
1792 wxSearchColor( int size, XColor *colors );
1793 ~wxSearchColor( void );
1794
1795 int SearchColor( int r, int g, int b );
1796private:
1797 int AddColor( unsigned int value, int pos );
1798
1799 int size;
1800 XColor *colors;
1801 unsigned int *color;
1802 int *entry;
1803
1804 int bottom;
1805 int top;
1806};
1807
1808wxSearchColor::wxSearchColor( void )
1809{
069d0f27
VZ
1810 size = 0;
1811 colors = (XColor*) NULL;
1812 color = (unsigned int *) NULL;
1813 entry = (int*) NULL;
88195b2b 1814
069d0f27
VZ
1815 bottom = 0;
1816 top = 0;
88195b2b
JS
1817}
1818
069d0f27 1819wxSearchColor::wxSearchColor( int size_, XColor *colors_ )
88195b2b
JS
1820{
1821 int i;
069d0f27
VZ
1822 size = size_;
1823 colors = colors_;
1824 color = new unsigned int[size];
1825 entry = new int [size];
88195b2b 1826
069d0f27
VZ
1827 for (i = 0; i < size; i++ ) {
1828 entry[i] = -1;
1829 }
88195b2b 1830
069d0f27 1831 bottom = top = ( size >> 1 );
88195b2b
JS
1832}
1833
1834wxSearchColor::~wxSearchColor( void )
1835{
069d0f27
VZ
1836 if ( color ) delete color;
1837 if ( entry ) delete entry;
88195b2b
JS
1838}
1839
1840int wxSearchColor::SearchColor( int r, int g, int b )
1841{
1842 unsigned int value = ( ( ( r * 256 ) + g ) * 256 ) + b;
069d0f27
VZ
1843 int begin = bottom;
1844 int end = top;
88195b2b
JS
1845 int middle;
1846
1847 while ( begin <= end ) {
1848
1849 middle = ( begin + end ) >> 1;
1850
069d0f27
VZ
1851 if ( value == color[middle] ) {
1852 return( entry[middle] );
1853 } else if ( value < color[middle] ) {
88195b2b
JS
1854 end = middle - 1;
1855 } else {
1856 begin = middle + 1;
1857 }
1858
1859 }
1860
1861 return AddColor( value, middle );
1862}
1863
1864int wxSearchColor::AddColor( unsigned int value, int pos )
1865{
1866 int i;
1867 int pixel = -1;
1868 int max = 3 * (65536);
1869 for ( i = 0; i < 256; i++ ) {
1870 int rdiff = ((value >> 8) & 0xFF00 ) - colors[i].red;
1871 int gdiff = ((value ) & 0xFF00 ) - colors[i].green;
1872 int bdiff = ((value << 8) & 0xFF00 ) - colors[i].blue;
1873 int sum = abs (rdiff) + abs (gdiff) + abs (bdiff);
1874 if (sum < max) { pixel = i; max = sum; }
1875 }
1876
069d0f27
VZ
1877 if ( entry[pos] < 0 ) {
1878 color[pos] = value;
1879 entry[pos] = pixel;
1880 } else if ( value < color[pos] ) {
88195b2b 1881
069d0f27
VZ
1882 if ( bottom > 0 ) {
1883 for ( i = bottom; i < pos; i++ ) {
1884 color[i-1] = color[i];
1885 entry[i-1] = entry[i];
88195b2b 1886 }
069d0f27
VZ
1887 bottom--;
1888 color[pos-1] = value;
1889 entry[pos-1] = pixel;
1890 } else if ( top < size-1 ) {
1891 for ( i = top; i >= pos; i-- ) {
1892 color[i+1] = color[i];
1893 entry[i+1] = entry[i];
88195b2b 1894 }
069d0f27
VZ
1895 top++;
1896 color[pos] = value;
1897 entry[pos] = pixel;
88195b2b
JS
1898 }
1899
1900 } else {
1901
069d0f27
VZ
1902 if ( top < size-1 ) {
1903 for ( i = top; i > pos; i-- ) {
1904 color[i+1] = color[i];
1905 entry[i+1] = entry[i];
88195b2b 1906 }
069d0f27
VZ
1907 top++;
1908 color[pos+1] = value;
1909 entry[pos+1] = pixel;
1910 } else if ( bottom > 0 ) {
1911 for ( i = bottom; i < pos; i++ ) {
1912 color[i-1] = color[i];
1913 entry[i-1] = entry[i];
88195b2b 1914 }
069d0f27
VZ
1915 bottom--;
1916 color[pos] = value;
1917 entry[pos] = pixel;
88195b2b
JS
1918 }
1919
1920 }
1921
1922 return( pixel );
1923}
1924
ee4c6942
JS
1925wxBitmap wxImage::ConvertToBitmap() const
1926{
b75867a6 1927 wxBitmap bitmap;
c7abc967 1928
223d09f6 1929 wxCHECK_MSG( Ok(), bitmap, wxT("invalid image") );
a91b47e8 1930
b75867a6
RR
1931 int width = GetWidth();
1932 int height = GetHeight();
c7abc967 1933
b75867a6
RR
1934 bitmap.SetHeight( height );
1935 bitmap.SetWidth( width );
c7abc967 1936
b75867a6
RR
1937 Display *dpy = (Display*) wxGetDisplay();
1938 Visual* vis = DefaultVisual( dpy, DefaultScreen( dpy ) );
1939 int bpp = DefaultDepth( dpy, DefaultScreen( dpy ) );
c7abc967 1940
b75867a6 1941 // Create image
c7abc967 1942
b75867a6 1943 XImage *data_image = XCreateImage( dpy, vis, bpp, ZPixmap, 0, 0, width, height, 32, 0 );
a91b47e8 1944 data_image->data = (char*) malloc( data_image->bytes_per_line * data_image->height );
c7abc967 1945
b75867a6 1946 bitmap.Create( width, height, bpp );
a91b47e8 1947
dbda9e86 1948 /*
b75867a6 1949 // Create mask
c7abc967 1950
dbda9e86 1951 GdkImage *mask_image = (GdkImage*) NULL;
c7abc967 1952
dbda9e86
JS
1953 if (HasMask())
1954 {
b75867a6 1955 unsigned char *mask_data = (unsigned char*)malloc( ((width >> 3)+8) * height );
c7abc967 1956
dbda9e86 1957 mask_image = gdk_image_new_bitmap( gdk_visual_get_system(), mask_data, width, height );
c7abc967 1958
dbda9e86
JS
1959 wxMask *mask = new wxMask();
1960 mask->m_bitmap = gdk_pixmap_new( (GdkWindow*)&gdk_root_parent, width, height, 1 );
c7abc967 1961
dbda9e86
JS
1962 bitmap.SetMask( mask );
1963 }
1964 */
c7abc967 1965
b75867a6 1966 // Retrieve depth info
c7abc967 1967
b75867a6
RR
1968 XVisualInfo vinfo_template;
1969 XVisualInfo *vi;
c7abc967 1970
b75867a6
RR
1971 vinfo_template.visual = vis;
1972 vinfo_template.visualid = XVisualIDFromVisual( vis );
1973 vinfo_template.depth = bpp;
1974 int nitem = 0;
c7abc967 1975
b75867a6 1976 vi = XGetVisualInfo( dpy, VisualIDMask|VisualDepthMask, &vinfo_template, &nitem );
c7abc967 1977
223d09f6 1978 wxCHECK_MSG( vi, wxNullBitmap, wxT("no visual") );
c7abc967 1979
38274997 1980 XFree( vi );
a91b47e8 1981
b75867a6
RR
1982 if ((bpp == 16) && (vi->red_mask != 0xf800)) bpp = 15;
1983 if (bpp < 8) bpp = 8;
c7abc967 1984
b75867a6 1985 // Render
c7abc967 1986
b75867a6
RR
1987 enum byte_order { RGB, RBG, BRG, BGR, GRB, GBR };
1988 byte_order b_o = RGB;
c7abc967 1989
b75867a6
RR
1990 if (bpp >= 24)
1991 {
1992 if ((vi->red_mask > vi->green_mask) && (vi->green_mask > vi->blue_mask)) b_o = RGB;
1993 else if ((vi->red_mask > vi->blue_mask) && (vi->blue_mask > vi->green_mask)) b_o = RGB;
1994 else if ((vi->blue_mask > vi->red_mask) && (vi->red_mask > vi->green_mask)) b_o = BRG;
1995 else if ((vi->blue_mask > vi->green_mask) && (vi->green_mask > vi->red_mask)) b_o = BGR;
1996 else if ((vi->green_mask > vi->red_mask) && (vi->red_mask > vi->blue_mask)) b_o = GRB;
1997 else if ((vi->green_mask > vi->blue_mask) && (vi->blue_mask > vi->red_mask)) b_o = GBR;
1998 }
c7abc967 1999
dbda9e86 2000 /*
b75867a6
RR
2001 int r_mask = GetMaskRed();
2002 int g_mask = GetMaskGreen();
2003 int b_mask = GetMaskBlue();
dbda9e86 2004 */
c7abc967 2005
38274997
RR
2006 XColor colors[256];
2007 if (bpp == 8)
2008 {
dbda9e86 2009 Colormap cmap = (Colormap) wxTheApp->GetMainColormap( dpy );
c7abc967 2010
38274997 2011 for (int i = 0; i < 256; i++) colors[i].pixel = i;
dbda9e86 2012 XQueryColors( dpy, cmap, colors, 256 );
38274997 2013 }
c7abc967 2014
88195b2b 2015 wxSearchColor scolor( 256, colors );
b75867a6 2016 unsigned char* data = GetData();
c7abc967 2017
b75867a6
RR
2018 int index = 0;
2019 for (int y = 0; y < height; y++)
2020 {
2021 for (int x = 0; x < width; x++)
2022 {
2023 int r = data[index];
dbda9e86 2024 index++;
b75867a6 2025 int g = data[index];
dbda9e86 2026 index++;
b75867a6 2027 int b = data[index];
dbda9e86 2028 index++;
c7abc967 2029
dbda9e86
JS
2030 /*
2031 if (HasMask())
2032 {
2033 if ((r == r_mask) && (b == b_mask) && (g == g_mask))
2034 gdk_image_put_pixel( mask_image, x, y, 1 );
2035 else
2036 gdk_image_put_pixel( mask_image, x, y, 0 );
2037 }
2038 */
c7abc967 2039
dbda9e86
JS
2040 switch (bpp)
2041 {
2042 case 8:
2043 {
88195b2b 2044#if 0 // Old, slower code
b75867a6 2045 int pixel = -1;
dbda9e86
JS
2046 /*
2047 if (wxTheApp->m_colorCube)
2048 {
2049 pixel = wxTheApp->m_colorCube
c7abc967
VZ
2050 [ ((r & 0xf8) << 7) + ((g & 0xf8) << 2) + ((b & 0xf8) >> 3) ];
2051 }
b75867a6 2052 else
dbda9e86
JS
2053 {
2054 */
2055 int max = 3 * (65536);
2056 for (int i = 0; i < 256; i++)
2057 {
2058 int rdiff = (r << 8) - colors[i].red;
2059 int gdiff = (g << 8) - colors[i].green;
2060 int bdiff = (b << 8) - colors[i].blue;
2061 int sum = abs (rdiff) + abs (gdiff) + abs (bdiff);
2062 if (sum < max) { pixel = i; max = sum; }
2063 }
2064 /*
2065 }
2066 */
88195b2b
JS
2067#endif
2068
069d0f27
VZ
2069 // And this is all to get the 'right' color...
2070 int pixel = scolor.SearchColor( r, g, b );
dbda9e86
JS
2071 XPutPixel( data_image, x, y, pixel );
2072 break;
2073 }
2074 case 15:
2075 {
2076 int pixel = ((r & 0xf8) << 7) | ((g & 0xf8) << 2) | ((b & 0xf8) >> 3);
2077 XPutPixel( data_image, x, y, pixel );
2078 break;
2079 }
2080 case 16:
2081 {
2082 int pixel = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | ((b & 0xf8) >> 3);
2083 XPutPixel( data_image, x, y, pixel );
2084 break;
2085 }
2086 case 32:
2087 case 24:
2088 {
2089 int pixel = 0;
2090 switch (b_o)
2091 {
2092 case RGB: pixel = (r << 16) | (g << 8) | b; break;
2093 case RBG: pixel = (r << 16) | (b << 8) | g; break;
2094 case BRG: pixel = (b << 16) | (r << 8) | g; break;
2095 case BGR: pixel = (b << 16) | (g << 8) | r; break;
2096 case GRB: pixel = (g << 16) | (r << 8) | b; break;
2097 case GBR: pixel = (g << 16) | (b << 8) | r; break;
2098 }
2099 XPutPixel( data_image, x, y, pixel );
2100 }
2101 default: break;
2102 }
b75867a6
RR
2103 } // for
2104 } // for
c7abc967 2105
b75867a6 2106 // Blit picture
c7abc967 2107
b75867a6
RR
2108 XGCValues gcvalues;
2109 gcvalues.foreground = BlackPixel( dpy, DefaultScreen( dpy ) );
2110 GC gc = XCreateGC( dpy, RootWindow ( dpy, DefaultScreen(dpy) ), GCForeground, &gcvalues );
2111 XPutImage( dpy, (Drawable)bitmap.GetPixmap(), gc, data_image, 0, 0, 0, 0, width, height );
c7abc967 2112
b75867a6
RR
2113 XDestroyImage( data_image );
2114 XFreeGC( dpy, gc );
c7abc967 2115
dbda9e86 2116 /*
b75867a6 2117 // Blit mask
c7abc967 2118
dbda9e86
JS
2119 if (HasMask())
2120 {
2121 GdkGC *mask_gc = gdk_gc_new( bitmap.GetMask()->GetBitmap() );
c7abc967 2122
b75867a6 2123 gdk_draw_image( bitmap.GetMask()->GetBitmap(), mask_gc, mask_image, 0, 0, 0, 0, width, height );
c7abc967 2124
dbda9e86
JS
2125 gdk_image_destroy( mask_image );
2126 gdk_gc_unref( mask_gc );
2127 }
2128 */
c7abc967 2129
b75867a6 2130 return bitmap;
ee4c6942
JS
2131}
2132
2133wxImage::wxImage( const wxBitmap &bitmap )
2134{
223d09f6 2135 wxCHECK_RET( bitmap.Ok(), wxT("invalid bitmap") );
c7abc967 2136
38274997
RR
2137 Display *dpy = (Display*) wxGetDisplay();
2138 Visual* vis = DefaultVisual( dpy, DefaultScreen( dpy ) );
2139 int bpp = DefaultDepth( dpy, DefaultScreen( dpy ) );
c7abc967 2140
38274997 2141 XImage *ximage = XGetImage( dpy,
dbda9e86
JS
2142 (Drawable)bitmap.GetPixmap(),
2143 0, 0,
2144 bitmap.GetWidth(), bitmap.GetHeight(),
2145 AllPlanes, ZPixmap );
c7abc967 2146
223d09f6 2147 wxCHECK_RET( ximage, wxT("couldn't create image") );
c7abc967 2148
38274997
RR
2149 Create( bitmap.GetWidth(), bitmap.GetHeight() );
2150 char unsigned *data = GetData();
c7abc967 2151
38274997
RR
2152 if (!data)
2153 {
2154 XDestroyImage( ximage );
223d09f6 2155 wxFAIL_MSG( wxT("couldn't create image") );
38274997
RR
2156 return;
2157 }
c7abc967 2158
dbda9e86 2159 /*
38274997
RR
2160 GdkImage *gdk_image_mask = (GdkImage*) NULL;
2161 if (bitmap.GetMask())
2162 {
dbda9e86
JS
2163 gdk_image_mask = gdk_image_get( bitmap.GetMask()->GetBitmap(),
2164 0, 0,
2165 bitmap.GetWidth(), bitmap.GetHeight() );
c7abc967 2166
dbda9e86
JS
2167 SetMaskColour( 16, 16, 16 ); // anything unlikely and dividable
2168 }
2169 */
c7abc967 2170
38274997 2171 // Retrieve depth info
c7abc967 2172
38274997
RR
2173 XVisualInfo vinfo_template;
2174 XVisualInfo *vi;
c7abc967 2175
38274997
RR
2176 vinfo_template.visual = vis;
2177 vinfo_template.visualid = XVisualIDFromVisual( vis );
2178 vinfo_template.depth = bpp;
2179 int nitem = 0;
c7abc967 2180
38274997 2181 vi = XGetVisualInfo( dpy, VisualIDMask|VisualDepthMask, &vinfo_template, &nitem );
c7abc967 2182
223d09f6 2183 wxCHECK_RET( vi, wxT("no visual") );
c7abc967 2184
38274997 2185 if ((bpp == 16) && (vi->red_mask != 0xf800)) bpp = 15;
c7abc967 2186
38274997 2187 XFree( vi );
c7abc967 2188
38274997
RR
2189 XColor colors[256];
2190 if (bpp == 8)
2191 {
dbda9e86 2192 Colormap cmap = (Colormap)wxTheApp->GetMainColormap( dpy );
c7abc967 2193
38274997 2194 for (int i = 0; i < 256; i++) colors[i].pixel = i;
dbda9e86 2195 XQueryColors( dpy, cmap, colors, 256 );
38274997 2196 }
c7abc967 2197
38274997
RR
2198 long pos = 0;
2199 for (int j = 0; j < bitmap.GetHeight(); j++)
2200 {
2201 for (int i = 0; i < bitmap.GetWidth(); i++)
2202 {
dbda9e86 2203 int pixel = XGetPixel( ximage, i, j );
38274997
RR
2204 if (bpp <= 8)
2205 {
2206 data[pos] = colors[pixel].red >> 8;
2207 data[pos+1] = colors[pixel].green >> 8;
2208 data[pos+2] = colors[pixel].blue >> 8;
2209 } else if (bpp == 15)
2210 {
2211 data[pos] = (pixel >> 7) & 0xf8;
2212 data[pos+1] = (pixel >> 2) & 0xf8;
2213 data[pos+2] = (pixel << 3) & 0xf8;
2214 } else if (bpp == 16)
2215 {
2216 data[pos] = (pixel >> 8) & 0xf8;
2217 data[pos+1] = (pixel >> 3) & 0xfc;
2218 data[pos+2] = (pixel << 3) & 0xf8;
2219 } else
2220 {
2221 data[pos] = (pixel >> 16) & 0xff;
2222 data[pos+1] = (pixel >> 8) & 0xff;
2223 data[pos+2] = pixel & 0xff;
2224 }
c7abc967 2225
dbda9e86 2226 /*
38274997
RR
2227 if (gdk_image_mask)
2228 {
dbda9e86
JS
2229 int mask_pixel = gdk_image_get_pixel( gdk_image_mask, i, j );
2230 if (mask_pixel == 0)
2231 {
2232 data[pos] = 16;
2233 data[pos+1] = 16;
2234 data[pos+2] = 16;
38274997 2235 }
dbda9e86
JS
2236 }
2237 */
c7abc967 2238
38274997
RR
2239 pos += 3;
2240 }
2241 }
c7abc967 2242
38274997 2243 XDestroyImage( ximage );
dbda9e86 2244 /*
38274997 2245 if (gdk_image_mask) gdk_image_destroy( gdk_image_mask );
dbda9e86 2246 */
ee4c6942
JS
2247}
2248#endif
a91b47e8 2249
004fd0c8
DW
2250#ifdef __WXPM__
2251// OS/2 Presentation manager conversion routings
2252
2253wxBitmap wxImage::ConvertToBitmap() const
2254{
2255 if ( !Ok() )
2256 return wxNullBitmap;
2257 wxBitmap bitmap; // remove
2258// TODO:
2259/*
2260 int sizeLimit = 1024*768*3;
2261
2262 // width and height of the device-dependent bitmap
2263 int width = GetWidth();
2264 int bmpHeight = GetHeight();
2265
2266 // calc the number of bytes per scanline and padding
2267 int bytePerLine = width*3;
2268 int sizeDWORD = sizeof( DWORD );
2269 int lineBoundary = bytePerLine % sizeDWORD;
2270 int padding = 0;
2271 if( lineBoundary > 0 )
2272 {
2273 padding = sizeDWORD - lineBoundary;
2274 bytePerLine += padding;
2275 }
2276 // calc the number of DIBs and heights of DIBs
2277 int numDIB = 1;
2278 int hRemain = 0;
2279 int height = sizeLimit/bytePerLine;
2280 if( height >= bmpHeight )
2281 height = bmpHeight;
2282 else
2283 {
2284 numDIB = bmpHeight / height;
2285 hRemain = bmpHeight % height;
2286 if( hRemain >0 ) numDIB++;
2287 }
2288
2289 // set bitmap parameters
2290 wxBitmap bitmap;
2291 wxCHECK_MSG( Ok(), bitmap, wxT("invalid image") );
2292 bitmap.SetWidth( width );
2293 bitmap.SetHeight( bmpHeight );
2294 bitmap.SetDepth( wxDisplayDepth() );
2295
2296 // create a DIB header
2297 int headersize = sizeof(BITMAPINFOHEADER);
2298 LPBITMAPINFO lpDIBh = (BITMAPINFO *) malloc( headersize );
2299 wxCHECK_MSG( lpDIBh, bitmap, wxT("could not allocate memory for DIB header") );
2300 // Fill in the DIB header
2301 lpDIBh->bmiHeader.biSize = headersize;
2302 lpDIBh->bmiHeader.biWidth = (DWORD)width;
2303 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
2304 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
2305 // the general formula for biSizeImage:
2306 // ( ( ( ((DWORD)width*24) +31 ) & ~31 ) >> 3 ) * height;
2307 lpDIBh->bmiHeader.biPlanes = 1;
2308 lpDIBh->bmiHeader.biBitCount = 24;
2309 lpDIBh->bmiHeader.biCompression = BI_RGB;
2310 lpDIBh->bmiHeader.biClrUsed = 0;
2311 // These seem not really needed for our purpose here.
2312 lpDIBh->bmiHeader.biClrImportant = 0;
2313 lpDIBh->bmiHeader.biXPelsPerMeter = 0;
2314 lpDIBh->bmiHeader.biYPelsPerMeter = 0;
2315 // memory for DIB data
2316 unsigned char *lpBits;
2317 lpBits = (unsigned char *)malloc( lpDIBh->bmiHeader.biSizeImage );
2318 if( !lpBits )
2319 {
2320 wxFAIL_MSG( wxT("could not allocate memory for DIB") );
2321 free( lpDIBh );
2322 return bitmap;
2323 }
2324
2325 // create and set the device-dependent bitmap
2326 HDC hdc = ::GetDC(NULL);
2327 HDC memdc = ::CreateCompatibleDC( hdc );
2328 HBITMAP hbitmap;
2329 hbitmap = ::CreateCompatibleBitmap( hdc, width, bmpHeight );
2330 ::SelectObject( memdc, hbitmap);
2331
2332 // copy image data into DIB data and then into DDB (in a loop)
2333 unsigned char *data = GetData();
2334 int i, j, n;
2335 int origin = 0;
2336 unsigned char *ptdata = data;
2337 unsigned char *ptbits;
2338
2339 for( n=0; n<numDIB; n++ )
2340 {
2341 if( numDIB > 1 && n == numDIB-1 && hRemain > 0 )
2342 {
2343 // redefine height and size of the (possibly) last smaller DIB
2344 // memory is not reallocated
2345 height = hRemain;
2346 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
2347 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
2348 }
2349 ptbits = lpBits;
2350
2351 for( j=0; j<height; j++ )
2352 {
2353 for( i=0; i<width; i++ )
2354 {
2355 *(ptbits++) = *(ptdata+2);
2356 *(ptbits++) = *(ptdata+1);
2357 *(ptbits++) = *(ptdata );
2358 ptdata += 3;
2359 }
2360 for( i=0; i< padding; i++ ) *(ptbits++) = 0;
2361 }
2362 ::StretchDIBits( memdc, 0, origin, width, height,\
2363 0, 0, width, height, lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
2364 origin += height;
2365 // if numDIB = 1, lines below can also be used
2366 // hbitmap = CreateDIBitmap( hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits, lpDIBh, DIB_RGB_COLORS );
2367 // The above line is equivalent to the following two lines.
2368 // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
2369 // ::SetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS);
2370 // or the following lines
2371 // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
2372 // HDC memdc = ::CreateCompatibleDC( hdc );
2373 // ::SelectObject( memdc, hbitmap);
2374 // ::SetDIBitsToDevice( memdc, 0, 0, width, height,
2375 // 0, 0, 0, height, (void *)lpBits, lpDIBh, DIB_RGB_COLORS);
2376 // ::SelectObject( memdc, 0 );
2377 // ::DeleteDC( memdc );
2378 }
2379 bitmap.SetHBITMAP( (WXHBITMAP) hbitmap );
2380
2381 // similarly, created an mono-bitmap for the possible mask
2382 if( HasMask() )
2383 {
2384 hbitmap = ::CreateBitmap( (WORD)width, (WORD)bmpHeight, 1, 1, NULL );
2385 ::SelectObject( memdc, hbitmap);
2386 if( numDIB == 1 ) height = bmpHeight;
2387 else height = sizeLimit/bytePerLine;
2388 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
2389 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
2390 origin = 0;
2391 unsigned char r = GetMaskRed();
2392 unsigned char g = GetMaskGreen();
2393 unsigned char b = GetMaskBlue();
2394 unsigned char zero = 0, one = 255;
2395 ptdata = data;
2396 for( n=0; n<numDIB; n++ )
2397 {
2398 if( numDIB > 1 && n == numDIB - 1 && hRemain > 0 )
2399 {
2400 // redefine height and size of the (possibly) last smaller DIB
2401 // memory is not reallocated
2402 height = hRemain;
2403 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
2404 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
2405 }
2406 ptbits = lpBits;
2407 for( int j=0; j<height; j++ )
2408 {
2409 for(i=0; i<width; i++ )
2410 {
2411 if( (*(ptdata++)!=r) | (*(ptdata++)!=g) | (*(ptdata++)!=b) )
2412 {
2413 *(ptbits++) = one;
2414 *(ptbits++) = one;
2415 *(ptbits++) = one;
2416 }
2417 else
2418 {
2419 *(ptbits++) = zero;
2420 *(ptbits++) = zero;
2421 *(ptbits++) = zero;
2422 }
2423 }
2424 for( i=0; i< padding; i++ ) *(ptbits++) = zero;
2425 }
2426 ::StretchDIBits( memdc, 0, origin, width, height,\
2427 0, 0, width, height, lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
2428 origin += height;
2429 }
2430 // create a wxMask object
2431 wxMask *mask = new wxMask();
2432 mask->SetMaskBitmap( (WXHBITMAP) hbitmap );
2433 bitmap.SetMask( mask );
2434 }
2435
2436 // free allocated resources
2437 ::SelectObject( memdc, 0 );
2438 ::DeleteDC( memdc );
2439 ::ReleaseDC(NULL, hdc);
2440 free(lpDIBh);
2441 free(lpBits);
2442
2443 // check the wxBitmap object
2444 if( bitmap.GetHBITMAP() )
2445 bitmap.SetOk( TRUE );
2446 else
2447 bitmap.SetOk( FALSE );
2448*/
2449 return bitmap;
2450}
2451
2452wxImage::wxImage( const wxBitmap &bitmap )
2453{
2454 // check the bitmap
2455 if( !bitmap.Ok() )
2456 {
2457 wxFAIL_MSG( wxT("invalid bitmap") );
2458 return;
2459 }
2460
2461 // create an wxImage object
2462 int width = bitmap.GetWidth();
2463 int height = bitmap.GetHeight();
2464 Create( width, height );
2465 unsigned char *data = GetData();
2466 if( !data )
2467 {
2468 wxFAIL_MSG( wxT("could not allocate data for image") );
2469 return;
2470 }
2471
2472 // calc the number of bytes per scanline and padding in the DIB
2473 int bytePerLine = width*3;
2474 int sizeDWORD = sizeof( DWORD );
2475 int lineBoundary = bytePerLine % sizeDWORD;
2476 int padding = 0;
2477 if( lineBoundary > 0 )
2478 {
2479 padding = sizeDWORD - lineBoundary;
2480 bytePerLine += padding;
2481 }
2482// TODO:
2483/*
2484 // create a DIB header
2485 int headersize = sizeof(BITMAPINFOHEADER);
2486 LPBITMAPINFO lpDIBh = (BITMAPINFO *) malloc( headersize );
2487 if( !lpDIBh )
2488 {
2489 wxFAIL_MSG( wxT("could not allocate data for DIB header") );
2490 free( data );
2491 return;
2492 }
2493 // Fill in the DIB header
2494 lpDIBh->bmiHeader.biSize = headersize;
2495 lpDIBh->bmiHeader.biWidth = width;
2496 lpDIBh->bmiHeader.biHeight = -height;
2497 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
2498 lpDIBh->bmiHeader.biPlanes = 1;
2499 lpDIBh->bmiHeader.biBitCount = 24;
2500 lpDIBh->bmiHeader.biCompression = BI_RGB;
2501 lpDIBh->bmiHeader.biClrUsed = 0;
2502 // These seem not really needed for our purpose here.
2503 lpDIBh->bmiHeader.biClrImportant = 0;
2504 lpDIBh->bmiHeader.biXPelsPerMeter = 0;
2505 lpDIBh->bmiHeader.biYPelsPerMeter = 0;
2506 // memory for DIB data
2507 unsigned char *lpBits;
2508 lpBits = (unsigned char *) malloc( lpDIBh->bmiHeader.biSizeImage );
2509 if( !lpBits )
2510 {
2511 wxFAIL_MSG( wxT("could not allocate data for DIB") );
2512 free( data );
2513 free( lpDIBh );
2514 return;
2515 }
2516
2517 // copy data from the device-dependent bitmap to the DIB
2518 HDC hdc = ::GetDC(NULL);
2519 HBITMAP hbitmap;
2520 hbitmap = (HBITMAP) bitmap.GetHBITMAP();
2521 ::GetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS );
2522
2523 // copy DIB data into the wxImage object
2524 int i, j;
2525 unsigned char *ptdata = data;
2526 unsigned char *ptbits = lpBits;
2527 for( i=0; i<height; i++ )
2528 {
2529 for( j=0; j<width; j++ )
2530 {
2531 *(ptdata++) = *(ptbits+2);
2532 *(ptdata++) = *(ptbits+1);
2533 *(ptdata++) = *(ptbits );
2534 ptbits += 3;
2535 }
2536 ptbits += padding;
2537 }
2538
2539 // similarly, set data according to the possible mask bitmap
2540 if( bitmap.GetMask() && bitmap.GetMask()->GetMaskBitmap() )
2541 {
2542 hbitmap = (HBITMAP) bitmap.GetMask()->GetMaskBitmap();
2543 // memory DC created, color set, data copied, and memory DC deleted
2544 HDC memdc = ::CreateCompatibleDC( hdc );
2545 ::SetTextColor( memdc, RGB( 0, 0, 0 ) );
2546 ::SetBkColor( memdc, RGB( 255, 255, 255 ) );
2547 ::GetDIBits( memdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS );
2548 ::DeleteDC( memdc );
2549 // background color set to RGB(16,16,16) in consistent with wxGTK
2550 unsigned char r=16, g=16, b=16;
2551 ptdata = data;
2552 ptbits = lpBits;
2553 for( i=0; i<height; i++ )
2554 {
2555 for( j=0; j<width; j++ )
2556 {
2557 if( *ptbits != 0 )
2558 ptdata += 3;
2559 else
2560 {
2561 *(ptdata++) = r;
2562 *(ptdata++) = g;
2563 *(ptdata++) = b;
2564 }
2565 ptbits += 3;
2566 }
2567 ptbits += padding;
2568 }
2569 SetMaskColour( r, g, b );
2570 SetMask( TRUE );
2571 }
2572 else
2573 {
2574 SetMask( FALSE );
2575 }
2576 // free allocated resources
2577 ::ReleaseDC(NULL, hdc);
2578 free(lpDIBh);
2579 free(lpBits);
2580*/
2581}
2582
2583#endif
2584
a91b47e8
JS
2585// A module to allow wxImage initialization/cleanup
2586// without calling these functions from app.cpp or from
2587// the user's application.
2588
2589class wxImageModule: public wxModule
2590{
2591DECLARE_DYNAMIC_CLASS(wxImageModule)
2592public:
2593 wxImageModule() {}
2594 bool OnInit() { wxImage::InitStandardHandlers(); return TRUE; };
2595 void OnExit() { wxImage::CleanUpHandlers(); };
2596};
2597
2598IMPLEMENT_DYNAMIC_CLASS(wxImageModule, wxModule)
c9d01afd
GRG
2599
2600
2601//-----------------------------------------------------------------------------
2602
89d00456
GRG
2603// GRG, Dic/99
2604// Counts and returns the number of different colours. Optionally stops
cc9f7d79
GRG
2605// when it exceeds 'stopafter' different colours. This is useful, for
2606// example, to see if the image can be saved as 8-bit (256 colour or
2607// less, in this case it would be invoked as CountColours(256)). Default
2608// value for stopafter is -1 (don't care).
89d00456 2609//
cc9f7d79 2610unsigned long wxImage::CountColours( unsigned long stopafter )
89d00456
GRG
2611{
2612 wxHashTable h;
ad30de59 2613 wxObject dummy;
89d00456
GRG
2614 unsigned char r, g, b, *p;
2615 unsigned long size, nentries, key;
2616
2617 p = GetData();
2618 size = GetWidth() * GetHeight();
2619 nentries = 0;
2620
cc9f7d79 2621 for (unsigned long j = 0; (j < size) && (nentries <= stopafter) ; j++)
89d00456
GRG
2622 {
2623 r = *(p++);
2624 g = *(p++);
2625 b = *(p++);
2626 key = (r << 16) | (g << 8) | b;
2627
ad30de59 2628 if (h.Get(key) == NULL)
89d00456 2629 {
ad30de59 2630 h.Put(key, &dummy);
89d00456
GRG
2631 nentries++;
2632 }
2633 }
2634
89d00456
GRG
2635 return nentries;
2636}
2637
2638
c9d01afd
GRG
2639// GRG, Dic/99
2640// Computes the histogram of the image and fills a hash table, indexed
2641// with integer keys built as 0xRRGGBB, containing wxHNode objects. Each
2642// wxHNode contains an 'index' (useful to build a palette with the image
2643// colours) and a 'value', which is the number of pixels in the image with
2644// that colour.
89d00456 2645//
c9d01afd
GRG
2646unsigned long wxImage::ComputeHistogram( wxHashTable &h )
2647{
2648 unsigned char r, g, b, *p;
2649 unsigned long size, nentries, key;
2650 wxHNode *hnode;
2651
2652 p = GetData();
2653 size = GetWidth() * GetHeight();
2654 nentries = 0;
2655
2656 for (unsigned long j = 0; j < size; j++)
2657 {
2658 r = *(p++);
2659 g = *(p++);
2660 b = *(p++);
2661 key = (r << 16) | (g << 8) | b;
2662
2663 hnode = (wxHNode *) h.Get(key);
2664
2665 if (hnode)
2666 hnode->value++;
2667 else
2668 {
2669 hnode = new wxHNode();
97fdfcc9 2670 hnode->index = nentries++;
c9d01afd
GRG
2671 hnode->value = 1;
2672
2673 h.Put(key, (wxObject *)hnode);
2674 }
2675 }
2676
2677 return nentries;
2678}
2679
7a632f10
JS
2680/*
2681 * Rotation code by Carlos Moreno
2682 */
2683
b5c91ac6
GRG
2684// GRG: I've removed wxRotationPoint - we already have wxRealPoint which
2685// does exactly the same thing. And I also got rid of wxRotationPixel
2686// bacause of potential problems in architectures where alignment
2687// is an issue, so I had to rewrite parts of the code.
7a632f10 2688
7a632f10
JS
2689static const double gs_Epsilon = 1e-10;
2690
2691static inline int wxCint (double x)
2692{
2693 return (x > 0) ? (int) (x + 0.5) : (int) (x - 0.5);
2694}
2695
2696
2697// Auxiliary function to rotate a point (x,y) with respect to point p0
2698// make it inline and use a straight return to facilitate optimization
2699// also, the function receives the sine and cosine of the angle to avoid
2700// repeating the time-consuming calls to these functions -- sin/cos can
2701// be computed and stored in the calling function.
2702
b5c91ac6 2703inline wxRealPoint rotated_point (const wxRealPoint & p, double cos_angle, double sin_angle, const wxRealPoint & p0)
7a632f10 2704{
b5c91ac6
GRG
2705 return wxRealPoint (p0.x + (p.x - p0.x) * cos_angle - (p.y - p0.y) * sin_angle,
2706 p0.y + (p.y - p0.y) * cos_angle + (p.x - p0.x) * sin_angle);
7a632f10
JS
2707}
2708
b5c91ac6 2709inline wxRealPoint rotated_point (double x, double y, double cos_angle, double sin_angle, const wxRealPoint & p0)
7a632f10 2710{
b5c91ac6 2711 return rotated_point (wxRealPoint(x,y), cos_angle, sin_angle, p0);
7a632f10
JS
2712}
2713
2714wxImage wxImage::Rotate(double angle, const wxPoint & centre_of_rotation, bool interpolating, wxPoint * offset_after_rotation) const
2715{
7a632f10
JS
2716 int i;
2717 angle = -angle; // screen coordinates are a mirror image of "real" coordinates
2718
ad30de59 2719 // Create pointer-based array to accelerate access to wxImage's data
b5c91ac6 2720 unsigned char ** data = new unsigned char * [GetHeight()];
7a632f10 2721
b5c91ac6 2722 data[0] = GetData();
7a632f10 2723
b5c91ac6
GRG
2724 for (i = 1; i < GetHeight(); i++)
2725 data[i] = data[i - 1] + (3 * GetWidth());
7a632f10 2726
b5c91ac6 2727 // precompute coefficients for rotation formula
ad30de59 2728 // (sine and cosine of the angle)
7a632f10
JS
2729 const double cos_angle = cos(angle);
2730 const double sin_angle = sin(angle);
2731
ad30de59
GRG
2732 // Create new Image to store the result
2733 // First, find rectangle that covers the rotated image; to do that,
2734 // rotate the four corners
7a632f10 2735
b5c91ac6 2736 const wxRealPoint p0(centre_of_rotation.x, centre_of_rotation.y);
7a632f10 2737
b5c91ac6
GRG
2738 wxRealPoint p1 = rotated_point (0, 0, cos_angle, sin_angle, p0);
2739 wxRealPoint p2 = rotated_point (0, GetHeight(), cos_angle, sin_angle, p0);
2740 wxRealPoint p3 = rotated_point (GetWidth(), 0, cos_angle, sin_angle, p0);
2741 wxRealPoint p4 = rotated_point (GetWidth(), GetHeight(), cos_angle, sin_angle, p0);
7a632f10 2742
57c1c6cb
BJ
2743 int x1 = (int) floor (wxMin (wxMin(p1.x, p2.x), wxMin(p3.x, p4.x)));
2744 int y1 = (int) floor (wxMin (wxMin(p1.y, p2.y), wxMin(p3.y, p4.y)));
57c1c6cb
BJ
2745 int x2 = (int) ceil (wxMax (wxMax(p1.x, p2.x), wxMax(p3.x, p4.x)));
2746 int y2 = (int) ceil (wxMax (wxMax(p1.y, p2.y), wxMax(p3.y, p4.y)));
7a632f10
JS
2747
2748 wxImage rotated (x2 - x1 + 1, y2 - y1 + 1);
2749
2750 if (offset_after_rotation != NULL)
2751 {
06b466c7 2752 *offset_after_rotation = wxPoint (x1, y1);
7a632f10
JS
2753 }
2754
b5c91ac6
GRG
2755 // GRG: The rotated (destination) image is always accessed
2756 // sequentially, so there is no need for a pointer-based
2757 // array here (and in fact it would be slower).
2758 //
2759 unsigned char * dst = rotated.GetData();
7a632f10 2760
ad30de59
GRG
2761 // GRG: if the original image has a mask, use its RGB values
2762 // as the blank pixel, else, fall back to default (black).
2763 //
b5c91ac6
GRG
2764 unsigned char blank_r = 0;
2765 unsigned char blank_g = 0;
2766 unsigned char blank_b = 0;
ad30de59
GRG
2767
2768 if (HasMask())
2769 {
b5c91ac6
GRG
2770 blank_r = GetMaskRed();
2771 blank_g = GetMaskGreen();
2772 blank_b = GetMaskBlue();
2773 rotated.SetMaskColour( blank_r, blank_g, blank_b );
ad30de59
GRG
2774 }
2775
2776 // Now, for each point of the rotated image, find where it came from, by
2777 // performing an inverse rotation (a rotation of -angle) and getting the
2778 // pixel at those coordinates
2779
b5c91ac6
GRG
2780 // GRG: I've taken the (interpolating) test out of the loops, so that
2781 // it is done only once, instead of repeating it for each pixel.
7a632f10
JS
2782
2783 int x;
b5c91ac6 2784 if (interpolating)
7a632f10
JS
2785 {
2786 for (int y = 0; y < rotated.GetHeight(); y++)
2787 {
b5c91ac6 2788 for (x = 0; x < rotated.GetWidth(); x++)
7a632f10 2789 {
b5c91ac6
GRG
2790 wxRealPoint src = rotated_point (x + x1, y + y1, cos_angle, -sin_angle, p0);
2791
f2506310
JS
2792 if (-0.25 < src.x && src.x < GetWidth() - 0.75 &&
2793 -0.25 < src.y && src.y < GetHeight() - 0.75)
7a632f10 2794 {
ad30de59
GRG
2795 // interpolate using the 4 enclosing grid-points. Those
2796 // points can be obtained using floor and ceiling of the
2797 // exact coordinates of the point
f2506310 2798 // C.M. 2000-02-17: when the point is near the border, special care is required.
7a632f10 2799
f2506310
JS
2800 int x1, y1, x2, y2;
2801
2802 if (0 < src.x && src.x < GetWidth() - 1)
2803 {
2804 x1 = wxCint(floor(src.x));
2805 x2 = wxCint(ceil(src.x));
2806 }
2807 else // else means that x is near one of the borders (0 or width-1)
2808 {
2809 x1 = x2 = wxCint (src.x);
2810 }
2811
2812 if (0 < src.y && src.y < GetHeight() - 1)
2813 {
2814 y1 = wxCint(floor(src.y));
2815 y2 = wxCint(ceil(src.y));
2816 }
2817 else
2818 {
2819 y1 = y2 = wxCint (src.y);
2820 }
7a632f10 2821
ad30de59
GRG
2822 // get four points and the distances (square of the distance,
2823 // for efficiency reasons) for the interpolation formula
b5c91ac6
GRG
2824
2825 // GRG: Do not calculate the points until they are
2826 // really needed -- this way we can calculate
2827 // just one, instead of four, if d1, d2, d3
2828 // or d4 are < gs_Epsilon
7a632f10
JS
2829
2830 const double d1 = (src.x - x1) * (src.x - x1) + (src.y - y1) * (src.y - y1);
2831 const double d2 = (src.x - x2) * (src.x - x2) + (src.y - y1) * (src.y - y1);
2832 const double d3 = (src.x - x2) * (src.x - x2) + (src.y - y2) * (src.y - y2);
2833 const double d4 = (src.x - x1) * (src.x - x1) + (src.y - y2) * (src.y - y2);
2834
ad30de59
GRG
2835 // Now interpolate as a weighted average of the four surrounding
2836 // points, where the weights are the distances to each of those points
7a632f10 2837
ad30de59
GRG
2838 // If the point is exactly at one point of the grid of the source
2839 // image, then don't interpolate -- just assign the pixel
7a632f10 2840
06b466c7 2841 if (d1 < gs_Epsilon) // d1,d2,d3,d4 are positive -- no need for abs()
7a632f10 2842 {
b5c91ac6
GRG
2843 unsigned char *p = data[y1] + (3 * x1);
2844 *(dst++) = *(p++);
2845 *(dst++) = *(p++);
2846 *(dst++) = *(p++);
7a632f10
JS
2847 }
2848 else if (d2 < gs_Epsilon)
2849 {
b5c91ac6
GRG
2850 unsigned char *p = data[y1] + (3 * x2);
2851 *(dst++) = *(p++);
2852 *(dst++) = *(p++);
2853 *(dst++) = *(p++);
7a632f10
JS
2854 }
2855 else if (d3 < gs_Epsilon)
2856 {
b5c91ac6
GRG
2857 unsigned char *p = data[y2] + (3 * x2);
2858 *(dst++) = *(p++);
2859 *(dst++) = *(p++);
2860 *(dst++) = *(p++);
7a632f10
JS
2861 }
2862 else if (d4 < gs_Epsilon)
2863 {
b5c91ac6
GRG
2864 unsigned char *p = data[y2] + (3 * x1);
2865 *(dst++) = *(p++);
2866 *(dst++) = *(p++);
2867 *(dst++) = *(p++);
7a632f10
JS
2868 }
2869 else
2870 {
06b466c7 2871 // weights for the weighted average are proportional to the inverse of the distance
b5c91ac6
GRG
2872 unsigned char *v1 = data[y1] + (3 * x1);
2873 unsigned char *v2 = data[y1] + (3 * x2);
2874 unsigned char *v3 = data[y2] + (3 * x2);
2875 unsigned char *v4 = data[y2] + (3 * x1);
2876
06b466c7
VZ
2877 const double w1 = 1/d1, w2 = 1/d2, w3 = 1/d3, w4 = 1/d4;
2878
b5c91ac6
GRG
2879 // GRG: Unrolled.
2880
2881 *(dst++) = (unsigned char)
2882 ( (w1 * *(v1++) + w2 * *(v2++) +
2883 w3 * *(v3++) + w4 * *(v4++)) /
2884 (w1 + w2 + w3 + w4) );
2885 *(dst++) = (unsigned char)
2886 ( (w1 * *(v1++) + w2 * *(v2++) +
2887 w3 * *(v3++) + w4 * *(v4++)) /
2888 (w1 + w2 + w3 + w4) );
2889 *(dst++) = (unsigned char)
2890 ( (w1 * *(v1++) + w2 * *(v2++) +
2891 w3 * *(v3++) + w4 * *(v4++)) /
2892 (w1 + w2 + w3 + w4) );
7a632f10
JS
2893 }
2894 }
2895 else
2896 {
b5c91ac6
GRG
2897 *(dst++) = blank_r;
2898 *(dst++) = blank_g;
2899 *(dst++) = blank_b;
7a632f10
JS
2900 }
2901 }
b5c91ac6
GRG
2902 }
2903 }
2904 else // not interpolating
2905 {
2906 for (int y = 0; y < rotated.GetHeight(); y++)
2907 {
2908 for (x = 0; x < rotated.GetWidth(); x++)
7a632f10 2909 {
b5c91ac6
GRG
2910 wxRealPoint src = rotated_point (x + x1, y + y1, cos_angle, -sin_angle, p0);
2911
2912 const int xs = wxCint (src.x); // wxCint rounds to the
457e6c54 2913 const int ys = wxCint (src.y); // closest integer
7a632f10 2914
b5c91ac6
GRG
2915 if (0 <= xs && xs < GetWidth() &&
2916 0 <= ys && ys < GetHeight())
7a632f10 2917 {
b5c91ac6
GRG
2918 unsigned char *p = data[ys] + (3 * xs);
2919 *(dst++) = *(p++);
2920 *(dst++) = *(p++);
2921 *(dst++) = *(p++);
7a632f10
JS
2922 }
2923 else
2924 {
b5c91ac6
GRG
2925 *(dst++) = blank_r;
2926 *(dst++) = blank_g;
2927 *(dst++) = blank_b;
7a632f10
JS
2928 }
2929 }
2930 }
2931 }
2932
4aff28fc 2933 delete [] data;
4aff28fc 2934
7a632f10
JS
2935 return rotated;
2936}
c9d01afd 2937