added IO errors handling to TGA reading code
[wxWidgets.git] / src / common / imagtga.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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 // Repeat the pixel length times.
135 if ( !stream.Read(buf, pixelSize) )
136 return wxTGA_IOERR;
137
138 for (unsigned int i = 0; i < length; i++)
139 {
140 memcpy(imageData, buf, pixelSize);
141
142 imageData += pixelSize;
143 }
144 }
145 else // Raw packet.
146 {
147 // Get the run length of the packet.
148 current++;
149
150 length = current * pixelSize;
151
152 index += length;
153
154 // Write the next length pixels directly to the image data.
155 if ( !stream.Read(imageData, length) )
156 return wxTGA_IOERR;
157
158 imageData += length;
159 }
160 }
161
162 return wxTGA_OK;
163 }
164
165 static
166 int ReadTGA(wxImage* image, wxInputStream& stream)
167 {
168 // Read in the TGA header
169 unsigned char hdr[HDR_SIZE];
170 stream.Read(hdr, HDR_SIZE);
171
172 short offset = hdr[HDR_OFFSET] + HDR_SIZE;
173 short colorType = hdr[HDR_COLORTYPE];
174 short imageType = hdr[HDR_IMAGETYPE];
175 int paletteLength = hdr[HDR_PALETTELENGTH] + 256 * hdr[HDR_PALETTELENGTH + 1];
176 int width = (hdr[HDR_WIDTH] + 256 * hdr[HDR_WIDTH + 1]) -
177 (hdr[HDR_XORIGIN] + 256 * hdr[HDR_XORIGIN + 1]);
178 int height = (hdr[HDR_HEIGHT] + 256 * hdr[HDR_HEIGHT + 1]) -
179 (hdr[HDR_YORIGIN] + 256 * hdr[HDR_YORIGIN + 1]);
180 short bpp = hdr[HDR_BPP];
181 short orientation = hdr[HDR_ORIENTATION] & 0x20;
182
183 image->Create(width, height);
184
185 if (!image->Ok())
186 {
187 return wxTGA_MEMERR;
188 }
189
190 const short pixelSize = bpp / 8;
191
192 const unsigned long imageSize = width * height * pixelSize;
193
194 unsigned char *imageData = (unsigned char* )malloc(imageSize);
195
196 if (!imageData)
197 {
198 return wxTGA_MEMERR;
199 }
200
201 wxON_BLOCK_EXIT1(free, imageData);
202
203 unsigned char *dst = image->GetData();
204
205 unsigned char* alpha = NULL;
206 if (bpp == 16 || bpp == 32)
207 {
208 image->SetAlpha();
209
210 alpha = image->GetAlpha();
211 }
212
213 // Seek from the offset we got from the TGA header.
214 stream.SeekI(offset, wxFromStart);
215
216 // Load a palette if we have one.
217 if (colorType == wxTGA_MAPPED)
218 {
219 unsigned char buf[3];
220
221 unsigned char* r = new unsigned char[paletteLength];
222 unsigned char* g = new unsigned char[paletteLength];
223 unsigned char* b = new unsigned char[paletteLength];
224
225 for (int i = 0; i < paletteLength; i++)
226 {
227 stream.Read(buf, 3);
228
229 r[i] = buf[2];
230 g[i] = buf[1];
231 b[i] = buf[0];
232 }
233
234 #if wxUSE_PALETTE
235 // Set the palette of the image.
236 image->SetPalette(wxPalette(paletteLength, r, g, b));
237 #endif // wxUSE_PALETTE
238
239 delete[] r;
240 delete[] g;
241 delete[] b;
242 }
243
244 // Handle the various TGA formats we support.
245
246 switch (imageType)
247 {
248 #if wxUSE_PALETTE
249 // Raw indexed.
250
251 case 1:
252 {
253 const wxPalette& palette = image->GetPalette();
254 unsigned char r;
255 unsigned char g;
256 unsigned char b;
257
258 // No compression read the data directly to imageData.
259
260 stream.Read(imageData, imageSize);
261
262 // If orientation == 0, then the image is stored upside down.
263 // We need to store it right side up.
264
265 if (orientation == 0)
266 {
267 FlipTGA(imageData, width, height, pixelSize);
268 }
269
270 // Handle the different pixel depths.
271
272 switch (bpp)
273 {
274 // 8 bpp.
275
276 case 8:
277 {
278 for (unsigned long index = 0; index < imageSize; index += pixelSize)
279 {
280 palette.GetRGB(imageData[index], &r, &g, &b);
281
282 *(dst++) = r;
283 *(dst++) = g;
284 *(dst++) = b;
285 }
286 }
287 break;
288
289 // 16 bpp.
290
291 case 16:
292 {
293 for (unsigned long index = 0; index < imageSize; index += pixelSize)
294 {
295 palette.GetRGB(imageData[index], &r, &g, &b);
296
297 *(dst++) = r;
298 *(dst++) = g;
299 *(dst++) = b;
300 *(alpha++) = (imageData[index + 1] & 0x80) ? 0 : 255;
301 }
302 }
303 break;
304
305 default:
306 return wxTGA_INVFORMAT;
307 }
308 }
309 break;
310 #endif // wxUSE_PALETTE
311
312 // Raw RGB.
313
314 case 2:
315 {
316 // No compression read the data directly to imageData.
317
318 stream.Read(imageData, imageSize);
319
320 // If orientation == 0, then the image is stored upside down.
321 // We need to store it right side up.
322
323 if (orientation == 0)
324 {
325 FlipTGA(imageData, width, height, pixelSize);
326 }
327
328 // Handle the different pixel depths.
329
330 switch (bpp)
331 {
332 //16 bpp.
333
334 case 16:
335 {
336 unsigned char temp;
337
338 for (unsigned long index = 0; index < imageSize; index += pixelSize)
339 {
340 temp = (imageData[index + 1] & 0x7c) << 1;
341 temp |= temp >> 5;
342 *(dst++) = temp;
343
344 temp = ((imageData[index + 1] & 0x03) << 6) | ((imageData[index] & 0xe0) >> 2);
345 temp |= temp >> 5;
346 *(dst++) = temp;
347
348 temp = (imageData[index] & 0x1f) << 3;
349 temp |= temp >> 5;
350 *(dst++) = temp;
351
352 *(alpha++) = (imageData[index + 1] & 0x80) ? 0 : 255;
353 }
354 }
355 break;
356
357 // 24 bpp.
358
359 case 24:
360 {
361 for (unsigned long index = 0; index < imageSize; index += pixelSize)
362 {
363 *(dst++) = imageData[index + 2];
364 *(dst++) = imageData[index + 1];
365 *(dst++) = imageData[index];
366 }
367 }
368 break;
369
370 // 32 bpp.
371
372 case 32:
373 {
374 for (unsigned long index = 0; index < imageSize; index += pixelSize)
375 {
376 *(dst++) = imageData[index + 2];
377 *(dst++) = imageData[index + 1];
378 *(dst++) = imageData[index];
379 *(alpha++) = imageData[index + 3];
380 }
381 }
382 break;
383
384 default:
385 return wxTGA_INVFORMAT;
386 }
387 }
388 break;
389
390 // Raw grayscale.
391
392 case 3:
393 {
394 // No compression read the data directly to imageData.
395
396 stream.Read(imageData, imageSize);
397
398 // If orientation == 0, then the image is stored upside down.
399 // We need to store it right side up.
400
401 if (orientation == 0)
402 {
403 FlipTGA(imageData, width, height, pixelSize);
404 }
405
406 // Handle the different pixel depths.
407
408 switch (bpp)
409 {
410 // 8 bpp.
411
412 case 8:
413 {
414 for (unsigned long index = 0; index < imageSize; index += pixelSize)
415 {
416 *(dst++) = imageData[index];
417 *(dst++) = imageData[index];
418 *(dst++) = imageData[index];
419 }
420 }
421 break;
422
423 // 16 bpp.
424
425 case 16:
426 {
427 for (unsigned long index = 0; index < imageSize; index += pixelSize)
428 {
429 *(dst++) = imageData[index];
430 *(dst++) = imageData[index];
431 *(dst++) = imageData[index];
432 *(alpha++) = imageData[index + 1];
433 }
434 }
435 break;
436
437 default:
438 return wxTGA_INVFORMAT;
439 }
440 }
441 break;
442
443 #if wxUSE_PALETTE
444 // RLE indexed.
445
446 case 9:
447 {
448 const wxPalette& palette = image->GetPalette();
449 unsigned char r;
450 unsigned char g;
451 unsigned char b;
452
453 // Decode the RLE data.
454
455 int rc = DecodeRLE(imageData, imageSize, pixelSize, stream);
456 if ( rc != wxTGA_OK )
457 return rc;
458
459 // If orientation == 0, then the image is stored upside down.
460 // We need to store it right side up.
461
462 if (orientation == 0)
463 {
464 FlipTGA(imageData, width, height, pixelSize);
465 }
466
467 // Handle the different pixel depths.
468
469 switch (bpp)
470 {
471 // 8 bpp.
472
473 case 8:
474 {
475 for (unsigned long index = 0; index < imageSize; index += pixelSize)
476 {
477 palette.GetRGB(imageData[index], &r, &g, &b);
478
479 *(dst++) = r;
480 *(dst++) = g;
481 *(dst++) = b;
482 }
483 }
484 break;
485
486 // 16 bpp.
487
488 case 16:
489 {
490 for (unsigned long index = 0; index < imageSize; index += pixelSize)
491 {
492 palette.GetRGB(imageData[index], &r, &g, &b);
493
494 *(dst++) = r;
495 *(dst++) = g;
496 *(dst++) = b;
497 *(alpha++) = (imageData[index + 1] & 0x80) ? 0 : 255;
498 }
499 }
500 break;
501
502 default:
503 return wxTGA_INVFORMAT;
504 }
505 }
506 break;
507 #endif // wxUSE_PALETTE
508
509 // RLE RGB.
510
511 case 10:
512 {
513 // Decode the RLE data.
514
515 int rc = DecodeRLE(imageData, imageSize, pixelSize, stream);
516 if ( rc != wxTGA_OK )
517 return rc;
518
519 // If orientation == 0, then the image is stored upside down.
520 // We need to store it right side up.
521
522 if (orientation == 0)
523 {
524 FlipTGA(imageData, width, height, pixelSize);
525 }
526
527 // Handle the different pixel depths.
528
529 switch (bpp)
530 {
531 //16 bpp.
532
533 case 16:
534 {
535 unsigned char temp;
536
537 for (unsigned long index = 0; index < imageSize; index += pixelSize)
538 {
539 temp = (imageData[index + 1] & 0x7c) << 1;
540 temp |= temp >> 5;
541 *(dst++) = temp;
542
543 temp = ((imageData[index + 1] & 0x03) << 6) | ((imageData[index] & 0xe0) >> 2);
544 temp |= temp >> 5;
545 *(dst++) = temp;
546
547 temp = (imageData[index] & 0x1f) << 3;
548 temp |= temp >> 5;
549 *(dst++) = temp;
550
551 *(alpha++) = (imageData[index + 1] & 0x80) ? 0 : 255;
552 }
553 }
554 break;
555
556 // 24 bpp.
557
558 case 24:
559 {
560 for (unsigned long index = 0; index < imageSize; index += pixelSize)
561 {
562 *(dst++) = imageData[index + 2];
563 *(dst++) = imageData[index + 1];
564 *(dst++) = imageData[index];
565 }
566 }
567 break;
568
569 // 32 bpp.
570
571 case 32:
572 {
573 for (unsigned long index = 0; index < imageSize; index += pixelSize)
574 {
575 *(dst++) = imageData[index + 2];
576 *(dst++) = imageData[index + 1];
577 *(dst++) = imageData[index];
578 *(alpha++) = imageData[index + 3];
579 }
580 }
581 break;
582
583 default:
584 return wxTGA_INVFORMAT;
585 }
586 }
587 break;
588
589 // RLE grayscale.
590
591 case 11:
592 {
593 // Decode the RLE data.
594
595 int rc = DecodeRLE(imageData, imageSize, pixelSize, stream);
596 if ( rc != wxTGA_OK )
597 return rc;
598
599 // If orientation == 0, then the image is stored upside down.
600 // We need to store it right side up.
601
602 if (orientation == 0)
603 {
604 FlipTGA(imageData, width, height, pixelSize);
605 }
606
607 // Handle the different pixel depths.
608
609 switch (bpp)
610 {
611 // 8 bpp.
612
613 case 8:
614 {
615 for (unsigned long index = 0; index < imageSize; index += pixelSize)
616 {
617 *(dst++) = imageData[index];
618 *(dst++) = imageData[index];
619 *(dst++) = imageData[index];
620 }
621 }
622 break;
623
624 // 16 bpp.
625
626 case 16:
627 {
628 for (unsigned long index = 0; index < imageSize; index += pixelSize)
629 {
630 *(dst++) = imageData[index];
631 *(dst++) = imageData[index];
632 *(dst++) = imageData[index];
633 *(alpha++) = imageData[index + 1];
634 }
635 }
636 break;
637
638 default:
639 return wxTGA_INVFORMAT;
640 }
641 }
642 break;
643
644 default:
645 return wxTGA_INVFORMAT;
646 }
647
648 return wxTGA_OK;
649 }
650
651 static
652 int SaveTGA(wxImage* WXUNUSED(image), wxOutputStream& WXUNUSED(stream))
653 {
654 wxLogError(wxT("Saving in TGA format is not implemented."));
655
656 return wxTGA_OK;
657 }
658
659 // ----------------------------------------------------------------------------
660 // wxTGAHandler
661 // ----------------------------------------------------------------------------
662
663 bool wxTGAHandler::LoadFile(wxImage* image,
664 wxInputStream& stream,
665 bool verbose,
666 int WXUNUSED(index))
667 {
668 if ( !CanRead(stream) )
669 {
670 if ( verbose )
671 wxLogError(wxT("TGA: this is not a TGA file."));
672
673 return false;
674 }
675
676 image->Destroy();
677
678 int error = ReadTGA(image, stream);
679 if ( error != wxTGA_OK )
680 {
681 if ( verbose )
682 {
683 switch ( error )
684 {
685 case wxTGA_INVFORMAT:
686 wxLogError(wxT("TGA: image format unsupported."));
687 break;
688
689 case wxTGA_MEMERR:
690 wxLogError(wxT("TGA: couldn't allocate memory."));
691 break;
692
693 case wxTGA_IOERR:
694 wxLogError(wxT("TGA: couldn't read image data."));
695 break;
696
697 default:
698 wxLogError(wxT("TGA: unknown error!"));
699 }
700 }
701
702 image->Destroy();
703
704 return false;
705 }
706
707 return true;
708 }
709
710 bool wxTGAHandler::SaveFile(wxImage* image, wxOutputStream& stream, bool verbose)
711 {
712 int error = SaveTGA(image, stream);
713
714 if ( error != wxTGA_OK )
715 {
716 if ( verbose )
717 {
718 switch ( error )
719 {
720 case wxTGA_INVFORMAT:
721 wxLogError(wxT("TGA: invalid image."));
722 break;
723
724 case wxTGA_MEMERR:
725 wxLogError(wxT("TGA: couldn't allocate memory."));
726 break;
727
728 default:
729 wxLogError(wxT("TGA: unknown error!"));
730 }
731 }
732
733 return false;
734 }
735
736 return true;
737 }
738
739 bool wxTGAHandler::DoCanRead(wxInputStream& stream)
740 {
741 // read the fixed-size TGA headers
742 unsigned char hdr[HDR_SIZE];
743 stream.Read(hdr, HDR_SIZE);
744
745 // Check wether we can read the file or not.
746
747 short colorType = hdr[HDR_COLORTYPE];
748 if ( colorType != wxTGA_UNMAPPED && colorType != wxTGA_MAPPED )
749 {
750 return false;
751 }
752
753 short imageType = hdr[HDR_IMAGETYPE];
754 if ( imageType == 0 || imageType == 32 || imageType == 33 )
755 {
756 return false;
757 }
758
759 short bpp = hdr[HDR_BPP];
760 if ( bpp != 8 && bpp != 16 && bpp != 24 && bpp != 32 )
761 {
762 return false;
763 }
764
765 return true;
766 }
767
768 #endif // wxUSE_STREAMS
769
770 #endif // wxUSE_IMAGE && wxUSE_TGA