]>
git.saurik.com Git - wxWidgets.git/blob - src/common/imagtga.cpp
1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxImage TGA handler
4 // Author: Seth Jackson
6 // Copyright: (c) 2005 Seth Jackson
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // ============================================================================
12 // ============================================================================
14 // ----------------------------------------------------------------------------
16 // ----------------------------------------------------------------------------
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
25 #if wxUSE_IMAGE && wxUSE_TGA
28 #include "wx/palette.h"
31 #include "wx/imagtga.h"
33 #include "wx/scopeguard.h"
35 // ----------------------------------------------------------------------------
37 // ----------------------------------------------------------------------------
55 HDR_PALETTELENGTH
= 5,
73 // ============================================================================
75 // ============================================================================
77 IMPLEMENT_DYNAMIC_CLASS(wxTGAHandler
, wxImageHandler
)
81 // ----------------------------------------------------------------------------
83 // ----------------------------------------------------------------------------
86 void FlipTGA(unsigned char* imageData
, int width
, int height
, short pixelSize
)
88 int lineLength
= width
* pixelSize
;
89 unsigned char *line1
= imageData
;
90 unsigned char *line2
= &imageData
[lineLength
* (height
- 1)];
93 for ( ; line1
< line2
; line2
-= (lineLength
* 2))
95 for (int index
= 0; index
< lineLength
; line1
++, line2
++, index
++)
104 // return wxTGA_OK or wxTGA_IOERR
106 int DecodeRLE(unsigned char* imageData
, unsigned long imageSize
,
107 short pixelSize
, wxInputStream
& stream
)
109 unsigned long index
= 0;
110 unsigned char current
;
112 unsigned char buf
[4];
114 while (index
< imageSize
)
116 int ch
= stream
.GetC();
123 if ( current
& 0x80 )
125 // Get the run length of the packet.
132 index
+= current
* pixelSize
;
134 if (index
>= imageSize
)
139 // Repeat the pixel length times.
140 if ( !stream
.Read(buf
, pixelSize
) )
143 for (unsigned int i
= 0; i
< length
; i
++)
145 memcpy(imageData
, buf
, pixelSize
);
147 imageData
+= pixelSize
;
152 // Get the run length of the packet.
155 length
= current
* pixelSize
;
159 if (index
>= imageSize
)
164 // Write the next length pixels directly to the image data.
165 if ( !stream
.Read(imageData
, length
) )
176 Mimic the behaviour of wxPalette.GetRGB and the way the TGA image handler
177 used it. That is: don't check the return value of GetRGB and continue decoding
178 using previous RGB values.
180 It might be better to check for palette index bounds and stop decoding if
181 it's out of range (and add something like wxTGA_DATAERR to indicate unexpected
185 void Palette_GetRGB(const unsigned char *palette
, unsigned int paletteCount
,
187 unsigned char *red
, unsigned char *green
, unsigned char *blue
)
189 if (index
>= paletteCount
)
194 *red
= palette
[index
];
195 *green
= palette
[(paletteCount
* 1) + index
];
196 *blue
= palette
[(paletteCount
* 2) + index
];
200 void Palette_SetRGB(unsigned char *palette
, unsigned int paletteCount
,
202 unsigned char red
, unsigned char green
, unsigned char blue
)
204 palette
[index
] = red
;
205 palette
[(paletteCount
* 1) + index
] = green
;
206 palette
[(paletteCount
* 2) + index
] = blue
;
210 int ReadTGA(wxImage
* image
, wxInputStream
& stream
)
212 // Read in the TGA header
213 unsigned char hdr
[HDR_SIZE
];
214 stream
.Read(hdr
, HDR_SIZE
);
216 short offset
= hdr
[HDR_OFFSET
] + HDR_SIZE
;
217 short colorType
= hdr
[HDR_COLORTYPE
];
218 short imageType
= hdr
[HDR_IMAGETYPE
];
219 unsigned int paletteLength
= hdr
[HDR_PALETTELENGTH
]
220 + 256 * hdr
[HDR_PALETTELENGTH
+ 1];
221 int width
= (hdr
[HDR_WIDTH
] + 256 * hdr
[HDR_WIDTH
+ 1]) -
222 (hdr
[HDR_XORIGIN
] + 256 * hdr
[HDR_XORIGIN
+ 1]);
223 int height
= (hdr
[HDR_HEIGHT
] + 256 * hdr
[HDR_HEIGHT
+ 1]) -
224 (hdr
[HDR_YORIGIN
] + 256 * hdr
[HDR_YORIGIN
+ 1]);
225 short bpp
= hdr
[HDR_BPP
];
226 short orientation
= hdr
[HDR_ORIENTATION
] & 0x20;
228 image
->Create(width
, height
);
235 const short pixelSize
= bpp
/ 8;
237 const unsigned long imageSize
= width
* height
* pixelSize
;
239 unsigned char *imageData
= (unsigned char* )malloc(imageSize
);
246 wxON_BLOCK_EXIT1(free
, imageData
);
248 unsigned char *dst
= image
->GetData();
250 unsigned char* alpha
= NULL
;
251 if (bpp
== 16 || bpp
== 32)
255 alpha
= image
->GetAlpha();
258 // Seek from the offset we got from the TGA header.
259 if (stream
.SeekI(offset
, wxFromStart
) == wxInvalidOffset
)
260 return wxTGA_INVFORMAT
;
262 unsigned char *palette
= NULL
;
263 // Load a palette if we have one.
264 if (colorType
== wxTGA_MAPPED
)
266 unsigned char buf
[3];
268 palette
= (unsigned char *) malloc(paletteLength
* 3);
270 for (unsigned int i
= 0; i
< paletteLength
; i
++)
274 Palette_SetRGB(palette
, paletteLength
, i
, buf
[2], buf
[1], buf
[0]);
278 // Set the palette of the image.
279 image
->SetPalette(wxPalette((int) paletteLength
, &palette
[0],
280 &palette
[paletteLength
* 1], &palette
[paletteLength
* 2]));
281 #endif // wxUSE_PALETTE
285 wxON_BLOCK_EXIT1(free
, palette
);
287 // Handle the various TGA formats we support.
299 // No compression read the data directly to imageData.
301 stream
.Read(imageData
, imageSize
);
303 // If orientation == 0, then the image is stored upside down.
304 // We need to store it right side up.
306 if (orientation
== 0)
308 FlipTGA(imageData
, width
, height
, pixelSize
);
311 // Handle the different pixel depths.
319 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
321 Palette_GetRGB(palette
, paletteLength
,
322 imageData
[index
], &r
, &g
, &b
);
335 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
337 Palette_GetRGB(palette
, paletteLength
,
338 imageData
[index
], &r
, &g
, &b
);
343 *(alpha
++) = (imageData
[index
+ 1] & 0x80) ? 0 : 255;
349 return wxTGA_INVFORMAT
;
358 // No compression read the data directly to imageData.
360 stream
.Read(imageData
, imageSize
);
362 // If orientation == 0, then the image is stored upside down.
363 // We need to store it right side up.
365 if (orientation
== 0)
367 FlipTGA(imageData
, width
, height
, pixelSize
);
370 // Handle the different pixel depths.
380 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
382 temp
= (imageData
[index
+ 1] & 0x7c) << 1;
386 temp
= ((imageData
[index
+ 1] & 0x03) << 6) | ((imageData
[index
] & 0xe0) >> 2);
390 temp
= (imageData
[index
] & 0x1f) << 3;
394 *(alpha
++) = (imageData
[index
+ 1] & 0x80) ? 0 : 255;
403 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
405 *(dst
++) = imageData
[index
+ 2];
406 *(dst
++) = imageData
[index
+ 1];
407 *(dst
++) = imageData
[index
];
416 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
418 *(dst
++) = imageData
[index
+ 2];
419 *(dst
++) = imageData
[index
+ 1];
420 *(dst
++) = imageData
[index
];
421 *(alpha
++) = imageData
[index
+ 3];
427 return wxTGA_INVFORMAT
;
436 // No compression read the data directly to imageData.
438 stream
.Read(imageData
, imageSize
);
440 // If orientation == 0, then the image is stored upside down.
441 // We need to store it right side up.
443 if (orientation
== 0)
445 FlipTGA(imageData
, width
, height
, pixelSize
);
448 // Handle the different pixel depths.
456 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
458 *(dst
++) = imageData
[index
];
459 *(dst
++) = imageData
[index
];
460 *(dst
++) = imageData
[index
];
469 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
471 *(dst
++) = imageData
[index
];
472 *(dst
++) = imageData
[index
];
473 *(dst
++) = imageData
[index
];
474 *(alpha
++) = imageData
[index
+ 1];
480 return wxTGA_INVFORMAT
;
493 // Decode the RLE data.
495 int rc
= DecodeRLE(imageData
, imageSize
, pixelSize
, stream
);
496 if ( rc
!= wxTGA_OK
)
499 // If orientation == 0, then the image is stored upside down.
500 // We need to store it right side up.
502 if (orientation
== 0)
504 FlipTGA(imageData
, width
, height
, pixelSize
);
507 // Handle the different pixel depths.
515 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
517 Palette_GetRGB(palette
, paletteLength
,
518 imageData
[index
], &r
, &g
, &b
);
531 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
533 Palette_GetRGB(palette
, paletteLength
,
534 imageData
[index
], &r
, &g
, &b
);
539 *(alpha
++) = (imageData
[index
+ 1] & 0x80) ? 0 : 255;
545 return wxTGA_INVFORMAT
;
554 // Decode the RLE data.
556 int rc
= DecodeRLE(imageData
, imageSize
, pixelSize
, stream
);
557 if ( rc
!= wxTGA_OK
)
560 // If orientation == 0, then the image is stored upside down.
561 // We need to store it right side up.
563 if (orientation
== 0)
565 FlipTGA(imageData
, width
, height
, pixelSize
);
568 // Handle the different pixel depths.
578 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
580 temp
= (imageData
[index
+ 1] & 0x7c) << 1;
584 temp
= ((imageData
[index
+ 1] & 0x03) << 6) | ((imageData
[index
] & 0xe0) >> 2);
588 temp
= (imageData
[index
] & 0x1f) << 3;
592 *(alpha
++) = (imageData
[index
+ 1] & 0x80) ? 0 : 255;
601 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
603 *(dst
++) = imageData
[index
+ 2];
604 *(dst
++) = imageData
[index
+ 1];
605 *(dst
++) = imageData
[index
];
614 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
616 *(dst
++) = imageData
[index
+ 2];
617 *(dst
++) = imageData
[index
+ 1];
618 *(dst
++) = imageData
[index
];
619 *(alpha
++) = imageData
[index
+ 3];
625 return wxTGA_INVFORMAT
;
634 // Decode the RLE data.
636 int rc
= DecodeRLE(imageData
, imageSize
, pixelSize
, stream
);
637 if ( rc
!= wxTGA_OK
)
640 // If orientation == 0, then the image is stored upside down.
641 // We need to store it right side up.
643 if (orientation
== 0)
645 FlipTGA(imageData
, width
, height
, pixelSize
);
648 // Handle the different pixel depths.
656 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
658 *(dst
++) = imageData
[index
];
659 *(dst
++) = imageData
[index
];
660 *(dst
++) = imageData
[index
];
669 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
671 *(dst
++) = imageData
[index
];
672 *(dst
++) = imageData
[index
];
673 *(dst
++) = imageData
[index
];
674 *(alpha
++) = imageData
[index
+ 1];
680 return wxTGA_INVFORMAT
;
686 return wxTGA_INVFORMAT
;
693 int SaveTGA(const wxImage
& image
, wxOutputStream
*stream
)
695 bool hasAlpha
= image
.HasAlpha();
696 unsigned bytesPerPixel
= 3 + (hasAlpha
? 1 : 0);
697 wxSize size
= image
.GetSize();
698 size_t scanlineSize
= size
.x
* bytesPerPixel
;
699 unsigned char *scanlineData
= (unsigned char *) malloc(scanlineSize
);
705 wxON_BLOCK_EXIT1(free
, scanlineData
);
707 // Compose and write the TGA header
708 unsigned char hdr
[HDR_SIZE
];
709 (void) memset(&hdr
, 0, HDR_SIZE
);
711 hdr
[HDR_COLORTYPE
] = wxTGA_UNMAPPED
;
712 hdr
[HDR_IMAGETYPE
] = 2 /* Uncompressed truecolour */;
714 hdr
[HDR_WIDTH
] = size
.x
& 0xFF;
715 hdr
[HDR_WIDTH
+ 1] = (size
.x
>> 8) & 0xFF;
717 hdr
[HDR_HEIGHT
] = size
.y
& 0xFF;
718 hdr
[HDR_HEIGHT
+ 1] = (size
.y
>> 8) & 0xFF;
720 hdr
[HDR_BPP
] = hasAlpha
? 32 : 24;
721 hdr
[HDR_ORIENTATION
] = 1 << 5; // set bit to indicate top-down order
724 hdr
[HDR_ORIENTATION
] |= 8; // number of alpha bits
727 if ( !stream
->Write(hdr
, HDR_SIZE
) )
733 // Write image data, converting RGB to BGR and adding alpha if applicable
735 unsigned char *src
= image
.GetData();
736 unsigned char *alpha
= image
.GetAlpha();
737 for (int y
= 0; y
< size
.y
; ++y
)
739 unsigned char *dst
= scanlineData
;
740 for (int x
= 0; x
< size
.x
; ++x
)
750 dst
+= bytesPerPixel
;
752 if ( !stream
->Write(scanlineData
, scanlineSize
) )
761 // ----------------------------------------------------------------------------
763 // ----------------------------------------------------------------------------
765 bool wxTGAHandler::LoadFile(wxImage
* image
,
766 wxInputStream
& stream
,
770 if ( !CanRead(stream
) )
774 wxLogError(wxT("TGA: this is not a TGA file."));
782 int error
= ReadTGA(image
, stream
);
783 if ( error
!= wxTGA_OK
)
789 case wxTGA_INVFORMAT
:
790 wxLogError(wxT("TGA: image format unsupported."));
794 wxLogError(wxT("TGA: couldn't allocate memory."));
798 wxLogError(wxT("TGA: couldn't read image data."));
802 wxLogError(wxT("TGA: unknown error!"));
814 bool wxTGAHandler::SaveFile(wxImage
* image
, wxOutputStream
& stream
, bool verbose
)
816 int error
= SaveTGA(*image
, &stream
);
818 if ( error
!= wxTGA_OK
)
825 wxLogError(wxT("TGA: couldn't allocate memory."));
829 wxLogError(wxT("TGA: couldn't write image data."));
833 wxLogError(wxT("TGA: unknown error!"));
843 bool wxTGAHandler::DoCanRead(wxInputStream
& stream
)
845 // read the fixed-size TGA headers
846 unsigned char hdr
[HDR_SIZE
];
847 stream
.Read(hdr
, HDR_SIZE
); // it's ok to modify the stream position here
849 // Check wether we can read the file or not.
851 short colorType
= hdr
[HDR_COLORTYPE
];
852 if ( colorType
!= wxTGA_UNMAPPED
&& colorType
!= wxTGA_MAPPED
)
857 short imageType
= hdr
[HDR_IMAGETYPE
];
858 if ( imageType
== 0 || imageType
== 32 || imageType
== 33 )
863 short bpp
= hdr
[HDR_BPP
];
864 if ( bpp
!= 8 && bpp
!= 16 && bpp
!= 24 && bpp
!= 32 )
872 #endif // wxUSE_STREAMS
874 #endif // wxUSE_IMAGE && wxUSE_TGA