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