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