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