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