]> git.saurik.com Git - wxWidgets.git/blame - src/common/image.cpp
some != NULL checks
[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
b75867a6 1948 // Create mask
c7abc967 1949
68be9f09
JS
1950 XImage *mask_image = (XImage*) NULL;
1951 if (HasMask())
1952 {
1953 mask_image = XCreateImage( dpy, vis, 1, ZPixmap, 0, 0, width, height, 32, 0 );
1954 mask_image->data = (char*) malloc( mask_image->bytes_per_line * mask_image->height );
1955 }
c7abc967 1956
b75867a6 1957 // Retrieve depth info
c7abc967 1958
b75867a6
RR
1959 XVisualInfo vinfo_template;
1960 XVisualInfo *vi;
c7abc967 1961
b75867a6
RR
1962 vinfo_template.visual = vis;
1963 vinfo_template.visualid = XVisualIDFromVisual( vis );
1964 vinfo_template.depth = bpp;
1965 int nitem = 0;
c7abc967 1966
b75867a6 1967 vi = XGetVisualInfo( dpy, VisualIDMask|VisualDepthMask, &vinfo_template, &nitem );
c7abc967 1968
223d09f6 1969 wxCHECK_MSG( vi, wxNullBitmap, wxT("no visual") );
c7abc967 1970
38274997 1971 XFree( vi );
a91b47e8 1972
b75867a6
RR
1973 if ((bpp == 16) && (vi->red_mask != 0xf800)) bpp = 15;
1974 if (bpp < 8) bpp = 8;
c7abc967 1975
b75867a6 1976 // Render
c7abc967 1977
b75867a6
RR
1978 enum byte_order { RGB, RBG, BRG, BGR, GRB, GBR };
1979 byte_order b_o = RGB;
c7abc967 1980
b75867a6
RR
1981 if (bpp >= 24)
1982 {
1983 if ((vi->red_mask > vi->green_mask) && (vi->green_mask > vi->blue_mask)) b_o = RGB;
1984 else if ((vi->red_mask > vi->blue_mask) && (vi->blue_mask > vi->green_mask)) b_o = RGB;
1985 else if ((vi->blue_mask > vi->red_mask) && (vi->red_mask > vi->green_mask)) b_o = BRG;
1986 else if ((vi->blue_mask > vi->green_mask) && (vi->green_mask > vi->red_mask)) b_o = BGR;
1987 else if ((vi->green_mask > vi->red_mask) && (vi->red_mask > vi->blue_mask)) b_o = GRB;
1988 else if ((vi->green_mask > vi->blue_mask) && (vi->blue_mask > vi->red_mask)) b_o = GBR;
1989 }
c7abc967 1990
b75867a6
RR
1991 int r_mask = GetMaskRed();
1992 int g_mask = GetMaskGreen();
1993 int b_mask = GetMaskBlue();
c7abc967 1994
38274997
RR
1995 XColor colors[256];
1996 if (bpp == 8)
1997 {
dbda9e86 1998 Colormap cmap = (Colormap) wxTheApp->GetMainColormap( dpy );
c7abc967 1999
38274997 2000 for (int i = 0; i < 256; i++) colors[i].pixel = i;
dbda9e86 2001 XQueryColors( dpy, cmap, colors, 256 );
38274997 2002 }
c7abc967 2003
88195b2b 2004 wxSearchColor scolor( 256, colors );
b75867a6 2005 unsigned char* data = GetData();
c7abc967 2006
68be9f09
JS
2007 bool hasMask = HasMask();
2008
b75867a6
RR
2009 int index = 0;
2010 for (int y = 0; y < height; y++)
2011 {
2012 for (int x = 0; x < width; x++)
2013 {
2014 int r = data[index];
dbda9e86 2015 index++;
b75867a6 2016 int g = data[index];
dbda9e86 2017 index++;
b75867a6 2018 int b = data[index];
dbda9e86 2019 index++;
c7abc967 2020
68be9f09 2021 if (hasMask)
dbda9e86 2022 {
68be9f09
JS
2023 if ((r == r_mask) && (b == b_mask) && (g == g_mask))
2024 XPutPixel( mask_image, x, y, 0 );
2025 else
2026 XPutPixel( mask_image, x, y, 1 );
dbda9e86 2027 }
c7abc967 2028
dbda9e86
JS
2029 switch (bpp)
2030 {
2031 case 8:
2032 {
88195b2b 2033#if 0 // Old, slower code
b75867a6 2034 int pixel = -1;
dbda9e86
JS
2035 /*
2036 if (wxTheApp->m_colorCube)
2037 {
2038 pixel = wxTheApp->m_colorCube
c7abc967
VZ
2039 [ ((r & 0xf8) << 7) + ((g & 0xf8) << 2) + ((b & 0xf8) >> 3) ];
2040 }
b75867a6 2041 else
dbda9e86
JS
2042 {
2043 */
2044 int max = 3 * (65536);
2045 for (int i = 0; i < 256; i++)
2046 {
2047 int rdiff = (r << 8) - colors[i].red;
2048 int gdiff = (g << 8) - colors[i].green;
2049 int bdiff = (b << 8) - colors[i].blue;
2050 int sum = abs (rdiff) + abs (gdiff) + abs (bdiff);
2051 if (sum < max) { pixel = i; max = sum; }
2052 }
2053 /*
2054 }
2055 */
88195b2b
JS
2056#endif
2057
069d0f27
VZ
2058 // And this is all to get the 'right' color...
2059 int pixel = scolor.SearchColor( r, g, b );
dbda9e86
JS
2060 XPutPixel( data_image, x, y, pixel );
2061 break;
2062 }
2063 case 15:
2064 {
2065 int pixel = ((r & 0xf8) << 7) | ((g & 0xf8) << 2) | ((b & 0xf8) >> 3);
2066 XPutPixel( data_image, x, y, pixel );
2067 break;
2068 }
2069 case 16:
2070 {
2071 int pixel = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | ((b & 0xf8) >> 3);
2072 XPutPixel( data_image, x, y, pixel );
2073 break;
2074 }
2075 case 32:
2076 case 24:
2077 {
2078 int pixel = 0;
2079 switch (b_o)
2080 {
2081 case RGB: pixel = (r << 16) | (g << 8) | b; break;
2082 case RBG: pixel = (r << 16) | (b << 8) | g; break;
2083 case BRG: pixel = (b << 16) | (r << 8) | g; break;
2084 case BGR: pixel = (b << 16) | (g << 8) | r; break;
2085 case GRB: pixel = (g << 16) | (r << 8) | b; break;
2086 case GBR: pixel = (g << 16) | (b << 8) | r; break;
2087 }
2088 XPutPixel( data_image, x, y, pixel );
2089 }
2090 default: break;
2091 }
b75867a6
RR
2092 } // for
2093 } // for
c7abc967 2094
b75867a6 2095 // Blit picture
c7abc967 2096
b75867a6
RR
2097 XGCValues gcvalues;
2098 gcvalues.foreground = BlackPixel( dpy, DefaultScreen( dpy ) );
2099 GC gc = XCreateGC( dpy, RootWindow ( dpy, DefaultScreen(dpy) ), GCForeground, &gcvalues );
2100 XPutImage( dpy, (Drawable)bitmap.GetPixmap(), gc, data_image, 0, 0, 0, 0, width, height );
c7abc967 2101
b75867a6
RR
2102 XDestroyImage( data_image );
2103 XFreeGC( dpy, gc );
c7abc967 2104
b75867a6 2105 // Blit mask
68be9f09
JS
2106 if (HasMask())
2107 {
2108 wxBitmap maskBitmap(width, height, 1);
c7abc967 2109
68be9f09
JS
2110 GC gcMask = XCreateGC( dpy, (Pixmap) maskBitmap.GetPixmap(), (XtGCMask) 0, (XGCValues*)NULL );
2111 XPutImage( dpy, (Drawable)maskBitmap.GetPixmap(), gcMask, mask_image, 0, 0, 0, 0, width, height );
c7abc967 2112
68be9f09
JS
2113 XDestroyImage( mask_image );
2114 XFreeGC( dpy, gcMask );
c7abc967 2115
68be9f09
JS
2116 wxMask* mask = new wxMask;
2117 mask->SetPixmap(maskBitmap.GetPixmap());
2118
2119 bitmap.SetMask(mask);
2120
2121 maskBitmap.SetPixmapNull();
2122 }
c7abc967 2123
b75867a6 2124 return bitmap;
ee4c6942
JS
2125}
2126
2127wxImage::wxImage( const wxBitmap &bitmap )
2128{
223d09f6 2129 wxCHECK_RET( bitmap.Ok(), wxT("invalid bitmap") );
c7abc967 2130
38274997
RR
2131 Display *dpy = (Display*) wxGetDisplay();
2132 Visual* vis = DefaultVisual( dpy, DefaultScreen( dpy ) );
2133 int bpp = DefaultDepth( dpy, DefaultScreen( dpy ) );
c7abc967 2134
38274997 2135 XImage *ximage = XGetImage( dpy,
dbda9e86
JS
2136 (Drawable)bitmap.GetPixmap(),
2137 0, 0,
2138 bitmap.GetWidth(), bitmap.GetHeight(),
2139 AllPlanes, ZPixmap );
c7abc967 2140
223d09f6 2141 wxCHECK_RET( ximage, wxT("couldn't create image") );
c7abc967 2142
38274997
RR
2143 Create( bitmap.GetWidth(), bitmap.GetHeight() );
2144 char unsigned *data = GetData();
c7abc967 2145
38274997
RR
2146 if (!data)
2147 {
2148 XDestroyImage( ximage );
223d09f6 2149 wxFAIL_MSG( wxT("couldn't create image") );
38274997
RR
2150 return;
2151 }
c7abc967 2152
dbda9e86 2153 /*
38274997
RR
2154 GdkImage *gdk_image_mask = (GdkImage*) NULL;
2155 if (bitmap.GetMask())
2156 {
dbda9e86
JS
2157 gdk_image_mask = gdk_image_get( bitmap.GetMask()->GetBitmap(),
2158 0, 0,
2159 bitmap.GetWidth(), bitmap.GetHeight() );
c7abc967 2160
dbda9e86
JS
2161 SetMaskColour( 16, 16, 16 ); // anything unlikely and dividable
2162 }
2163 */
c7abc967 2164
38274997 2165 // Retrieve depth info
c7abc967 2166
38274997
RR
2167 XVisualInfo vinfo_template;
2168 XVisualInfo *vi;
c7abc967 2169
38274997
RR
2170 vinfo_template.visual = vis;
2171 vinfo_template.visualid = XVisualIDFromVisual( vis );
2172 vinfo_template.depth = bpp;
2173 int nitem = 0;
c7abc967 2174
38274997 2175 vi = XGetVisualInfo( dpy, VisualIDMask|VisualDepthMask, &vinfo_template, &nitem );
c7abc967 2176
223d09f6 2177 wxCHECK_RET( vi, wxT("no visual") );
c7abc967 2178
38274997 2179 if ((bpp == 16) && (vi->red_mask != 0xf800)) bpp = 15;
c7abc967 2180
38274997 2181 XFree( vi );
c7abc967 2182
38274997
RR
2183 XColor colors[256];
2184 if (bpp == 8)
2185 {
dbda9e86 2186 Colormap cmap = (Colormap)wxTheApp->GetMainColormap( dpy );
c7abc967 2187
38274997 2188 for (int i = 0; i < 256; i++) colors[i].pixel = i;
dbda9e86 2189 XQueryColors( dpy, cmap, colors, 256 );
38274997 2190 }
c7abc967 2191
38274997
RR
2192 long pos = 0;
2193 for (int j = 0; j < bitmap.GetHeight(); j++)
2194 {
2195 for (int i = 0; i < bitmap.GetWidth(); i++)
2196 {
dbda9e86 2197 int pixel = XGetPixel( ximage, i, j );
38274997
RR
2198 if (bpp <= 8)
2199 {
2200 data[pos] = colors[pixel].red >> 8;
2201 data[pos+1] = colors[pixel].green >> 8;
2202 data[pos+2] = colors[pixel].blue >> 8;
2203 } else if (bpp == 15)
2204 {
2205 data[pos] = (pixel >> 7) & 0xf8;
2206 data[pos+1] = (pixel >> 2) & 0xf8;
2207 data[pos+2] = (pixel << 3) & 0xf8;
2208 } else if (bpp == 16)
2209 {
2210 data[pos] = (pixel >> 8) & 0xf8;
2211 data[pos+1] = (pixel >> 3) & 0xfc;
2212 data[pos+2] = (pixel << 3) & 0xf8;
2213 } else
2214 {
2215 data[pos] = (pixel >> 16) & 0xff;
2216 data[pos+1] = (pixel >> 8) & 0xff;
2217 data[pos+2] = pixel & 0xff;
2218 }
c7abc967 2219
dbda9e86 2220 /*
38274997
RR
2221 if (gdk_image_mask)
2222 {
dbda9e86
JS
2223 int mask_pixel = gdk_image_get_pixel( gdk_image_mask, i, j );
2224 if (mask_pixel == 0)
2225 {
2226 data[pos] = 16;
2227 data[pos+1] = 16;
2228 data[pos+2] = 16;
38274997 2229 }
dbda9e86
JS
2230 }
2231 */
c7abc967 2232
38274997
RR
2233 pos += 3;
2234 }
2235 }
c7abc967 2236
38274997 2237 XDestroyImage( ximage );
dbda9e86 2238 /*
38274997 2239 if (gdk_image_mask) gdk_image_destroy( gdk_image_mask );
dbda9e86 2240 */
ee4c6942
JS
2241}
2242#endif
a91b47e8 2243
004fd0c8
DW
2244#ifdef __WXPM__
2245// OS/2 Presentation manager conversion routings
2246
2247wxBitmap wxImage::ConvertToBitmap() const
2248{
2249 if ( !Ok() )
2250 return wxNullBitmap;
2251 wxBitmap bitmap; // remove
2252// TODO:
2253/*
2254 int sizeLimit = 1024*768*3;
2255
2256 // width and height of the device-dependent bitmap
2257 int width = GetWidth();
2258 int bmpHeight = GetHeight();
2259
2260 // calc the number of bytes per scanline and padding
2261 int bytePerLine = width*3;
2262 int sizeDWORD = sizeof( DWORD );
2263 int lineBoundary = bytePerLine % sizeDWORD;
2264 int padding = 0;
2265 if( lineBoundary > 0 )
2266 {
2267 padding = sizeDWORD - lineBoundary;
2268 bytePerLine += padding;
2269 }
2270 // calc the number of DIBs and heights of DIBs
2271 int numDIB = 1;
2272 int hRemain = 0;
2273 int height = sizeLimit/bytePerLine;
2274 if( height >= bmpHeight )
2275 height = bmpHeight;
2276 else
2277 {
2278 numDIB = bmpHeight / height;
2279 hRemain = bmpHeight % height;
2280 if( hRemain >0 ) numDIB++;
2281 }
2282
2283 // set bitmap parameters
2284 wxBitmap bitmap;
2285 wxCHECK_MSG( Ok(), bitmap, wxT("invalid image") );
2286 bitmap.SetWidth( width );
2287 bitmap.SetHeight( bmpHeight );
2288 bitmap.SetDepth( wxDisplayDepth() );
2289
2290 // create a DIB header
2291 int headersize = sizeof(BITMAPINFOHEADER);
2292 LPBITMAPINFO lpDIBh = (BITMAPINFO *) malloc( headersize );
2293 wxCHECK_MSG( lpDIBh, bitmap, wxT("could not allocate memory for DIB header") );
2294 // Fill in the DIB header
2295 lpDIBh->bmiHeader.biSize = headersize;
2296 lpDIBh->bmiHeader.biWidth = (DWORD)width;
2297 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
2298 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
2299 // the general formula for biSizeImage:
2300 // ( ( ( ((DWORD)width*24) +31 ) & ~31 ) >> 3 ) * height;
2301 lpDIBh->bmiHeader.biPlanes = 1;
2302 lpDIBh->bmiHeader.biBitCount = 24;
2303 lpDIBh->bmiHeader.biCompression = BI_RGB;
2304 lpDIBh->bmiHeader.biClrUsed = 0;
2305 // These seem not really needed for our purpose here.
2306 lpDIBh->bmiHeader.biClrImportant = 0;
2307 lpDIBh->bmiHeader.biXPelsPerMeter = 0;
2308 lpDIBh->bmiHeader.biYPelsPerMeter = 0;
2309 // memory for DIB data
2310 unsigned char *lpBits;
2311 lpBits = (unsigned char *)malloc( lpDIBh->bmiHeader.biSizeImage );
2312 if( !lpBits )
2313 {
2314 wxFAIL_MSG( wxT("could not allocate memory for DIB") );
2315 free( lpDIBh );
2316 return bitmap;
2317 }
2318
2319 // create and set the device-dependent bitmap
2320 HDC hdc = ::GetDC(NULL);
2321 HDC memdc = ::CreateCompatibleDC( hdc );
2322 HBITMAP hbitmap;
2323 hbitmap = ::CreateCompatibleBitmap( hdc, width, bmpHeight );
2324 ::SelectObject( memdc, hbitmap);
2325
2326 // copy image data into DIB data and then into DDB (in a loop)
2327 unsigned char *data = GetData();
2328 int i, j, n;
2329 int origin = 0;
2330 unsigned char *ptdata = data;
2331 unsigned char *ptbits;
2332
2333 for( n=0; n<numDIB; n++ )
2334 {
2335 if( numDIB > 1 && n == numDIB-1 && hRemain > 0 )
2336 {
2337 // redefine height and size of the (possibly) last smaller DIB
2338 // memory is not reallocated
2339 height = hRemain;
2340 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
2341 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
2342 }
2343 ptbits = lpBits;
2344
2345 for( j=0; j<height; j++ )
2346 {
2347 for( i=0; i<width; i++ )
2348 {
2349 *(ptbits++) = *(ptdata+2);
2350 *(ptbits++) = *(ptdata+1);
2351 *(ptbits++) = *(ptdata );
2352 ptdata += 3;
2353 }
2354 for( i=0; i< padding; i++ ) *(ptbits++) = 0;
2355 }
2356 ::StretchDIBits( memdc, 0, origin, width, height,\
2357 0, 0, width, height, lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
2358 origin += height;
2359 // if numDIB = 1, lines below can also be used
2360 // hbitmap = CreateDIBitmap( hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits, lpDIBh, DIB_RGB_COLORS );
2361 // The above line is equivalent to the following two lines.
2362 // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
2363 // ::SetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS);
2364 // or the following lines
2365 // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
2366 // HDC memdc = ::CreateCompatibleDC( hdc );
2367 // ::SelectObject( memdc, hbitmap);
2368 // ::SetDIBitsToDevice( memdc, 0, 0, width, height,
2369 // 0, 0, 0, height, (void *)lpBits, lpDIBh, DIB_RGB_COLORS);
2370 // ::SelectObject( memdc, 0 );
2371 // ::DeleteDC( memdc );
2372 }
2373 bitmap.SetHBITMAP( (WXHBITMAP) hbitmap );
2374
2375 // similarly, created an mono-bitmap for the possible mask
2376 if( HasMask() )
2377 {
2378 hbitmap = ::CreateBitmap( (WORD)width, (WORD)bmpHeight, 1, 1, NULL );
2379 ::SelectObject( memdc, hbitmap);
2380 if( numDIB == 1 ) height = bmpHeight;
2381 else height = sizeLimit/bytePerLine;
2382 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
2383 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
2384 origin = 0;
2385 unsigned char r = GetMaskRed();
2386 unsigned char g = GetMaskGreen();
2387 unsigned char b = GetMaskBlue();
2388 unsigned char zero = 0, one = 255;
2389 ptdata = data;
2390 for( n=0; n<numDIB; n++ )
2391 {
2392 if( numDIB > 1 && n == numDIB - 1 && hRemain > 0 )
2393 {
2394 // redefine height and size of the (possibly) last smaller DIB
2395 // memory is not reallocated
2396 height = hRemain;
2397 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
2398 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
2399 }
2400 ptbits = lpBits;
2401 for( int j=0; j<height; j++ )
2402 {
2403 for(i=0; i<width; i++ )
2404 {
2405 if( (*(ptdata++)!=r) | (*(ptdata++)!=g) | (*(ptdata++)!=b) )
2406 {
2407 *(ptbits++) = one;
2408 *(ptbits++) = one;
2409 *(ptbits++) = one;
2410 }
2411 else
2412 {
2413 *(ptbits++) = zero;
2414 *(ptbits++) = zero;
2415 *(ptbits++) = zero;
2416 }
2417 }
2418 for( i=0; i< padding; i++ ) *(ptbits++) = zero;
2419 }
2420 ::StretchDIBits( memdc, 0, origin, width, height,\
2421 0, 0, width, height, lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
2422 origin += height;
2423 }
2424 // create a wxMask object
2425 wxMask *mask = new wxMask();
2426 mask->SetMaskBitmap( (WXHBITMAP) hbitmap );
2427 bitmap.SetMask( mask );
2428 }
2429
2430 // free allocated resources
2431 ::SelectObject( memdc, 0 );
2432 ::DeleteDC( memdc );
2433 ::ReleaseDC(NULL, hdc);
2434 free(lpDIBh);
2435 free(lpBits);
2436
2437 // check the wxBitmap object
2438 if( bitmap.GetHBITMAP() )
2439 bitmap.SetOk( TRUE );
2440 else
2441 bitmap.SetOk( FALSE );
2442*/
2443 return bitmap;
2444}
2445
2446wxImage::wxImage( const wxBitmap &bitmap )
2447{
2448 // check the bitmap
2449 if( !bitmap.Ok() )
2450 {
2451 wxFAIL_MSG( wxT("invalid bitmap") );
2452 return;
2453 }
2454
2455 // create an wxImage object
2456 int width = bitmap.GetWidth();
2457 int height = bitmap.GetHeight();
2458 Create( width, height );
2459 unsigned char *data = GetData();
2460 if( !data )
2461 {
2462 wxFAIL_MSG( wxT("could not allocate data for image") );
2463 return;
2464 }
2465
2466 // calc the number of bytes per scanline and padding in the DIB
2467 int bytePerLine = width*3;
2468 int sizeDWORD = sizeof( DWORD );
2469 int lineBoundary = bytePerLine % sizeDWORD;
2470 int padding = 0;
2471 if( lineBoundary > 0 )
2472 {
2473 padding = sizeDWORD - lineBoundary;
2474 bytePerLine += padding;
2475 }
2476// TODO:
2477/*
2478 // create a DIB header
2479 int headersize = sizeof(BITMAPINFOHEADER);
2480 LPBITMAPINFO lpDIBh = (BITMAPINFO *) malloc( headersize );
2481 if( !lpDIBh )
2482 {
2483 wxFAIL_MSG( wxT("could not allocate data for DIB header") );
2484 free( data );
2485 return;
2486 }
2487 // Fill in the DIB header
2488 lpDIBh->bmiHeader.biSize = headersize;
2489 lpDIBh->bmiHeader.biWidth = width;
2490 lpDIBh->bmiHeader.biHeight = -height;
2491 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
2492 lpDIBh->bmiHeader.biPlanes = 1;
2493 lpDIBh->bmiHeader.biBitCount = 24;
2494 lpDIBh->bmiHeader.biCompression = BI_RGB;
2495 lpDIBh->bmiHeader.biClrUsed = 0;
2496 // These seem not really needed for our purpose here.
2497 lpDIBh->bmiHeader.biClrImportant = 0;
2498 lpDIBh->bmiHeader.biXPelsPerMeter = 0;
2499 lpDIBh->bmiHeader.biYPelsPerMeter = 0;
2500 // memory for DIB data
2501 unsigned char *lpBits;
2502 lpBits = (unsigned char *) malloc( lpDIBh->bmiHeader.biSizeImage );
2503 if( !lpBits )
2504 {
2505 wxFAIL_MSG( wxT("could not allocate data for DIB") );
2506 free( data );
2507 free( lpDIBh );
2508 return;
2509 }
2510
2511 // copy data from the device-dependent bitmap to the DIB
2512 HDC hdc = ::GetDC(NULL);
2513 HBITMAP hbitmap;
2514 hbitmap = (HBITMAP) bitmap.GetHBITMAP();
2515 ::GetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS );
2516
2517 // copy DIB data into the wxImage object
2518 int i, j;
2519 unsigned char *ptdata = data;
2520 unsigned char *ptbits = lpBits;
2521 for( i=0; i<height; i++ )
2522 {
2523 for( j=0; j<width; j++ )
2524 {
2525 *(ptdata++) = *(ptbits+2);
2526 *(ptdata++) = *(ptbits+1);
2527 *(ptdata++) = *(ptbits );
2528 ptbits += 3;
2529 }
2530 ptbits += padding;
2531 }
2532
2533 // similarly, set data according to the possible mask bitmap
2534 if( bitmap.GetMask() && bitmap.GetMask()->GetMaskBitmap() )
2535 {
2536 hbitmap = (HBITMAP) bitmap.GetMask()->GetMaskBitmap();
2537 // memory DC created, color set, data copied, and memory DC deleted
2538 HDC memdc = ::CreateCompatibleDC( hdc );
2539 ::SetTextColor( memdc, RGB( 0, 0, 0 ) );
2540 ::SetBkColor( memdc, RGB( 255, 255, 255 ) );
2541 ::GetDIBits( memdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS );
2542 ::DeleteDC( memdc );
2543 // background color set to RGB(16,16,16) in consistent with wxGTK
2544 unsigned char r=16, g=16, b=16;
2545 ptdata = data;
2546 ptbits = lpBits;
2547 for( i=0; i<height; i++ )
2548 {
2549 for( j=0; j<width; j++ )
2550 {
2551 if( *ptbits != 0 )
2552 ptdata += 3;
2553 else
2554 {
2555 *(ptdata++) = r;
2556 *(ptdata++) = g;
2557 *(ptdata++) = b;
2558 }
2559 ptbits += 3;
2560 }
2561 ptbits += padding;
2562 }
2563 SetMaskColour( r, g, b );
2564 SetMask( TRUE );
2565 }
2566 else
2567 {
2568 SetMask( FALSE );
2569 }
2570 // free allocated resources
2571 ::ReleaseDC(NULL, hdc);
2572 free(lpDIBh);
2573 free(lpBits);
2574*/
2575}
2576
2577#endif
2578
a91b47e8
JS
2579// A module to allow wxImage initialization/cleanup
2580// without calling these functions from app.cpp or from
2581// the user's application.
2582
2583class wxImageModule: public wxModule
2584{
2585DECLARE_DYNAMIC_CLASS(wxImageModule)
2586public:
2587 wxImageModule() {}
2588 bool OnInit() { wxImage::InitStandardHandlers(); return TRUE; };
2589 void OnExit() { wxImage::CleanUpHandlers(); };
2590};
2591
2592IMPLEMENT_DYNAMIC_CLASS(wxImageModule, wxModule)
c9d01afd
GRG
2593
2594
2595//-----------------------------------------------------------------------------
2596
89d00456
GRG
2597// GRG, Dic/99
2598// Counts and returns the number of different colours. Optionally stops
cc9f7d79
GRG
2599// when it exceeds 'stopafter' different colours. This is useful, for
2600// example, to see if the image can be saved as 8-bit (256 colour or
2601// less, in this case it would be invoked as CountColours(256)). Default
2602// value for stopafter is -1 (don't care).
89d00456 2603//
cc9f7d79 2604unsigned long wxImage::CountColours( unsigned long stopafter )
89d00456
GRG
2605{
2606 wxHashTable h;
ad30de59 2607 wxObject dummy;
89d00456
GRG
2608 unsigned char r, g, b, *p;
2609 unsigned long size, nentries, key;
2610
2611 p = GetData();
2612 size = GetWidth() * GetHeight();
2613 nentries = 0;
2614
cc9f7d79 2615 for (unsigned long j = 0; (j < size) && (nentries <= stopafter) ; j++)
89d00456
GRG
2616 {
2617 r = *(p++);
2618 g = *(p++);
2619 b = *(p++);
2620 key = (r << 16) | (g << 8) | b;
2621
ad30de59 2622 if (h.Get(key) == NULL)
89d00456 2623 {
ad30de59 2624 h.Put(key, &dummy);
89d00456
GRG
2625 nentries++;
2626 }
2627 }
2628
89d00456
GRG
2629 return nentries;
2630}
2631
2632
c9d01afd
GRG
2633// GRG, Dic/99
2634// Computes the histogram of the image and fills a hash table, indexed
2635// with integer keys built as 0xRRGGBB, containing wxHNode objects. Each
2636// wxHNode contains an 'index' (useful to build a palette with the image
2637// colours) and a 'value', which is the number of pixels in the image with
2638// that colour.
89d00456 2639//
c9d01afd
GRG
2640unsigned long wxImage::ComputeHistogram( wxHashTable &h )
2641{
2642 unsigned char r, g, b, *p;
2643 unsigned long size, nentries, key;
2644 wxHNode *hnode;
2645
2646 p = GetData();
2647 size = GetWidth() * GetHeight();
2648 nentries = 0;
2649
2650 for (unsigned long j = 0; j < size; j++)
2651 {
2652 r = *(p++);
2653 g = *(p++);
2654 b = *(p++);
2655 key = (r << 16) | (g << 8) | b;
2656
2657 hnode = (wxHNode *) h.Get(key);
2658
2659 if (hnode)
2660 hnode->value++;
2661 else
2662 {
2663 hnode = new wxHNode();
97fdfcc9 2664 hnode->index = nentries++;
c9d01afd
GRG
2665 hnode->value = 1;
2666
2667 h.Put(key, (wxObject *)hnode);
2668 }
2669 }
2670
2671 return nentries;
2672}
2673
7a632f10
JS
2674/*
2675 * Rotation code by Carlos Moreno
2676 */
2677
b5c91ac6
GRG
2678// GRG: I've removed wxRotationPoint - we already have wxRealPoint which
2679// does exactly the same thing. And I also got rid of wxRotationPixel
2680// bacause of potential problems in architectures where alignment
2681// is an issue, so I had to rewrite parts of the code.
7a632f10 2682
7a632f10
JS
2683static const double gs_Epsilon = 1e-10;
2684
2685static inline int wxCint (double x)
2686{
2687 return (x > 0) ? (int) (x + 0.5) : (int) (x - 0.5);
2688}
2689
2690
2691// Auxiliary function to rotate a point (x,y) with respect to point p0
2692// make it inline and use a straight return to facilitate optimization
2693// also, the function receives the sine and cosine of the angle to avoid
2694// repeating the time-consuming calls to these functions -- sin/cos can
2695// be computed and stored in the calling function.
2696
b5c91ac6 2697inline wxRealPoint rotated_point (const wxRealPoint & p, double cos_angle, double sin_angle, const wxRealPoint & p0)
7a632f10 2698{
b5c91ac6
GRG
2699 return wxRealPoint (p0.x + (p.x - p0.x) * cos_angle - (p.y - p0.y) * sin_angle,
2700 p0.y + (p.y - p0.y) * cos_angle + (p.x - p0.x) * sin_angle);
7a632f10
JS
2701}
2702
b5c91ac6 2703inline wxRealPoint rotated_point (double x, double y, double cos_angle, double sin_angle, const wxRealPoint & p0)
7a632f10 2704{
b5c91ac6 2705 return rotated_point (wxRealPoint(x,y), cos_angle, sin_angle, p0);
7a632f10
JS
2706}
2707
2708wxImage wxImage::Rotate(double angle, const wxPoint & centre_of_rotation, bool interpolating, wxPoint * offset_after_rotation) const
2709{
7a632f10
JS
2710 int i;
2711 angle = -angle; // screen coordinates are a mirror image of "real" coordinates
2712
ad30de59 2713 // Create pointer-based array to accelerate access to wxImage's data
b5c91ac6 2714 unsigned char ** data = new unsigned char * [GetHeight()];
7a632f10 2715
b5c91ac6 2716 data[0] = GetData();
7a632f10 2717
b5c91ac6
GRG
2718 for (i = 1; i < GetHeight(); i++)
2719 data[i] = data[i - 1] + (3 * GetWidth());
7a632f10 2720
b5c91ac6 2721 // precompute coefficients for rotation formula
ad30de59 2722 // (sine and cosine of the angle)
7a632f10
JS
2723 const double cos_angle = cos(angle);
2724 const double sin_angle = sin(angle);
2725
ad30de59
GRG
2726 // Create new Image to store the result
2727 // First, find rectangle that covers the rotated image; to do that,
2728 // rotate the four corners
7a632f10 2729
b5c91ac6 2730 const wxRealPoint p0(centre_of_rotation.x, centre_of_rotation.y);
7a632f10 2731
b5c91ac6
GRG
2732 wxRealPoint p1 = rotated_point (0, 0, cos_angle, sin_angle, p0);
2733 wxRealPoint p2 = rotated_point (0, GetHeight(), cos_angle, sin_angle, p0);
2734 wxRealPoint p3 = rotated_point (GetWidth(), 0, cos_angle, sin_angle, p0);
2735 wxRealPoint p4 = rotated_point (GetWidth(), GetHeight(), cos_angle, sin_angle, p0);
7a632f10 2736
57c1c6cb
BJ
2737 int x1 = (int) floor (wxMin (wxMin(p1.x, p2.x), wxMin(p3.x, p4.x)));
2738 int y1 = (int) floor (wxMin (wxMin(p1.y, p2.y), wxMin(p3.y, p4.y)));
57c1c6cb
BJ
2739 int x2 = (int) ceil (wxMax (wxMax(p1.x, p2.x), wxMax(p3.x, p4.x)));
2740 int y2 = (int) ceil (wxMax (wxMax(p1.y, p2.y), wxMax(p3.y, p4.y)));
7a632f10
JS
2741
2742 wxImage rotated (x2 - x1 + 1, y2 - y1 + 1);
2743
2744 if (offset_after_rotation != NULL)
2745 {
06b466c7 2746 *offset_after_rotation = wxPoint (x1, y1);
7a632f10
JS
2747 }
2748
b5c91ac6
GRG
2749 // GRG: The rotated (destination) image is always accessed
2750 // sequentially, so there is no need for a pointer-based
2751 // array here (and in fact it would be slower).
2752 //
2753 unsigned char * dst = rotated.GetData();
7a632f10 2754
ad30de59
GRG
2755 // GRG: if the original image has a mask, use its RGB values
2756 // as the blank pixel, else, fall back to default (black).
2757 //
b5c91ac6
GRG
2758 unsigned char blank_r = 0;
2759 unsigned char blank_g = 0;
2760 unsigned char blank_b = 0;
ad30de59
GRG
2761
2762 if (HasMask())
2763 {
b5c91ac6
GRG
2764 blank_r = GetMaskRed();
2765 blank_g = GetMaskGreen();
2766 blank_b = GetMaskBlue();
2767 rotated.SetMaskColour( blank_r, blank_g, blank_b );
ad30de59
GRG
2768 }
2769
2770 // Now, for each point of the rotated image, find where it came from, by
2771 // performing an inverse rotation (a rotation of -angle) and getting the
2772 // pixel at those coordinates
2773
b5c91ac6
GRG
2774 // GRG: I've taken the (interpolating) test out of the loops, so that
2775 // it is done only once, instead of repeating it for each pixel.
7a632f10
JS
2776
2777 int x;
b5c91ac6 2778 if (interpolating)
7a632f10
JS
2779 {
2780 for (int y = 0; y < rotated.GetHeight(); y++)
2781 {
b5c91ac6 2782 for (x = 0; x < rotated.GetWidth(); x++)
7a632f10 2783 {
b5c91ac6
GRG
2784 wxRealPoint src = rotated_point (x + x1, y + y1, cos_angle, -sin_angle, p0);
2785
f2506310
JS
2786 if (-0.25 < src.x && src.x < GetWidth() - 0.75 &&
2787 -0.25 < src.y && src.y < GetHeight() - 0.75)
7a632f10 2788 {
ad30de59
GRG
2789 // interpolate using the 4 enclosing grid-points. Those
2790 // points can be obtained using floor and ceiling of the
2791 // exact coordinates of the point
f2506310 2792 // C.M. 2000-02-17: when the point is near the border, special care is required.
7a632f10 2793
f2506310
JS
2794 int x1, y1, x2, y2;
2795
2796 if (0 < src.x && src.x < GetWidth() - 1)
2797 {
2798 x1 = wxCint(floor(src.x));
2799 x2 = wxCint(ceil(src.x));
2800 }
2801 else // else means that x is near one of the borders (0 or width-1)
2802 {
2803 x1 = x2 = wxCint (src.x);
2804 }
2805
2806 if (0 < src.y && src.y < GetHeight() - 1)
2807 {
2808 y1 = wxCint(floor(src.y));
2809 y2 = wxCint(ceil(src.y));
2810 }
2811 else
2812 {
2813 y1 = y2 = wxCint (src.y);
2814 }
7a632f10 2815
ad30de59
GRG
2816 // get four points and the distances (square of the distance,
2817 // for efficiency reasons) for the interpolation formula
b5c91ac6
GRG
2818
2819 // GRG: Do not calculate the points until they are
2820 // really needed -- this way we can calculate
2821 // just one, instead of four, if d1, d2, d3
2822 // or d4 are < gs_Epsilon
7a632f10
JS
2823
2824 const double d1 = (src.x - x1) * (src.x - x1) + (src.y - y1) * (src.y - y1);
2825 const double d2 = (src.x - x2) * (src.x - x2) + (src.y - y1) * (src.y - y1);
2826 const double d3 = (src.x - x2) * (src.x - x2) + (src.y - y2) * (src.y - y2);
2827 const double d4 = (src.x - x1) * (src.x - x1) + (src.y - y2) * (src.y - y2);
2828
ad30de59
GRG
2829 // Now interpolate as a weighted average of the four surrounding
2830 // points, where the weights are the distances to each of those points
7a632f10 2831
ad30de59
GRG
2832 // If the point is exactly at one point of the grid of the source
2833 // image, then don't interpolate -- just assign the pixel
7a632f10 2834
06b466c7 2835 if (d1 < gs_Epsilon) // d1,d2,d3,d4 are positive -- no need for abs()
7a632f10 2836 {
b5c91ac6
GRG
2837 unsigned char *p = data[y1] + (3 * x1);
2838 *(dst++) = *(p++);
2839 *(dst++) = *(p++);
2840 *(dst++) = *(p++);
7a632f10
JS
2841 }
2842 else if (d2 < gs_Epsilon)
2843 {
b5c91ac6
GRG
2844 unsigned char *p = data[y1] + (3 * x2);
2845 *(dst++) = *(p++);
2846 *(dst++) = *(p++);
2847 *(dst++) = *(p++);
7a632f10
JS
2848 }
2849 else if (d3 < gs_Epsilon)
2850 {
b5c91ac6
GRG
2851 unsigned char *p = data[y2] + (3 * x2);
2852 *(dst++) = *(p++);
2853 *(dst++) = *(p++);
2854 *(dst++) = *(p++);
7a632f10
JS
2855 }
2856 else if (d4 < gs_Epsilon)
2857 {
b5c91ac6
GRG
2858 unsigned char *p = data[y2] + (3 * x1);
2859 *(dst++) = *(p++);
2860 *(dst++) = *(p++);
2861 *(dst++) = *(p++);
7a632f10
JS
2862 }
2863 else
2864 {
06b466c7 2865 // weights for the weighted average are proportional to the inverse of the distance
b5c91ac6
GRG
2866 unsigned char *v1 = data[y1] + (3 * x1);
2867 unsigned char *v2 = data[y1] + (3 * x2);
2868 unsigned char *v3 = data[y2] + (3 * x2);
2869 unsigned char *v4 = data[y2] + (3 * x1);
2870
06b466c7
VZ
2871 const double w1 = 1/d1, w2 = 1/d2, w3 = 1/d3, w4 = 1/d4;
2872
b5c91ac6
GRG
2873 // GRG: Unrolled.
2874
2875 *(dst++) = (unsigned char)
2876 ( (w1 * *(v1++) + w2 * *(v2++) +
2877 w3 * *(v3++) + w4 * *(v4++)) /
2878 (w1 + w2 + w3 + w4) );
2879 *(dst++) = (unsigned char)
2880 ( (w1 * *(v1++) + w2 * *(v2++) +
2881 w3 * *(v3++) + w4 * *(v4++)) /
2882 (w1 + w2 + w3 + w4) );
2883 *(dst++) = (unsigned char)
2884 ( (w1 * *(v1++) + w2 * *(v2++) +
2885 w3 * *(v3++) + w4 * *(v4++)) /
2886 (w1 + w2 + w3 + w4) );
7a632f10
JS
2887 }
2888 }
2889 else
2890 {
b5c91ac6
GRG
2891 *(dst++) = blank_r;
2892 *(dst++) = blank_g;
2893 *(dst++) = blank_b;
7a632f10
JS
2894 }
2895 }
b5c91ac6
GRG
2896 }
2897 }
2898 else // not interpolating
2899 {
2900 for (int y = 0; y < rotated.GetHeight(); y++)
2901 {
2902 for (x = 0; x < rotated.GetWidth(); x++)
7a632f10 2903 {
b5c91ac6
GRG
2904 wxRealPoint src = rotated_point (x + x1, y + y1, cos_angle, -sin_angle, p0);
2905
2906 const int xs = wxCint (src.x); // wxCint rounds to the
457e6c54 2907 const int ys = wxCint (src.y); // closest integer
7a632f10 2908
b5c91ac6
GRG
2909 if (0 <= xs && xs < GetWidth() &&
2910 0 <= ys && ys < GetHeight())
7a632f10 2911 {
b5c91ac6
GRG
2912 unsigned char *p = data[ys] + (3 * xs);
2913 *(dst++) = *(p++);
2914 *(dst++) = *(p++);
2915 *(dst++) = *(p++);
7a632f10
JS
2916 }
2917 else
2918 {
b5c91ac6
GRG
2919 *(dst++) = blank_r;
2920 *(dst++) = blank_g;
2921 *(dst++) = blank_b;
7a632f10
JS
2922 }
2923 }
2924 }
2925 }
2926
4aff28fc 2927 delete [] data;
4aff28fc 2928
7a632f10
JS
2929 return rotated;
2930}
c9d01afd 2931