]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/common/image.cpp
Added CodeWarrior files to distribution scripts
[wxWidgets.git] / src / common / image.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: image.cpp
3// Purpose: wxImage
4// Author: Robert Roebling
5// RCS-ID: $Id$
6// Copyright: (c) Robert Roebling
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10#ifdef __GNUG__
11#pragma implementation "image.h"
12#endif
13
14// For compilers that support precompilation, includes "wx.h".
15#include "wx/wxprec.h"
16
17#ifdef __BORLANDC__
18#pragma hdrstop
19#endif
20
21#include "wx/image.h"
22#include "wx/bitmap.h"
23#include "wx/debug.h"
24#include "wx/log.h"
25#include "../png/png.h"
26#include "wx/filefn.h"
27
28#ifdef __WXGTK__
29#include "gdk/gdkprivate.h"
30#include "gdk/gdkx.h"
31#endif
32
33//-----------------------------------------------------------------------------
34// wxImage
35//-----------------------------------------------------------------------------
36
37class wxImageRefData: public wxObjectRefData
38{
39
40public:
41 wxImageRefData(void);
42 ~wxImageRefData(void);
43
44 int m_width;
45 int m_height;
46 unsigned char *m_data;
47 bool m_hasMask;
48 unsigned char m_maskRed,m_maskGreen,m_maskBlue;
49 bool m_ok;
50};
51
52wxImageRefData::wxImageRefData(void)
53{
54 m_width = 0;
55 m_height = 0;
56 m_data = (unsigned char*) NULL;
57 m_ok = FALSE;
58 m_maskRed = 0;
59 m_maskGreen = 0;
60 m_maskBlue = 0;
61 m_hasMask = FALSE;
62}
63
64wxImageRefData::~wxImageRefData(void)
65{
66 if (m_data) free( m_data );
67}
68
69wxList wxImage::sm_handlers;
70
71//-----------------------------------------------------------------------------
72
73#define M_IMGDATA ((wxImageRefData *)m_refData)
74
75#if !USE_SHARED_LIBRARIES
76IMPLEMENT_DYNAMIC_CLASS(wxImage, wxObject)
77#endif
78
79wxImage::wxImage()
80{
81}
82
83wxImage::wxImage( int width, int height )
84{
85 Create( width, height );
86}
87
88wxImage::wxImage( const wxString& name, long type )
89{
90 LoadFile( name, type );
91}
92
93wxImage::wxImage( const wxImage& image )
94{
95 Ref(image);
96}
97
98wxImage::wxImage( const wxImage* image )
99{
100 if (image) Ref(*image);
101}
102
103void wxImage::Create( int width, int height )
104{
105 m_refData = new wxImageRefData();
106
107 M_IMGDATA->m_data = (unsigned char *) malloc( width*height*3 );
108 if (M_IMGDATA->m_data)
109 {
110 for (int l = 0; l < width*height*3; l++) M_IMGDATA->m_data[l] = 0;
111
112 M_IMGDATA->m_width = width;
113 M_IMGDATA->m_height = height;
114 M_IMGDATA->m_ok = TRUE;
115 }
116 else
117 {
118 UnRef();
119 }
120}
121
122void wxImage::Destroy()
123{
124 UnRef();
125}
126
127wxImage wxImage::Scale( int width, int height )
128{
129 wxImage image;
130
131 wxCHECK_MSG( Ok(), image, "invlaid image" );
132
133 wxCHECK_MSG( (width > 0) && (height > 0), image, "invalid image size" );
134
135 image.Create( width, height );
136
137 char unsigned *data = image.GetData();
138
139 wxCHECK_MSG( data, image, "unable to create image" );
140
141 if (M_IMGDATA->m_hasMask)
142 image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
143
144 double xscale = (double)width / (double)M_IMGDATA->m_width;
145 double yscale = (double)height / (double)M_IMGDATA->m_height;
146
147 for (int j = 0; j < height; j++)
148 {
149 for (int i = 0; i < width; i++)
150 {
151 int new_pos = 3*(j*width + i);
152 int old_pos = 3*((long)(j/yscale)*M_IMGDATA->m_width + (long)(i/xscale));
153 data[ new_pos ] = M_IMGDATA->m_data[ old_pos ];
154 data[ new_pos+1 ] = M_IMGDATA->m_data[ old_pos+1 ];
155 data[ new_pos+2 ] = M_IMGDATA->m_data[ old_pos+2 ];
156 }
157 }
158
159 return image;
160}
161
162void wxImage::SetRGB( int x, int y, unsigned char r, unsigned char g, unsigned char b )
163{
164 wxCHECK_RET( Ok(), "invalid image" );
165
166 int w = M_IMGDATA->m_width;
167 int h = M_IMGDATA->m_height;
168
169 wxCHECK_RET( (x>=0) && (y>=0) && (x<w) && (y<h), "invalid image index" );
170
171 long pos = (y * w + x) * 3;
172
173 M_IMGDATA->m_data[ pos ] = r;
174 M_IMGDATA->m_data[ pos+1 ] = g;
175 M_IMGDATA->m_data[ pos+2 ] = b;
176}
177
178unsigned char wxImage::GetRed( int x, int y )
179{
180 wxCHECK_MSG( Ok(), 0, "invalid image" );
181
182 int w = M_IMGDATA->m_width;
183 int h = M_IMGDATA->m_height;
184
185 wxCHECK_MSG( (x>=0) && (y>=0) && (x<w) && (y<h), 0, "invalid image index" );
186
187 long pos = (y * w + x) * 3;
188
189 return M_IMGDATA->m_data[pos];
190}
191
192unsigned char wxImage::GetGreen( int x, int y )
193{
194 wxCHECK_MSG( Ok(), 0, "invalid image" );
195
196 int w = M_IMGDATA->m_width;
197 int h = M_IMGDATA->m_height;
198
199 wxCHECK_MSG( (x>=0) && (y>=0) && (x<w) && (y<h), 0, "invalid image index" );
200
201 long pos = (y * w + x) * 3;
202
203 return M_IMGDATA->m_data[pos+1];
204}
205
206unsigned char wxImage::GetBlue( int x, int y )
207{
208 wxCHECK_MSG( Ok(), 0, "invalid image" );
209
210 int w = M_IMGDATA->m_width;
211 int h = M_IMGDATA->m_height;
212
213 wxCHECK_MSG( (x>=0) && (y>=0) && (x<w) && (y<h), 0, "invalid image index" );
214
215 long pos = (y * w + x) * 3;
216
217 return M_IMGDATA->m_data[pos+2];
218}
219
220bool wxImage::Ok() const
221{
222 return (M_IMGDATA && M_IMGDATA->m_ok);
223}
224
225char unsigned *wxImage::GetData() const
226{
227 wxCHECK_MSG( Ok(), (char unsigned *)NULL, "invalid image" );
228
229 return M_IMGDATA->m_data;
230}
231
232void wxImage::SetData( char unsigned *WXUNUSED(data) )
233{
234 wxCHECK_RET( Ok(), "invalid image" );
235}
236
237void wxImage::SetMaskColour( unsigned char r, unsigned char g, unsigned char b )
238{
239 wxCHECK_RET( Ok(), "invalid image" );
240
241 M_IMGDATA->m_maskRed = r;
242 M_IMGDATA->m_maskGreen = g;
243 M_IMGDATA->m_maskBlue = b;
244 M_IMGDATA->m_hasMask = TRUE;
245}
246
247unsigned char wxImage::GetMaskRed() const
248{
249 wxCHECK_MSG( Ok(), 0, "invalid image" );
250
251 return M_IMGDATA->m_maskRed;
252}
253
254unsigned char wxImage::GetMaskGreen() const
255{
256 wxCHECK_MSG( Ok(), 0, "invalid image" );
257
258 return M_IMGDATA->m_maskGreen;
259}
260
261unsigned char wxImage::GetMaskBlue() const
262{
263 wxCHECK_MSG( Ok(), 0, "invalid image" );
264
265 return M_IMGDATA->m_maskBlue;
266}
267
268void wxImage::SetMask( bool mask )
269{
270 wxCHECK_RET( Ok(), "invalid image" );
271
272 M_IMGDATA->m_hasMask = mask;
273}
274
275bool wxImage::HasMask() const
276{
277 wxCHECK_MSG( Ok(), FALSE, "invalid image" );
278
279 return M_IMGDATA->m_hasMask;
280}
281
282int wxImage::GetWidth() const
283{
284 wxCHECK_MSG( Ok(), 0, "invalid image" );
285
286 return M_IMGDATA->m_width;
287}
288
289int wxImage::GetHeight() const
290{
291 wxCHECK_MSG( Ok(), 0, "invalid image" );
292
293 return M_IMGDATA->m_height;
294}
295
296bool wxImage::LoadFile( const wxString& filename, long type )
297{
298 UnRef();
299
300 if (!wxFileExists(filename))
301 {
302 wxLogWarning( "Image file does not exist." );
303
304 return FALSE;
305 }
306
307 m_refData = new wxImageRefData;
308
309 wxImageHandler *handler = FindHandler(type);
310
311 if (handler == NULL)
312 {
313 wxLogWarning( "No image handler for type %d defined.", type );
314
315 return FALSE;
316 }
317
318 return handler->LoadFile( this, filename );
319}
320
321bool wxImage::SaveFile( const wxString& filename, int type )
322{
323 wxCHECK_MSG( Ok(), FALSE, "invalid image" );
324
325 wxImageHandler *handler = FindHandler(type);
326
327 if (handler == NULL)
328 {
329 wxLogWarning( "No image handler for type %d defined.", type );
330
331 return FALSE;
332 }
333
334 return handler->SaveFile( this, filename );
335}
336
337void wxImage::AddHandler( wxImageHandler *handler )
338{
339 sm_handlers.Append( handler );
340}
341
342void wxImage::InsertHandler( wxImageHandler *handler )
343{
344 sm_handlers.Insert( handler );
345}
346
347bool wxImage::RemoveHandler( const wxString& name )
348{
349 wxImageHandler *handler = FindHandler(name);
350 if (handler)
351 {
352 sm_handlers.DeleteObject(handler);
353 return TRUE;
354 }
355 else
356 return FALSE;
357}
358
359wxImageHandler *wxImage::FindHandler( const wxString& name )
360{
361 wxNode *node = sm_handlers.First();
362 while (node)
363 {
364 wxImageHandler *handler = (wxImageHandler*)node->Data();
365 if (handler->GetName() == name) return handler;
366 node = node->Next();
367 }
368 return (wxImageHandler *)NULL;
369}
370
371wxImageHandler *wxImage::FindHandler( const wxString& extension, long bitmapType )
372{
373 wxNode *node = sm_handlers.First();
374 while (node)
375 {
376 wxImageHandler *handler = (wxImageHandler*)node->Data();
377 if ( handler->GetExtension() == extension &&
378 (bitmapType == -1 || handler->GetType() == bitmapType) )
379 return handler;
380 node = node->Next();
381 }
382 return (wxImageHandler*)NULL;
383}
384
385wxImageHandler *wxImage::FindHandler( long bitmapType )
386{
387 wxNode *node = sm_handlers.First();
388 while (node)
389 {
390 wxImageHandler *handler = (wxImageHandler *)node->Data();
391 if (handler->GetType() == bitmapType) return handler;
392 node = node->Next();
393 }
394 return NULL;
395}
396
397void wxImage::InitStandardHandlers()
398{
399 AddHandler( new wxBMPHandler );
400 AddHandler( new wxPNGHandler );
401}
402
403void wxImage::CleanUpHandlers()
404{
405 wxNode *node = sm_handlers.First();
406 while (node)
407 {
408 wxImageHandler *handler = (wxImageHandler *)node->Data();
409 wxNode *next = node->Next();
410 delete handler;
411 delete node;
412 node = next;
413 }
414}
415
416//-----------------------------------------------------------------------------
417// wxImageHandler
418//-----------------------------------------------------------------------------
419
420#if !USE_SHARED_LIBRARIES
421IMPLEMENT_DYNAMIC_CLASS(wxImageHandler,wxObject)
422#endif
423
424bool wxImageHandler::LoadFile( wxImage *WXUNUSED(image), const wxString& WXUNUSED(name) )
425{
426 return FALSE;
427}
428
429bool wxImageHandler::SaveFile( wxImage *WXUNUSED(image), const wxString& WXUNUSED(name) )
430{
431 return FALSE;
432}
433
434//-----------------------------------------------------------------------------
435// wxPNGHandler
436//-----------------------------------------------------------------------------
437
438#if !USE_SHARED_LIBRARIES
439IMPLEMENT_DYNAMIC_CLASS(wxPNGHandler,wxImageHandler)
440#endif
441
442bool wxPNGHandler::LoadFile( wxImage *image, const wxString& name )
443{
444 FILE *f;
445 png_structp png_ptr;
446 png_infop info_ptr;
447 unsigned char *ptr, **lines, *ptr2;
448 int transp,bit_depth,color_type,interlace_type;
449 png_uint_32 width, height;
450 unsigned int i;
451
452 image->Destroy();
453
454 transp = 0;
455 png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
456 if (!png_ptr) return FALSE;
457
458 info_ptr = png_create_info_struct( png_ptr );
459 if (!info_ptr)
460 {
461 png_destroy_read_struct( &png_ptr, NULL, NULL );
462 return FALSE;
463 }
464
465 if (setjmp(png_ptr->jmpbuf))
466 {
467 png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
468 return FALSE;
469 }
470
471 if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
472 {
473 png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
474 return FALSE;
475 }
476
477 f = fopen( name, "rb" );
478 png_init_io( png_ptr, f );
479
480 png_read_info( png_ptr, info_ptr );
481 png_get_IHDR( png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL );
482
483 if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand( png_ptr );
484
485 png_set_strip_16( png_ptr );
486 png_set_packing( png_ptr );
487 if (png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand( png_ptr );
488 png_set_filler( png_ptr, 0xff, PNG_FILLER_AFTER );
489
490 image->Create( width, height );
491
492 if (!image->Ok())
493 {
494 png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
495 return FALSE;
496 }
497
498 lines = (unsigned char **)malloc( height * sizeof(unsigned char *) );
499 if (lines == NULL)
500 {
501 image->Destroy();
502 png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
503 return FALSE;
504 }
505
506 for (i = 0; i < height; i++)
507 {
508 if ((lines[i] = (unsigned char *)malloc(width * (sizeof(unsigned char) * 4))) == NULL)
509 {
510 image->Destroy();
511 for (unsigned int n = 0; n < i; n++) free( lines[n] );
512 free( lines );
513 png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
514 return FALSE;
515 }
516 }
517
518 png_read_image( png_ptr, lines );
519 png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
520 ptr = image->GetData();
521 if ((color_type == PNG_COLOR_TYPE_GRAY) ||
522 (color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
523 {
524 for (unsigned int y = 0; y < height; y++)
525 {
526 ptr2 = lines[y];
527 for (unsigned int x = 0; x < width; x++)
528 {
529 unsigned char r = *ptr2++;
530 unsigned char a = *ptr2++;
531 if (a < 128)
532 {
533 *ptr++ = 255;
534 *ptr++ = 0;
535 *ptr++ = 255;
536 transp = 1;
537 }
538 else
539 {
540 *ptr++ = r;
541 *ptr++ = r;
542 *ptr++ = r;
543 }
544 }
545 }
546 }
547 else
548 {
549 for (unsigned int y = 0; y < height; y++)
550 {
551 ptr2 = lines[y];
552 for (unsigned int x = 0; x < width; x++)
553 {
554 unsigned char r = *ptr2++;
555 unsigned char g = *ptr2++;
556 unsigned char b = *ptr2++;
557 unsigned char a = *ptr2++;
558 if (a < 128)
559 {
560 *ptr++ = 255;
561 *ptr++ = 0;
562 *ptr++ = 255;
563 transp = 1;
564 }
565 else
566 {
567 if ((r == 255) && (g == 0) && (b == 255)) r = 254;
568 *ptr++ = r;
569 *ptr++ = g;
570 *ptr++ = b;
571 }
572 }
573 }
574 }
575 for (i = 0; i < height; i++) free( lines[i] );
576 free( lines );
577 if (transp)
578 image->SetMaskColour( 255, 0, 255 );
579 else
580 image->SetMask( FALSE );
581
582 return TRUE;
583}
584
585
586bool wxPNGHandler::SaveFile( wxImage *image, const wxString& name )
587{
588 FILE *f = fopen( name, "wb" );
589 if (f)
590 {
591 png_structp png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
592 if (!png_ptr)
593 {
594 fclose( f );
595 return FALSE;
596 }
597
598 png_infop info_ptr = png_create_info_struct(png_ptr);
599 if (info_ptr == NULL)
600 {
601 fclose(f);
602 png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
603 return FALSE;
604 }
605
606 if (setjmp(png_ptr->jmpbuf))
607 {
608 fclose( f );
609 png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
610 return FALSE;
611 }
612
613 png_init_io( png_ptr, f );
614 png_set_IHDR( png_ptr, info_ptr, image->GetWidth(), image->GetHeight(), 8,
615 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
616 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
617
618 png_color_8 sig_bit;
619 sig_bit.red = 8;
620 sig_bit.green = 8;
621 sig_bit.blue = 8;
622 sig_bit.alpha = 8;
623 png_set_sBIT( png_ptr, info_ptr, &sig_bit );
624 png_write_info( png_ptr, info_ptr );
625 png_set_shift( png_ptr, &sig_bit );
626 png_set_packing( png_ptr );
627
628 unsigned char *data = (unsigned char *)malloc( image->GetWidth()*4 );
629 if (!data)
630 {
631 fclose( f );
632 png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
633 return FALSE;
634 }
635
636 for (int y = 0; y < image->GetHeight(); y++)
637 {
638 unsigned char *ptr = image->GetData() + (y * image->GetWidth() * 3);
639 for (int x = 0; x < image->GetWidth(); x++)
640 {
641 data[(x << 2) + 0] = *ptr++;
642 data[(x << 2) + 1] = *ptr++;
643 data[(x << 2) + 2] = *ptr++;
644 if ((data[(x << 2) + 0] == image->GetMaskRed()) &&
645 (data[(x << 2) + 1] == image->GetMaskGreen()) &&
646 (data[(x << 2) + 2] == image->GetMaskBlue()))
647 data[(x << 2) + 3] = 0;
648 else
649 data[(x << 2) + 3] = 255;
650 }
651 png_bytep row_ptr = data;
652 png_write_rows( png_ptr, &row_ptr, 1 );
653 }
654 free(data);
655 png_write_end( png_ptr, info_ptr );
656 png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
657
658 fclose(f);
659 }
660 return TRUE;
661}
662
663//-----------------------------------------------------------------------------
664// wxBMPHandler
665//-----------------------------------------------------------------------------
666
667#if !USE_SHARED_LIBRARIES
668IMPLEMENT_DYNAMIC_CLASS(wxBMPHandler,wxImageHandler)
669#endif
670
671bool wxBMPHandler::LoadFile( wxImage *image, const wxString& name )
672{
673 FILE *file;
674 unsigned char *data, *ptr;
675 int done, i, bpp, planes, comp, ncolors, line, column,
676 linesize, linepos, rshift = 0, gshift = 0, bshift = 0;
677 unsigned char aByte;
678 short int word;
679 long int dbuf[4], dword, rmask = 0, gmask = 0, bmask = 0, offset,
680 size;
681 signed char bbuf[4];
682 struct _cmap
683 {
684 unsigned char r, g, b;
685 }
686 *cmap = NULL;
687#ifndef BI_RGB
688#define BI_RGB 0
689#define BI_RLE8 1
690#define BI_RLE4 2
691#define BI_BITFIELDS 3
692#endif
693
694 image->Destroy();
695
696 file = fopen(name, "r");
697 if (!file)
698 return NULL;
699
700 done = 0;
701 /*
702 * Reading the bmp header
703 */
704
705 fread(&bbuf, 1, 2, file);
706
707 fread(dbuf, 4, 4, file);
708
709 size = dbuf[0];
710 offset = dbuf[2];
711
712 fread(dbuf, 4, 2, file);
713 int width = (int)dbuf[0];
714 int height = (int)dbuf[1];
715 if (width > 32767)
716 {
717 fprintf(stderr, "IMLIB ERROR: Image width > 32767 pixels for file\n");
718 fclose(file);
719 return FALSE;
720 }
721 if (height > 32767)
722 {
723 fprintf(stderr, "IMLIB ERROR: Image height > 32767 pixels for file\n");
724 fclose(file);
725 return FALSE;
726 }
727 fread(&word, 2, 1, file);
728 planes = (int)word;
729 fread(&word, 2, 1, file);
730 bpp = (int)word;
731 if (bpp != 1 && bpp != 4 && bpp != 8 && bpp && 16 && bpp != 24 && bpp != 32)
732 {
733 fprintf(stderr, "IMLIB ERROR: unknown bitdepth in file\n");
734 fclose(file);
735 return FALSE;
736 }
737 fread(dbuf, 4, 4, file);
738 comp = (int)dbuf[0];
739 if (comp != BI_RGB && comp != BI_RLE4 && comp != BI_RLE8 && comp != BI_BITFIELDS)
740 {
741 fprintf(stderr, "IMLIB ERROR: unknown encoding in Windows BMP file\n");
742 fclose(file);
743 return FALSE;
744 }
745 fread(dbuf, 4, 2, file);
746 ncolors = (int)dbuf[0];
747 if (ncolors == 0)
748 ncolors = 1 << bpp;
749 /* some more sanity checks */
750 if (((comp == BI_RLE4) && (bpp != 4)) || ((comp == BI_RLE8) && (bpp != 8)) || ((comp == BI_BITFIELDS) && (bpp != 16 && bpp != 32)))
751 {
752 fprintf(stderr, "IMLIB ERROR: encoding of BMP doesn't match bitdepth\n");
753 fclose(file);
754 return FALSE;
755 }
756 if (bpp < 16)
757 {
758 cmap = (struct _cmap *)malloc(sizeof(struct _cmap) * ncolors);
759
760 if (!cmap)
761 {
762 fprintf(stderr, "IMLIB ERROR: Cannot allocate RAM for color map in BMP file\n");
763 fclose(file);
764 return FALSE;
765 }
766 }
767 else
768 cmap = NULL;
769
770 image->Create( width, height );
771 ptr = image->GetData();
772 if (!ptr)
773 {
774 fprintf(stderr, "IMLIB ERROR: Cannot allocate RAM for RGB data in file\n");
775 fclose(file);
776 if (cmap)
777 free(cmap);
778 return FALSE;
779 }
780
781 /*
782 * Reading the palette, if it exists.
783 */
784 if (bpp < 16 && ncolors != 0)
785 {
786 for (i = 0; i < ncolors; i++)
787 {
788 fread(bbuf, 1, 4, file);
789 cmap[i].b = bbuf[0];
790 cmap[i].g = bbuf[1];
791 cmap[i].r = bbuf[2];
792 }
793 }
794 else if (bpp == 16 || bpp == 32)
795 {
796 if (comp == BI_BITFIELDS)
797 {
798 int bit = 0;
799
800 fread(dbuf, 4, 3, file);
801 bmask = dbuf[0];
802 gmask = dbuf[1];
803 rmask = dbuf[2];
804 /* find shift amount.. ugly, but i can't think of a better way */
805 for (bit = 0; bit < bpp; bit++)
806 {
807 if (bmask & (1 << bit))
808 bshift = bit;
809 if (gmask & (1 << bit))
810 gshift = bit;
811 if (rmask & (1 << bit))
812 rshift = bit;
813 }
814 }
815 else if (bpp == 16)
816 {
817 rmask = 0x7C00;
818 gmask = 0x03E0;
819 bmask = 0x001F;
820 rshift = 10;
821 gshift = 5;
822 bshift = 0;
823 }
824 else if (bpp == 32)
825 {
826 rmask = 0x00FF0000;
827 gmask = 0x0000FF00;
828 bmask = 0x000000FF;
829 rshift = 16;
830 gshift = 8;
831 bshift = 0;
832 }
833 }
834
835 /*
836 * REading the image data
837 */
838 fseek(file, offset, SEEK_SET);
839 data = ptr;
840
841 /* set the whole image to the background color */
842 if (bpp < 16 && (comp == BI_RLE4 || comp == BI_RLE8))
843 {
844 for (i = 0; i < width * height; i++)
845 {
846 *ptr++ = cmap[0].r;
847 *ptr++ = cmap[0].g;
848 *ptr++ = cmap[0].b;
849 }
850 ptr = data;
851 }
852 line = 0;
853 column = 0;
854#define poffset (line * width * 3 + column * 3)
855
856 /*
857 * BMPs are stored upside down... hmmmmmmmmmm....
858 */
859
860 linesize = ((width * bpp + 31) / 32) * 4;
861 for (line = (height - 1); line >= 0; line--)
862 {
863 linepos = 0;
864 for (column = 0; column < width;)
865 {
866 if (bpp < 16)
867 {
868 int index;
869
870 linepos++;
871 aByte = getc(file);
872 if (bpp == 1)
873 {
874 int bit = 0;
875
876 for (bit = 0; bit < 8; bit++)
877 {
878 index = ((aByte & (0x80 >> bit)) ? 1 : 0);
879 ptr[poffset] = cmap[index].r;
880 ptr[poffset + 1] = cmap[index].g;
881 ptr[poffset + 2] = cmap[index].b;
882 column++;
883 }
884 }
885 else if (bpp == 4)
886 {
887 if (comp == BI_RLE4)
888 {
889 fprintf(stderr, "can't deal with 4bit encoded yet.\n");
890 image->Destroy();
891 free(cmap);
892 return FALSE;
893 }
894 else
895 {
896 int nibble = 0;
897
898 for (nibble = 0; nibble < 2; nibble++)
899 {
900 index = ((aByte & (0xF0 >> nibble * 4)) >> (!nibble * 4));
901 if (index >= 16)
902 index = 15;
903 ptr[poffset] = cmap[index].r;
904 ptr[poffset + 1] = cmap[index].g;
905 ptr[poffset + 2] = cmap[index].b;
906 column++;
907 }
908 }
909 }
910 else if (bpp == 8)
911 {
912 if (comp == BI_RLE8)
913 {
914 unsigned char first;
915
916 first = aByte;
917 aByte = getc(file);
918 if (first == 0)
919 {
920 if (aByte == 0)
921 {
922/* column = width; */
923 }
924 else if (aByte == 1)
925 {
926 column = width;
927 line = -1;
928 }
929 else if (aByte == 2)
930 {
931 aByte = getc(file);
932 column += aByte;
933 linepos = column * bpp / 8;
934 aByte = getc(file);
935 line += aByte;
936 }
937 else
938 {
939 int absolute = aByte;
940
941 for (i = 0; i < absolute; i++)
942 {
943 linepos++;
944 aByte = getc(file);
945 ptr[poffset] = cmap[aByte].r;
946 ptr[poffset + 1] = cmap[aByte].g;
947 ptr[poffset + 2] = cmap[aByte].b;
948 column++;
949 }
950 if (absolute & 0x01)
951 aByte = getc(file);
952 }
953 }
954 else
955 {
956 for (i = 0; i < first; i++)
957 {
958 ptr[poffset] = cmap[aByte].r;
959 ptr[poffset + 1] = cmap[aByte].g;
960 ptr[poffset + 2] = cmap[aByte].b;
961 column++;
962 linepos++;
963 }
964 }
965 }
966 else
967 {
968 ptr[poffset] = cmap[aByte].r;
969 ptr[poffset + 1] = cmap[aByte].g;
970 ptr[poffset + 2] = cmap[aByte].b;
971 column++;
972 linepos += size;
973 }
974 }
975 }
976 else if (bpp == 24)
977 {
978 linepos += fread(&bbuf, 1, 3, file);
979 ptr[poffset] = (unsigned char)bbuf[2];
980 ptr[poffset + 1] = (unsigned char)bbuf[1];
981 ptr[poffset + 2] = (unsigned char)bbuf[0];
982 column++;
983 }
984 else if (bpp == 16)
985 {
986 unsigned char temp;
987
988 linepos += fread(&word, 2, 1, file);
989 temp = (word & rmask) >> rshift;
990 ptr[poffset] = temp;
991 temp = (word & gmask) >> gshift;
992 ptr[poffset + 1] = temp;
993 temp = (word & bmask) >> gshift;
994 ptr[poffset + 2] = temp;
995 column++;
996 }
997 else
998 {
999 unsigned char temp;
1000
1001 linepos += fread(&dword, 4, 1, file);
1002 temp = (dword & rmask) >> rshift;
1003 ptr[poffset] = temp;
1004 temp = (dword & gmask) >> gshift;
1005 ptr[poffset + 1] = temp;
1006 temp = (dword & bmask) >> bshift;
1007 ptr[poffset + 2] = temp;
1008 column++;
1009 }
1010 }
1011 while ((linepos < linesize) && (comp != 1) && (comp != 2))
1012 {
1013 int temp = fread(&aByte, 1, 1, file);
1014
1015 linepos += temp;
1016 if (!temp)
1017 break;
1018 }
1019 }
1020 if (cmap) free(cmap);
1021
1022 image->SetMask( FALSE );
1023
1024 fclose(file);
1025 return TRUE;
1026}
1027
1028#ifdef __WXMSW__
1029
1030wxBitmap wxImage::ConvertToBitmap() const
1031{
1032
1033 wxBitmap bitmap;
1034 wxCHECK_MSG( Ok(), bitmap, "invalid image" );
1035 int width = GetWidth();
1036 int height = GetHeight();
1037 bitmap.SetWidth( width );
1038 bitmap.SetHeight( height );
1039 bitmap.SetDepth( wxDisplayDepth() );
1040
1041 int headersize = sizeof(BITMAPINFOHEADER);
1042 LPBITMAPINFO lpDIBh = (BITMAPINFO *) malloc( headersize );
1043 wxCHECK_MSG( lpDIBh, bitmap, "could not allocate memory for DIB header" );
1044
1045// Fill in the DIB header
1046 lpDIBh->bmiHeader.biSize = headersize;
1047 lpDIBh->bmiHeader.biWidth = width;
1048 lpDIBh->bmiHeader.biHeight = -height;
1049 lpDIBh->bmiHeader.biSizeImage = width * height * 3;
1050
1051 lpDIBh->bmiHeader.biPlanes = 1;
1052 lpDIBh->bmiHeader.biBitCount = 24;
1053 lpDIBh->bmiHeader.biCompression = BI_RGB;
1054 lpDIBh->bmiHeader.biClrUsed = 0;
1055
1056// These seem not needed for our purpose here.
1057// lpDIBh->bmiHeader.biClrImportant = 0;
1058// lpDIBh->bmiHeader.biXPelsPerMeter = 0;
1059// lpDIBh->bmiHeader.biYPelsPerMeter = 0;
1060
1061 unsigned char *lpBits = (unsigned char *) malloc( width*height*3 );
1062 if( !lpBits )
1063 {
1064 wxFAIL_MSG( "could not allocate memory for DIB" );
1065 free( lpDIBh );
1066 return bitmap;
1067 }
1068
1069 unsigned char *data = GetData();
1070
1071 unsigned char *ptdata = data, *ptbits = lpBits;
1072 for( int i=0; i<width*height; i++ )
1073 {
1074 *(ptbits++) = *(ptdata+2);
1075 *(ptbits++) = *(ptdata+1);
1076 *(ptbits++) = *(ptdata );
1077 ptdata += 3;
1078 }
1079
1080 HDC hdc = ::GetDC(NULL);
1081
1082 HBITMAP hbitmap;
1083 hbitmap = CreateDIBitmap( hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits, lpDIBh, DIB_RGB_COLORS );
1084
1085// The above line is equivalent to the following two lines.
1086// hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
1087// ::SetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS);
1088// or the following lines
1089// hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
1090// HDC memdc = ::CreateCompatibleDC( hdc );
1091// ::SelectObject( memdc, hbitmap);
1092// ::SetDIBitsToDevice( memdc, 0, 0, width, height,
1093// 0, 0, 0, height, (void *)lpBits, lpDIBh, DIB_RGB_COLORS);
1094// ::SelectObject( memdc, 0 );
1095// ::DeleteDC( memdc );
1096
1097 bitmap.SetHBITMAP( (WXHBITMAP) hbitmap );
1098
1099 if( HasMask() )
1100 {
1101 unsigned char r = GetMaskRed();
1102 unsigned char g = GetMaskGreen();
1103 unsigned char b = GetMaskBlue();
1104 unsigned char zero = 0, one = 255;
1105 ptdata = data;
1106 ptbits = lpBits;
1107 for( int i=0; i<width*height; i++ )
1108 {
1109 if( (*(ptdata++)!=r) | (*(ptdata++)!=g) | (*(ptdata++)!=b) )
1110 {
1111 *(ptbits++) = one;
1112 *(ptbits++) = one;
1113 *(ptbits++) = one;
1114 }
1115 else
1116 {
1117 *(ptbits++) = zero;
1118 *(ptbits++) = zero;
1119 *(ptbits++) = zero;
1120 }
1121 }
1122 hbitmap = ::CreateBitmap( (WORD)width, (WORD)height, 1, 1, NULL );
1123 ::SetDIBits( hdc, hbitmap, 0, (WORD)height, lpBits, lpDIBh, DIB_RGB_COLORS);
1124 wxMask *mask = new wxMask();
1125 mask->SetMaskBitmap( (WXHBITMAP) hbitmap );
1126 bitmap.SetMask( mask );
1127
1128/* The following can also be used but is slow to run
1129 wxColour colour( GetMaskRed(), GetMaskGreen(), GetMaskBlue());
1130 wxMask *mask = new wxMask( bitmap, colour );
1131 bitmap.SetMask( mask );
1132*/
1133 }
1134
1135 ::ReleaseDC(NULL, hdc);
1136 free(lpDIBh);
1137 free(lpBits);
1138
1139 if( bitmap.GetHBITMAP() )
1140 bitmap.SetOk( TRUE );
1141 else
1142 bitmap.SetOk( FALSE );
1143
1144 return bitmap;
1145}
1146
1147
1148wxImage::wxImage( const wxBitmap &bitmap )
1149{
1150 if( !bitmap.Ok() )
1151 {
1152 wxFAIL_MSG( "invalid bitmap" );
1153 return;
1154 }
1155
1156 int width = bitmap.GetWidth();
1157 int height = bitmap.GetHeight();
1158 Create( width, height );
1159 unsigned char *data = GetData();
1160 if( !data )
1161 {
1162 wxFAIL_MSG( "could not allocate data for image" );
1163 return;
1164 }
1165
1166 int headersize = sizeof(BITMAPINFOHEADER);
1167 LPBITMAPINFO lpDIBh = (BITMAPINFO *) malloc( headersize );
1168 if( !lpDIBh )
1169 {
1170 wxFAIL_MSG( "could not allocate data for DIB header" );
1171 free( data );
1172 return;
1173 }
1174
1175// Fill in the DIB header
1176 lpDIBh->bmiHeader.biSize = headersize;
1177 lpDIBh->bmiHeader.biWidth = width;
1178 lpDIBh->bmiHeader.biHeight = -height;
1179 lpDIBh->bmiHeader.biSizeImage = width * height * 3;
1180
1181 lpDIBh->bmiHeader.biPlanes = 1;
1182 lpDIBh->bmiHeader.biBitCount = 24;
1183 lpDIBh->bmiHeader.biCompression = BI_RGB;
1184 lpDIBh->bmiHeader.biClrUsed = 0;
1185
1186// These seem not needed for our purpose here.
1187// lpDIBh->bmiHeader.biClrImportant = 0;
1188// lpDIBh->bmiHeader.biXPelsPerMeter = 0;
1189// lpDIBh->bmiHeader.biYPelsPerMeter = 0;
1190
1191 unsigned char *lpBits = (unsigned char *) malloc( width*height*3 );
1192 if( !lpBits )
1193 {
1194 wxFAIL_MSG( "could not allocate data for DIB" );
1195 free( data );
1196 free( lpDIBh );
1197 return;
1198 }
1199
1200 HBITMAP hbitmap;
1201 hbitmap = (HBITMAP) bitmap.GetHBITMAP();
1202 HDC hdc = ::GetDC(NULL);
1203 ::GetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS );
1204
1205 unsigned char *ptdata = data, *ptbits = lpBits;
1206 for( int i=0; i<width*height; i++ )
1207 {
1208 *(ptdata++) = *(ptbits+2);
1209 *(ptdata++) = *(ptbits+1);
1210 *(ptdata++) = *(ptbits );
1211 ptbits += 3;
1212 }
1213
1214 if( bitmap.GetMask() && bitmap.GetMask()->GetMaskBitmap() )
1215 {
1216 hbitmap = (HBITMAP) bitmap.GetMask()->GetMaskBitmap();
1217 HDC memdc = ::CreateCompatibleDC( hdc );
1218 ::SetTextColor( memdc, RGB( 0, 0, 0 ) );
1219 ::SetBkColor( memdc, RGB( 255, 255, 255 ) );
1220 ::GetDIBits( memdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS );
1221 ::DeleteDC( memdc );
1222 unsigned char r=16, g=16, b=16; // background set to RGB(16,16,16)
1223 ptdata = data;
1224 ptbits = lpBits;
1225 for( int i=0; i<width*height; i++ )
1226 {
1227 if( *ptbits != 0 )
1228 {
1229 *(ptdata++) = r;
1230 *(ptdata++) = g;
1231 *(ptdata++) = b;
1232 }
1233 ptbits += 3;
1234 }
1235 SetMaskColour( r, g, b );
1236 }
1237
1238 ::ReleaseDC(NULL, hdc);
1239 free(lpDIBh);
1240 free(lpBits);
1241}
1242
1243#endif
1244
1245#ifdef __WXGTK__
1246
1247wxBitmap wxImage::ConvertToBitmap() const
1248{
1249 wxBitmap bitmap;
1250
1251 wxCHECK_MSG( Ok(), bitmap, "invalid image" );
1252
1253 int width = GetWidth();
1254 int height = GetHeight();
1255
1256 bitmap.SetHeight( height );
1257 bitmap.SetWidth( width );
1258
1259 // Create picture
1260
1261 GdkImage *data_image =
1262 gdk_image_new( GDK_IMAGE_FASTEST, gdk_visual_get_system(), width, height );
1263
1264 bitmap.SetPixmap( gdk_pixmap_new( (GdkWindow*)&gdk_root_parent, width, height, -1 ) );
1265
1266 // Create mask
1267
1268 GdkImage *mask_image = (GdkImage*) NULL;
1269
1270 if (HasMask())
1271 {
1272 unsigned char *mask_data = (unsigned char*)malloc( ((width >> 3)+8) * height );
1273
1274 mask_image = gdk_image_new_bitmap( gdk_visual_get_system(), mask_data, width, height );
1275
1276 wxMask *mask = new wxMask();
1277 mask->m_bitmap = gdk_pixmap_new( (GdkWindow*)&gdk_root_parent, width, height, 1 );
1278
1279 bitmap.SetMask( mask );
1280 }
1281
1282 // Retrieve depth
1283
1284 GdkVisual *visual = gdk_window_get_visual( bitmap.GetPixmap() );
1285 if (visual == NULL) visual = gdk_window_get_visual( (GdkWindow*) &gdk_root_parent );
1286 int bpp = visual->depth;
1287 if ((bpp == 16) && (visual->red_mask != 0xf800)) bpp = 15;
1288 if (bpp < 8) bpp = 8;
1289
1290 // Render
1291
1292 enum byte_order { RGB, RBG, BRG, BGR, GRB, GBR };
1293 byte_order b_o = RGB;
1294
1295 if (bpp >= 24)
1296 {
1297 GdkVisual *visual = gdk_visual_get_system();
1298 if ((visual->red_mask > visual->green_mask) && (visual->green_mask > visual->blue_mask)) b_o = RGB;
1299 else if ((visual->red_mask > visual->blue_mask) && (visual->blue_mask > visual->green_mask)) b_o = RGB;
1300 else if ((visual->blue_mask > visual->red_mask) && (visual->red_mask > visual->green_mask)) b_o = BRG;
1301 else if ((visual->blue_mask > visual->green_mask) && (visual->green_mask > visual->red_mask)) b_o = BGR;
1302 else if ((visual->green_mask > visual->red_mask) && (visual->red_mask > visual->blue_mask)) b_o = GRB;
1303 else if ((visual->green_mask > visual->blue_mask) && (visual->blue_mask > visual->red_mask)) b_o = GBR;
1304 }
1305
1306 int r_mask = GetMaskRed();
1307 int g_mask = GetMaskGreen();
1308 int b_mask = GetMaskBlue();
1309
1310 unsigned char* data = GetData();
1311
1312 int index = 0;
1313 for (int y = 0; y < height; y++)
1314 {
1315 for (int x = 0; x < width; x++)
1316 {
1317 int r = data[index];
1318 index++;
1319 int g = data[index];
1320 index++;
1321 int b = data[index];
1322 index++;
1323
1324 if (HasMask())
1325 {
1326 if ((r == r_mask) && (b == b_mask) && (g == g_mask))
1327 gdk_image_put_pixel( mask_image, x, y, 1 );
1328 else
1329 gdk_image_put_pixel( mask_image, x, y, 0 );
1330 }
1331
1332 switch (bpp)
1333 {
1334 case 8:
1335 {
1336 GdkColormap *cmap = gtk_widget_get_default_colormap();
1337 GdkColor *colors = cmap->colors;
1338 int max = 3 * (65536);
1339 int index = -1;
1340
1341 for (int i = 0; i < cmap->size; i++)
1342 {
1343 int rdiff = (r << 8) - colors[i].red;
1344 int gdiff = (g << 8) - colors[i].green;
1345 int bdiff = (b << 8) - colors[i].blue;
1346 int sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
1347 if (sum < max) { index = i; max = sum; }
1348 }
1349
1350 gdk_image_put_pixel( data_image, x, y, index );
1351
1352 break;
1353 }
1354 case 15:
1355 {
1356 guint32 pixel = ((r & 0xf8) << 7) | ((g & 0xf8) << 2) | ((b & 0xf8) >> 3);
1357 gdk_image_put_pixel( data_image, x, y, pixel );
1358 break;
1359 }
1360 case 16:
1361 {
1362 guint32 pixel = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | ((b & 0xf8) >> 3);
1363 gdk_image_put_pixel( data_image, x, y, pixel );
1364 break;
1365 }
1366 case 32:
1367 case 24:
1368 {
1369 guint32 pixel = 0;
1370 switch (b_o)
1371 {
1372 case RGB: pixel = (r << 16) | (g << 8) | b; break;
1373 case RBG: pixel = (r << 16) | (b << 8) | g; break;
1374 case BRG: pixel = (b << 16) | (r << 8) | g; break;
1375 case BGR: pixel = (b << 16) | (g << 8) | r; break;
1376 case GRB: pixel = (g << 16) | (r << 8) | b; break;
1377 case GBR: pixel = (g << 16) | (b << 8) | r; break;
1378 }
1379 gdk_image_put_pixel( data_image, x, y, pixel );
1380 }
1381 default: break;
1382 }
1383 } // for
1384 } // for
1385
1386 // Blit picture
1387
1388 GdkGC *data_gc = gdk_gc_new( bitmap.GetPixmap() );
1389
1390 gdk_draw_image( bitmap.GetPixmap(), data_gc, data_image, 0, 0, 0, 0, width, height );
1391
1392 gdk_image_destroy( data_image );
1393 gdk_gc_unref( data_gc );
1394
1395 // Blit mask
1396
1397 if (HasMask())
1398 {
1399 GdkGC *mask_gc = gdk_gc_new( bitmap.GetMask()->GetBitmap() );
1400
1401 gdk_draw_image( bitmap.GetMask()->GetBitmap(), mask_gc, mask_image, 0, 0, 0, 0, width, height );
1402
1403 gdk_image_destroy( mask_image );
1404 gdk_gc_unref( mask_gc );
1405 }
1406
1407 return bitmap;
1408}
1409
1410wxImage::wxImage( const wxBitmap &bitmap )
1411{
1412 wxCHECK_RET( bitmap.Ok(), "invalid bitmap" );
1413
1414 GdkImage *gdk_image = gdk_image_get( bitmap.GetPixmap(),
1415 0, 0,
1416 bitmap.GetWidth(), bitmap.GetHeight() );
1417
1418 wxCHECK_RET( gdk_image, "couldn't create image" );
1419
1420 Create( bitmap.GetWidth(), bitmap.GetHeight() );
1421 char unsigned *data = GetData();
1422
1423 if (!data)
1424 {
1425 gdk_image_destroy( gdk_image );
1426 wxFAIL_MSG( "couldn't create image" );
1427 return;
1428 }
1429
1430 GdkImage *gdk_image_mask = (GdkImage*) NULL;
1431 if (bitmap.GetMask())
1432 {
1433 gdk_image_mask = gdk_image_get( bitmap.GetMask()->GetBitmap(),
1434 0, 0,
1435 bitmap.GetWidth(), bitmap.GetHeight() );
1436
1437 SetMaskColour( 16, 16, 16 ); // anything unlikely and dividable
1438 }
1439
1440 GdkVisual *visual = gdk_window_get_visual( bitmap.GetPixmap() );
1441 if (visual == NULL) visual = gdk_window_get_visual( (GdkWindow*) &gdk_root_parent );
1442 int bpp = visual->depth;
1443 if ((bpp == 16) && (visual->red_mask != 0xf800)) bpp = 15;
1444
1445 GdkColormap *cmap = gtk_widget_get_default_colormap();
1446
1447 long pos = 0;
1448 for (int j = 0; j < bitmap.GetHeight(); j++)
1449 {
1450 for (int i = 0; i < bitmap.GetWidth(); i++)
1451 {
1452 int pixel = gdk_image_get_pixel( gdk_image, i, j );
1453 if (bpp <= 8)
1454 {
1455 data[pos] = cmap->colors[pixel].red >> 8;
1456 data[pos+1] = cmap->colors[pixel].green >> 8;
1457 data[pos+2] = cmap->colors[pixel].blue >> 8;
1458 } else if (bpp == 15)
1459 {
1460 data[pos] = (pixel >> 7) & 0xf8;
1461 data[pos+1] = (pixel >> 2) & 0xf8;
1462 data[pos+2] = (pixel << 3) & 0xf8;
1463 } else if (bpp == 16)
1464 {
1465 data[pos] = (pixel >> 8) & 0xf8;
1466 data[pos+1] = (pixel >> 3) & 0xfc;
1467 data[pos+2] = (pixel << 3) & 0xf8;
1468 } else
1469 {
1470 data[pos] = (pixel >> 16) & 0xff;
1471 data[pos+1] = (pixel >> 8) & 0xff;
1472 data[pos+2] = pixel & 0xff;
1473 }
1474
1475 if (gdk_image_mask)
1476 {
1477 int mask_pixel = gdk_image_get_pixel( gdk_image_mask, i, j );
1478 if (mask_pixel == 0)
1479 {
1480 data[pos] = 16;
1481 data[pos+1] = 16;
1482 data[pos+2] = 16;
1483 }
1484 }
1485
1486 pos += 3;
1487 }
1488 }
1489
1490 gdk_image_destroy( gdk_image );
1491 if (gdk_image_mask) gdk_image_destroy( gdk_image_mask );
1492}
1493
1494#endif