]> git.saurik.com Git - wxWidgets.git/blob - src/common/imagtga.cpp
Move the wxWebFileProtocolHandler from the IE backend to the common source, add the...
[wxWidgets.git] / src / common / imagtga.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/imagtga.cpp
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"
33 #include "wx/scopeguard.h"
34
35 // ----------------------------------------------------------------------------
36 // constants
37 // ----------------------------------------------------------------------------
38
39 // TGA error codes.
40 enum
41 {
42 wxTGA_OK,
43 wxTGA_INVFORMAT,
44 wxTGA_MEMERR,
45 wxTGA_IOERR
46 };
47
48 // TGA header bytes.
49 enum
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.
67 enum
68 {
69 wxTGA_UNMAPPED = 0,
70 wxTGA_MAPPED = 1
71 };
72
73 // ============================================================================
74 // implementation
75 // ============================================================================
76
77 IMPLEMENT_DYNAMIC_CLASS(wxTGAHandler, wxImageHandler)
78
79 #if wxUSE_STREAMS
80
81 // ----------------------------------------------------------------------------
82 // worker functions
83 // ----------------------------------------------------------------------------
84
85 static
86 void 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
104 // return wxTGA_OK or wxTGA_IOERR
105 static
106 int DecodeRLE(unsigned char* imageData, unsigned long imageSize,
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 {
116 int ch = stream.GetC();
117 if ( ch == wxEOF )
118 return wxTGA_IOERR;
119
120 current = ch;
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
134 if (index >= imageSize)
135 {
136 return wxTGA_IOERR;
137 }
138
139 // Repeat the pixel length times.
140 if ( !stream.Read(buf, pixelSize) )
141 return wxTGA_IOERR;
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
159 if (index >= imageSize)
160 {
161 return wxTGA_IOERR;
162 }
163
164 // Write the next length pixels directly to the image data.
165 if ( !stream.Read(imageData, length) )
166 return wxTGA_IOERR;
167
168 imageData += length;
169 }
170 }
171
172 return wxTGA_OK;
173 }
174
175 /*
176 Mimic the behaviour of wxPalette.GetRGB and the way the TGA image handler
177 used it. That is: don't check the return value of GetRGB and continue decoding
178 using previous RGB values.
179
180 It might be better to check for palette index bounds and stop decoding if
181 it's out of range (and add something like wxTGA_DATAERR to indicate unexpected
182 pixel data).
183 */
184 static
185 void 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
199 static
200 void 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
209 static
210 int 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];
219 unsigned int paletteLength = hdr[HDR_PALETTELENGTH]
220 + 256 * hdr[HDR_PALETTELENGTH + 1];
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
230 if (!image->IsOk())
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
246 wxON_BLOCK_EXIT1(free, imageData);
247
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.
259 if (stream.SeekI(offset, wxFromStart) == wxInvalidOffset)
260 return wxTGA_INVFORMAT;
261
262 unsigned char *palette = NULL;
263 // Load a palette if we have one.
264 if (colorType == wxTGA_MAPPED)
265 {
266 unsigned char buf[3];
267
268 palette = (unsigned char *) malloc(paletteLength * 3);
269
270 for (unsigned int i = 0; i < paletteLength; i++)
271 {
272 stream.Read(buf, 3);
273
274 Palette_SetRGB(palette, paletteLength, i, buf[2], buf[1], buf[0]);
275 }
276
277 #if wxUSE_PALETTE
278 // Set the palette of the image.
279 image->SetPalette(wxPalette((int) paletteLength, &palette[0],
280 &palette[paletteLength * 1], &palette[paletteLength * 2]));
281 #endif // wxUSE_PALETTE
282
283 }
284
285 wxON_BLOCK_EXIT1(free, palette);
286
287 // Handle the various TGA formats we support.
288
289 switch (imageType)
290 {
291 // Raw indexed.
292
293 case 1:
294 {
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 {
321 Palette_GetRGB(palette, paletteLength,
322 imageData[index], &r, &g, &b);
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 {
337 Palette_GetRGB(palette, paletteLength,
338 imageData[index], &r, &g, &b);
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 {
489 unsigned char r;
490 unsigned char g;
491 unsigned char b;
492
493 // Decode the RLE data.
494
495 int rc = DecodeRLE(imageData, imageSize, pixelSize, stream);
496 if ( rc != wxTGA_OK )
497 return rc;
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 {
517 Palette_GetRGB(palette, paletteLength,
518 imageData[index], &r, &g, &b);
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 {
533 Palette_GetRGB(palette, paletteLength,
534 imageData[index], &r, &g, &b);
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
556 int rc = DecodeRLE(imageData, imageSize, pixelSize, stream);
557 if ( rc != wxTGA_OK )
558 return rc;
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
636 int rc = DecodeRLE(imageData, imageSize, pixelSize, stream);
637 if ( rc != wxTGA_OK )
638 return rc;
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
689 return wxTGA_OK;
690 }
691
692 static
693 int SaveTGA(const wxImage& image, wxOutputStream *stream)
694 {
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 }
757
758 return wxTGA_OK;
759 }
760
761 // ----------------------------------------------------------------------------
762 // wxTGAHandler
763 // ----------------------------------------------------------------------------
764
765 bool wxTGAHandler::LoadFile(wxImage* image,
766 wxInputStream& stream,
767 bool verbose,
768 int WXUNUSED(index))
769 {
770 if ( !CanRead(stream) )
771 {
772 if ( verbose )
773 {
774 wxLogError(wxT("TGA: this is not a TGA file."));
775 }
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
797 case wxTGA_IOERR:
798 wxLogError(wxT("TGA: couldn't read image data."));
799 break;
800
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
814 bool wxTGAHandler::SaveFile(wxImage* image, wxOutputStream& stream, bool verbose)
815 {
816 int error = SaveTGA(*image, &stream);
817
818 if ( error != wxTGA_OK )
819 {
820 if ( verbose )
821 {
822 switch ( error )
823 {
824 case wxTGA_MEMERR:
825 wxLogError(wxT("TGA: couldn't allocate memory."));
826 break;
827
828 case wxTGA_IOERR:
829 wxLogError(wxT("TGA: couldn't write image data."));
830 break;
831
832 default:
833 wxLogError(wxT("TGA: unknown error!"));
834 }
835 }
836
837 return false;
838 }
839
840 return true;
841 }
842
843 bool wxTGAHandler::DoCanRead(wxInputStream& stream)
844 {
845 // read the fixed-size TGA headers
846 unsigned char hdr[HDR_SIZE];
847 stream.Read(hdr, HDR_SIZE); // it's ok to modify the stream position here
848
849 // Check whether we can read the file or not.
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