]> git.saurik.com Git - wxWidgets.git/blame - src/common/imagtga.cpp
adding implementation for osx
[wxWidgets.git] / src / common / imagtga.cpp
CommitLineData
3af706cc 1/////////////////////////////////////////////////////////////////////////////
80fdcdb9 2// Name: src/common/imagtga.cpp
3af706cc
VZ
3// Purpose: wxImage TGA handler
4// Author: Seth Jackson
5// CVS-ID: $Id$
6// Copyright: (c) 2005 Seth Jackson
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// ============================================================================
11// declarations
12// ============================================================================
13
14// ----------------------------------------------------------------------------
15// headers
16// ----------------------------------------------------------------------------
17
18// For compilers that support precompilation, includes "wx.h".
19#include "wx/wxprec.h"
20
21#ifdef __BORLANDC__
22 #pragma hdrstop
23#endif
24
25#if wxUSE_IMAGE && wxUSE_TGA
26
27#ifndef WX_PRECOMP
28 #include "wx/palette.h"
29#endif
30
31#include "wx/imagtga.h"
32#include "wx/log.h"
56692119 33#include "wx/scopeguard.h"
3af706cc
VZ
34
35// ----------------------------------------------------------------------------
36// constants
37// ----------------------------------------------------------------------------
38
39// TGA error codes.
40enum
41{
8fee6306
VZ
42 wxTGA_OK,
43 wxTGA_INVFORMAT,
44 wxTGA_MEMERR,
45 wxTGA_IOERR
3af706cc
VZ
46};
47
48// TGA header bytes.
49enum
50{
51 HDR_OFFSET = 0,
52 HDR_COLORTYPE = 1,
53 HDR_IMAGETYPE = 2,
54 HDR_PALETTESTART = 3,
55 HDR_PALETTELENGTH = 5,
56 HDR_PALETTEBITS = 7,
57 HDR_XORIGIN = 8,
58 HDR_YORIGIN = 10,
59 HDR_WIDTH = 12,
60 HDR_HEIGHT = 14,
61 HDR_BPP = 16,
62 HDR_ORIENTATION = 17,
63 HDR_SIZE
64};
65
66// TGA color types.
67enum
68{
69 wxTGA_UNMAPPED = 0,
70 wxTGA_MAPPED = 1
71};
72
73// ============================================================================
74// implementation
75// ============================================================================
76
77IMPLEMENT_DYNAMIC_CLASS(wxTGAHandler, wxImageHandler)
78
79#if wxUSE_STREAMS
80
81// ----------------------------------------------------------------------------
82// worker functions
83// ----------------------------------------------------------------------------
84
85static
86void FlipTGA(unsigned char* imageData, int width, int height, short pixelSize)
87{
88 int lineLength = width * pixelSize;
89 unsigned char *line1 = imageData;
90 unsigned char *line2 = &imageData[lineLength * (height - 1)];
91
92 unsigned char temp;
93 for ( ; line1 < line2; line2 -= (lineLength * 2))
94 {
95 for (int index = 0; index < lineLength; line1++, line2++, index++)
96 {
97 temp = *line1;
98 *line1 = *line2;
99 *line2 = temp;
100 }
101 }
102}
103
8fee6306 104// return wxTGA_OK or wxTGA_IOERR
3af706cc 105static
8fee6306 106int DecodeRLE(unsigned char* imageData, unsigned long imageSize,
3af706cc
VZ
107 short pixelSize, wxInputStream& stream)
108{
109 unsigned long index = 0;
110 unsigned char current;
111 unsigned int length;
112 unsigned char buf[4];
113
114 while (index < imageSize)
115 {
8fee6306
VZ
116 int ch = stream.GetC();
117 if ( ch == wxEOF )
118 return wxTGA_IOERR;
119
120 current = ch;
3af706cc
VZ
121
122 // RLE packet.
123 if ( current & 0x80 )
124 {
125 // Get the run length of the packet.
126 current &= 0x7f;
127
128 current++;
129
130 length = current;
131
132 index += current * pixelSize;
133
fdfedfc0
DS
134 if (index >= imageSize)
135 {
136 return wxTGA_IOERR;
137 }
138
3af706cc 139 // Repeat the pixel length times.
8fee6306
VZ
140 if ( !stream.Read(buf, pixelSize) )
141 return wxTGA_IOERR;
3af706cc
VZ
142
143 for (unsigned int i = 0; i < length; i++)
144 {
145 memcpy(imageData, buf, pixelSize);
146
147 imageData += pixelSize;
148 }
149 }
150 else // Raw packet.
151 {
152 // Get the run length of the packet.
153 current++;
154
155 length = current * pixelSize;
156
157 index += length;
158
fdfedfc0
DS
159 if (index >= imageSize)
160 {
161 return wxTGA_IOERR;
162 }
163
3af706cc 164 // Write the next length pixels directly to the image data.
8fee6306
VZ
165 if ( !stream.Read(imageData, length) )
166 return wxTGA_IOERR;
3af706cc
VZ
167
168 imageData += length;
169 }
170 }
8fee6306
VZ
171
172 return wxTGA_OK;
3af706cc
VZ
173}
174
07e99d87
DS
175/*
176Mimic the behaviour of wxPalette.GetRGB and the way the TGA image handler
177used it. That is: don't check the return value of GetRGB and continue decoding
178using previous RGB values.
179
180It might be better to check for palette index bounds and stop decoding if
181it's out of range (and add something like wxTGA_DATAERR to indicate unexpected
182pixel data).
183*/
184static
185void Palette_GetRGB(const unsigned char *palette, unsigned int paletteCount,
186 unsigned int index,
187 unsigned char *red, unsigned char *green, unsigned char *blue)
188{
189 if (index >= paletteCount)
190 {
191 return;
192 }
193
194 *red = palette[index];
195 *green = palette[(paletteCount * 1) + index];
196 *blue = palette[(paletteCount * 2) + index];
197}
198
199static
200void Palette_SetRGB(unsigned char *palette, unsigned int paletteCount,
201 unsigned int index,
202 unsigned char red, unsigned char green, unsigned char blue)
203{
204 palette[index] = red;
205 palette[(paletteCount * 1) + index] = green;
206 palette[(paletteCount * 2) + index] = blue;
207}
208
3af706cc
VZ
209static
210int ReadTGA(wxImage* image, wxInputStream& stream)
211{
212 // Read in the TGA header
213 unsigned char hdr[HDR_SIZE];
214 stream.Read(hdr, HDR_SIZE);
215
216 short offset = hdr[HDR_OFFSET] + HDR_SIZE;
217 short colorType = hdr[HDR_COLORTYPE];
218 short imageType = hdr[HDR_IMAGETYPE];
07e99d87
DS
219 unsigned int paletteLength = hdr[HDR_PALETTELENGTH]
220 + 256 * hdr[HDR_PALETTELENGTH + 1];
3af706cc
VZ
221 int width = (hdr[HDR_WIDTH] + 256 * hdr[HDR_WIDTH + 1]) -
222 (hdr[HDR_XORIGIN] + 256 * hdr[HDR_XORIGIN + 1]);
223 int height = (hdr[HDR_HEIGHT] + 256 * hdr[HDR_HEIGHT + 1]) -
224 (hdr[HDR_YORIGIN] + 256 * hdr[HDR_YORIGIN + 1]);
225 short bpp = hdr[HDR_BPP];
226 short orientation = hdr[HDR_ORIENTATION] & 0x20;
227
228 image->Create(width, height);
229
a1b806b9 230 if (!image->IsOk())
3af706cc
VZ
231 {
232 return wxTGA_MEMERR;
233 }
234
235 const short pixelSize = bpp / 8;
236
237 const unsigned long imageSize = width * height * pixelSize;
238
239 unsigned char *imageData = (unsigned char* )malloc(imageSize);
240
241 if (!imageData)
242 {
243 return wxTGA_MEMERR;
244 }
245
56692119
VZ
246 wxON_BLOCK_EXIT1(free, imageData);
247
3af706cc
VZ
248 unsigned char *dst = image->GetData();
249
250 unsigned char* alpha = NULL;
251 if (bpp == 16 || bpp == 32)
252 {
253 image->SetAlpha();
254
255 alpha = image->GetAlpha();
256 }
257
258 // Seek from the offset we got from the TGA header.
b81e4506
FM
259 if (stream.SeekI(offset, wxFromStart) == wxInvalidOffset)
260 return wxTGA_INVFORMAT;
3af706cc 261
07e99d87 262 unsigned char *palette = NULL;
3af706cc
VZ
263 // Load a palette if we have one.
264 if (colorType == wxTGA_MAPPED)
265 {
266 unsigned char buf[3];
267
07e99d87 268 palette = (unsigned char *) malloc(paletteLength * 3);
3af706cc 269
07e99d87 270 for (unsigned int i = 0; i < paletteLength; i++)
3af706cc
VZ
271 {
272 stream.Read(buf, 3);
273
07e99d87 274 Palette_SetRGB(palette, paletteLength, i, buf[2], buf[1], buf[0]);
3af706cc
VZ
275 }
276
6e3d5828 277#if wxUSE_PALETTE
3af706cc 278 // Set the palette of the image.
07e99d87
DS
279 image->SetPalette(wxPalette((int) paletteLength, &palette[0],
280 &palette[paletteLength * 1], &palette[paletteLength * 2]));
6e3d5828 281#endif // wxUSE_PALETTE
3af706cc 282
3af706cc
VZ
283 }
284
07e99d87
DS
285 wxON_BLOCK_EXIT1(free, palette);
286
3af706cc
VZ
287 // Handle the various TGA formats we support.
288
289 switch (imageType)
290 {
291 // Raw indexed.
292
293 case 1:
294 {
3af706cc
VZ
295 unsigned char r;
296 unsigned char g;
297 unsigned char b;
298
299 // No compression read the data directly to imageData.
300
301 stream.Read(imageData, imageSize);
302
303 // If orientation == 0, then the image is stored upside down.
304 // We need to store it right side up.
305
306 if (orientation == 0)
307 {
308 FlipTGA(imageData, width, height, pixelSize);
309 }
310
311 // Handle the different pixel depths.
312
313 switch (bpp)
314 {
315 // 8 bpp.
316
317 case 8:
318 {
319 for (unsigned long index = 0; index < imageSize; index += pixelSize)
320 {
07e99d87
DS
321 Palette_GetRGB(palette, paletteLength,
322 imageData[index], &r, &g, &b);
3af706cc
VZ
323
324 *(dst++) = r;
325 *(dst++) = g;
326 *(dst++) = b;
327 }
328 }
329 break;
330
331 // 16 bpp.
332
333 case 16:
334 {
335 for (unsigned long index = 0; index < imageSize; index += pixelSize)
336 {
07e99d87
DS
337 Palette_GetRGB(palette, paletteLength,
338 imageData[index], &r, &g, &b);
3af706cc
VZ
339
340 *(dst++) = r;
341 *(dst++) = g;
342 *(dst++) = b;
343 *(alpha++) = (imageData[index + 1] & 0x80) ? 0 : 255;
344 }
345 }
346 break;
347
348 default:
349 return wxTGA_INVFORMAT;
350 }
351 }
352 break;
353
354 // Raw RGB.
355
356 case 2:
357 {
358 // No compression read the data directly to imageData.
359
360 stream.Read(imageData, imageSize);
361
362 // If orientation == 0, then the image is stored upside down.
363 // We need to store it right side up.
364
365 if (orientation == 0)
366 {
367 FlipTGA(imageData, width, height, pixelSize);
368 }
369
370 // Handle the different pixel depths.
371
372 switch (bpp)
373 {
374 //16 bpp.
375
376 case 16:
377 {
378 unsigned char temp;
379
380 for (unsigned long index = 0; index < imageSize; index += pixelSize)
381 {
382 temp = (imageData[index + 1] & 0x7c) << 1;
383 temp |= temp >> 5;
384 *(dst++) = temp;
385
386 temp = ((imageData[index + 1] & 0x03) << 6) | ((imageData[index] & 0xe0) >> 2);
387 temp |= temp >> 5;
388 *(dst++) = temp;
389
390 temp = (imageData[index] & 0x1f) << 3;
391 temp |= temp >> 5;
392 *(dst++) = temp;
393
394 *(alpha++) = (imageData[index + 1] & 0x80) ? 0 : 255;
395 }
396 }
397 break;
398
399 // 24 bpp.
400
401 case 24:
402 {
403 for (unsigned long index = 0; index < imageSize; index += pixelSize)
404 {
405 *(dst++) = imageData[index + 2];
406 *(dst++) = imageData[index + 1];
407 *(dst++) = imageData[index];
408 }
409 }
410 break;
411
412 // 32 bpp.
413
414 case 32:
415 {
416 for (unsigned long index = 0; index < imageSize; index += pixelSize)
417 {
418 *(dst++) = imageData[index + 2];
419 *(dst++) = imageData[index + 1];
420 *(dst++) = imageData[index];
421 *(alpha++) = imageData[index + 3];
422 }
423 }
424 break;
425
426 default:
427 return wxTGA_INVFORMAT;
428 }
429 }
430 break;
431
432 // Raw grayscale.
433
434 case 3:
435 {
436 // No compression read the data directly to imageData.
437
438 stream.Read(imageData, imageSize);
439
440 // If orientation == 0, then the image is stored upside down.
441 // We need to store it right side up.
442
443 if (orientation == 0)
444 {
445 FlipTGA(imageData, width, height, pixelSize);
446 }
447
448 // Handle the different pixel depths.
449
450 switch (bpp)
451 {
452 // 8 bpp.
453
454 case 8:
455 {
456 for (unsigned long index = 0; index < imageSize; index += pixelSize)
457 {
458 *(dst++) = imageData[index];
459 *(dst++) = imageData[index];
460 *(dst++) = imageData[index];
461 }
462 }
463 break;
464
465 // 16 bpp.
466
467 case 16:
468 {
469 for (unsigned long index = 0; index < imageSize; index += pixelSize)
470 {
471 *(dst++) = imageData[index];
472 *(dst++) = imageData[index];
473 *(dst++) = imageData[index];
474 *(alpha++) = imageData[index + 1];
475 }
476 }
477 break;
478
479 default:
480 return wxTGA_INVFORMAT;
481 }
482 }
483 break;
484
485 // RLE indexed.
486
487 case 9:
488 {
3af706cc
VZ
489 unsigned char r;
490 unsigned char g;
491 unsigned char b;
492
493 // Decode the RLE data.
494
8fee6306
VZ
495 int rc = DecodeRLE(imageData, imageSize, pixelSize, stream);
496 if ( rc != wxTGA_OK )
497 return rc;
3af706cc
VZ
498
499 // If orientation == 0, then the image is stored upside down.
500 // We need to store it right side up.
501
502 if (orientation == 0)
503 {
504 FlipTGA(imageData, width, height, pixelSize);
505 }
506
507 // Handle the different pixel depths.
508
509 switch (bpp)
510 {
511 // 8 bpp.
512
513 case 8:
514 {
515 for (unsigned long index = 0; index < imageSize; index += pixelSize)
516 {
07e99d87
DS
517 Palette_GetRGB(palette, paletteLength,
518 imageData[index], &r, &g, &b);
3af706cc
VZ
519
520 *(dst++) = r;
521 *(dst++) = g;
522 *(dst++) = b;
523 }
524 }
525 break;
526
527 // 16 bpp.
528
529 case 16:
530 {
531 for (unsigned long index = 0; index < imageSize; index += pixelSize)
532 {
07e99d87
DS
533 Palette_GetRGB(palette, paletteLength,
534 imageData[index], &r, &g, &b);
3af706cc
VZ
535
536 *(dst++) = r;
537 *(dst++) = g;
538 *(dst++) = b;
539 *(alpha++) = (imageData[index + 1] & 0x80) ? 0 : 255;
540 }
541 }
542 break;
543
544 default:
545 return wxTGA_INVFORMAT;
546 }
547 }
548 break;
549
550 // RLE RGB.
551
552 case 10:
553 {
554 // Decode the RLE data.
555
8fee6306
VZ
556 int rc = DecodeRLE(imageData, imageSize, pixelSize, stream);
557 if ( rc != wxTGA_OK )
558 return rc;
3af706cc
VZ
559
560 // If orientation == 0, then the image is stored upside down.
561 // We need to store it right side up.
562
563 if (orientation == 0)
564 {
565 FlipTGA(imageData, width, height, pixelSize);
566 }
567
568 // Handle the different pixel depths.
569
570 switch (bpp)
571 {
572 //16 bpp.
573
574 case 16:
575 {
576 unsigned char temp;
577
578 for (unsigned long index = 0; index < imageSize; index += pixelSize)
579 {
580 temp = (imageData[index + 1] & 0x7c) << 1;
581 temp |= temp >> 5;
582 *(dst++) = temp;
583
584 temp = ((imageData[index + 1] & 0x03) << 6) | ((imageData[index] & 0xe0) >> 2);
585 temp |= temp >> 5;
586 *(dst++) = temp;
587
588 temp = (imageData[index] & 0x1f) << 3;
589 temp |= temp >> 5;
590 *(dst++) = temp;
591
592 *(alpha++) = (imageData[index + 1] & 0x80) ? 0 : 255;
593 }
594 }
595 break;
596
597 // 24 bpp.
598
599 case 24:
600 {
601 for (unsigned long index = 0; index < imageSize; index += pixelSize)
602 {
603 *(dst++) = imageData[index + 2];
604 *(dst++) = imageData[index + 1];
605 *(dst++) = imageData[index];
606 }
607 }
608 break;
609
610 // 32 bpp.
611
612 case 32:
613 {
614 for (unsigned long index = 0; index < imageSize; index += pixelSize)
615 {
616 *(dst++) = imageData[index + 2];
617 *(dst++) = imageData[index + 1];
618 *(dst++) = imageData[index];
619 *(alpha++) = imageData[index + 3];
620 }
621 }
622 break;
623
624 default:
625 return wxTGA_INVFORMAT;
626 }
627 }
628 break;
629
630 // RLE grayscale.
631
632 case 11:
633 {
634 // Decode the RLE data.
635
8fee6306
VZ
636 int rc = DecodeRLE(imageData, imageSize, pixelSize, stream);
637 if ( rc != wxTGA_OK )
638 return rc;
3af706cc
VZ
639
640 // If orientation == 0, then the image is stored upside down.
641 // We need to store it right side up.
642
643 if (orientation == 0)
644 {
645 FlipTGA(imageData, width, height, pixelSize);
646 }
647
648 // Handle the different pixel depths.
649
650 switch (bpp)
651 {
652 // 8 bpp.
653
654 case 8:
655 {
656 for (unsigned long index = 0; index < imageSize; index += pixelSize)
657 {
658 *(dst++) = imageData[index];
659 *(dst++) = imageData[index];
660 *(dst++) = imageData[index];
661 }
662 }
663 break;
664
665 // 16 bpp.
666
667 case 16:
668 {
669 for (unsigned long index = 0; index < imageSize; index += pixelSize)
670 {
671 *(dst++) = imageData[index];
672 *(dst++) = imageData[index];
673 *(dst++) = imageData[index];
674 *(alpha++) = imageData[index + 1];
675 }
676 }
677 break;
678
679 default:
680 return wxTGA_INVFORMAT;
681 }
682 }
683 break;
684
685 default:
686 return wxTGA_INVFORMAT;
687 }
688
3af706cc
VZ
689 return wxTGA_OK;
690}
691
692static
3d926ff8 693int SaveTGA(const wxImage& image, wxOutputStream *stream)
3af706cc 694{
3d926ff8
DS
695 bool hasAlpha = image.HasAlpha();
696 unsigned bytesPerPixel = 3 + (hasAlpha ? 1 : 0);
697 wxSize size = image.GetSize();
698 size_t scanlineSize = size.x * bytesPerPixel;
699 unsigned char *scanlineData = (unsigned char *) malloc(scanlineSize);
700 if (!scanlineData)
701 {
702 return wxTGA_MEMERR;
703 }
704
705 wxON_BLOCK_EXIT1(free, scanlineData);
706
707 // Compose and write the TGA header
708 unsigned char hdr[HDR_SIZE];
709 (void) memset(&hdr, 0, HDR_SIZE);
710
711 hdr[HDR_COLORTYPE] = wxTGA_UNMAPPED;
712 hdr[HDR_IMAGETYPE] = 2 /* Uncompressed truecolour */;
713
714 hdr[HDR_WIDTH] = size.x & 0xFF;
715 hdr[HDR_WIDTH + 1] = (size.x >> 8) & 0xFF;
716
717 hdr[HDR_HEIGHT] = size.y & 0xFF;
718 hdr[HDR_HEIGHT + 1] = (size.y >> 8) & 0xFF;
719
720 hdr[HDR_BPP] = hasAlpha ? 32 : 24;
721 hdr[HDR_ORIENTATION] = 1 << 5; // set bit to indicate top-down order
722 if (hasAlpha)
723 {
724 hdr[HDR_ORIENTATION] |= 8; // number of alpha bits
725 }
726
727 if ( !stream->Write(hdr, HDR_SIZE) )
728 {
729 return wxTGA_IOERR;
730 }
731
732
733 // Write image data, converting RGB to BGR and adding alpha if applicable
734
735 unsigned char *src = image.GetData();
736 unsigned char *alpha = image.GetAlpha();
737 for (int y = 0; y < size.y; ++y)
738 {
739 unsigned char *dst = scanlineData;
740 for (int x = 0; x < size.x; ++x)
741 {
742 dst[0] = src[2];
743 dst[1] = src[1];
744 dst[2] = src[0];
745 if (alpha)
746 {
747 dst[3] = *(alpha++);
748 }
749 src += 3;
750 dst += bytesPerPixel;
751 }
752 if ( !stream->Write(scanlineData, scanlineSize) )
753 {
754 return wxTGA_IOERR;
755 }
756 }
3af706cc
VZ
757
758 return wxTGA_OK;
759}
760
761// ----------------------------------------------------------------------------
762// wxTGAHandler
763// ----------------------------------------------------------------------------
764
765bool wxTGAHandler::LoadFile(wxImage* image,
766 wxInputStream& stream,
767 bool verbose,
768 int WXUNUSED(index))
769{
770 if ( !CanRead(stream) )
771 {
772 if ( verbose )
af588446 773 {
3af706cc 774 wxLogError(wxT("TGA: this is not a TGA file."));
af588446 775 }
3af706cc
VZ
776
777 return false;
778 }
779
780 image->Destroy();
781
782 int error = ReadTGA(image, stream);
783 if ( error != wxTGA_OK )
784 {
785 if ( verbose )
786 {
787 switch ( error )
788 {
789 case wxTGA_INVFORMAT:
790 wxLogError(wxT("TGA: image format unsupported."));
791 break;
792
793 case wxTGA_MEMERR:
794 wxLogError(wxT("TGA: couldn't allocate memory."));
795 break;
796
8fee6306
VZ
797 case wxTGA_IOERR:
798 wxLogError(wxT("TGA: couldn't read image data."));
799 break;
800
3af706cc
VZ
801 default:
802 wxLogError(wxT("TGA: unknown error!"));
803 }
804 }
805
806 image->Destroy();
807
808 return false;
809 }
810
811 return true;
812}
813
814bool wxTGAHandler::SaveFile(wxImage* image, wxOutputStream& stream, bool verbose)
815{
3d926ff8 816 int error = SaveTGA(*image, &stream);
3af706cc
VZ
817
818 if ( error != wxTGA_OK )
819 {
820 if ( verbose )
821 {
822 switch ( error )
823 {
3af706cc
VZ
824 case wxTGA_MEMERR:
825 wxLogError(wxT("TGA: couldn't allocate memory."));
826 break;
827
3d926ff8
DS
828 case wxTGA_IOERR:
829 wxLogError(wxT("TGA: couldn't write image data."));
830 break;
831
3af706cc
VZ
832 default:
833 wxLogError(wxT("TGA: unknown error!"));
834 }
835 }
836
837 return false;
838 }
839
840 return true;
841}
842
843bool wxTGAHandler::DoCanRead(wxInputStream& stream)
844{
845 // read the fixed-size TGA headers
846 unsigned char hdr[HDR_SIZE];
8faef7cc 847 stream.Read(hdr, HDR_SIZE); // it's ok to modify the stream position here
3af706cc 848
d13b34d3 849 // Check whether we can read the file or not.
3af706cc
VZ
850
851 short colorType = hdr[HDR_COLORTYPE];
852 if ( colorType != wxTGA_UNMAPPED && colorType != wxTGA_MAPPED )
853 {
854 return false;
855 }
856
857 short imageType = hdr[HDR_IMAGETYPE];
858 if ( imageType == 0 || imageType == 32 || imageType == 33 )
859 {
860 return false;
861 }
862
863 short bpp = hdr[HDR_BPP];
864 if ( bpp != 8 && bpp != 16 && bpp != 24 && bpp != 32 )
865 {
866 return false;
867 }
868
869 return true;
870}
871
872#endif // wxUSE_STREAMS
873
874#endif // wxUSE_IMAGE && wxUSE_TGA