]>
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 // Repeat the pixel length times.
135 if ( !stream
.Read(buf
, pixelSize
) )
138 for (unsigned int i
= 0; i
< length
; i
++)
140 memcpy(imageData
, buf
, pixelSize
);
142 imageData
+= pixelSize
;
147 // Get the run length of the packet.
150 length
= current
* pixelSize
;
154 // Write the next length pixels directly to the image data.
155 if ( !stream
.Read(imageData
, length
) )
166 Mimic the behaviour of wxPalette.GetRGB and the way the TGA image handler
167 used it. That is: don't check the return value of GetRGB and continue decoding
168 using previous RGB values.
170 It might be better to check for palette index bounds and stop decoding if
171 it's out of range (and add something like wxTGA_DATAERR to indicate unexpected
175 void Palette_GetRGB(const unsigned char *palette
, unsigned int paletteCount
,
177 unsigned char *red
, unsigned char *green
, unsigned char *blue
)
179 if (index
>= paletteCount
)
184 *red
= palette
[index
];
185 *green
= palette
[(paletteCount
* 1) + index
];
186 *blue
= palette
[(paletteCount
* 2) + index
];
190 void Palette_SetRGB(unsigned char *palette
, unsigned int paletteCount
,
192 unsigned char red
, unsigned char green
, unsigned char blue
)
194 palette
[index
] = red
;
195 palette
[(paletteCount
* 1) + index
] = green
;
196 palette
[(paletteCount
* 2) + index
] = blue
;
200 int ReadTGA(wxImage
* image
, wxInputStream
& stream
)
202 // Read in the TGA header
203 unsigned char hdr
[HDR_SIZE
];
204 stream
.Read(hdr
, HDR_SIZE
);
206 short offset
= hdr
[HDR_OFFSET
] + HDR_SIZE
;
207 short colorType
= hdr
[HDR_COLORTYPE
];
208 short imageType
= hdr
[HDR_IMAGETYPE
];
209 unsigned int paletteLength
= hdr
[HDR_PALETTELENGTH
]
210 + 256 * hdr
[HDR_PALETTELENGTH
+ 1];
211 int width
= (hdr
[HDR_WIDTH
] + 256 * hdr
[HDR_WIDTH
+ 1]) -
212 (hdr
[HDR_XORIGIN
] + 256 * hdr
[HDR_XORIGIN
+ 1]);
213 int height
= (hdr
[HDR_HEIGHT
] + 256 * hdr
[HDR_HEIGHT
+ 1]) -
214 (hdr
[HDR_YORIGIN
] + 256 * hdr
[HDR_YORIGIN
+ 1]);
215 short bpp
= hdr
[HDR_BPP
];
216 short orientation
= hdr
[HDR_ORIENTATION
] & 0x20;
218 image
->Create(width
, height
);
225 const short pixelSize
= bpp
/ 8;
227 const unsigned long imageSize
= width
* height
* pixelSize
;
229 unsigned char *imageData
= (unsigned char* )malloc(imageSize
);
236 wxON_BLOCK_EXIT1(free
, imageData
);
238 unsigned char *dst
= image
->GetData();
240 unsigned char* alpha
= NULL
;
241 if (bpp
== 16 || bpp
== 32)
245 alpha
= image
->GetAlpha();
248 // Seek from the offset we got from the TGA header.
249 if (stream
.SeekI(offset
, wxFromStart
) == wxInvalidOffset
)
250 return wxTGA_INVFORMAT
;
252 unsigned char *palette
= NULL
;
253 // Load a palette if we have one.
254 if (colorType
== wxTGA_MAPPED
)
256 unsigned char buf
[3];
258 palette
= (unsigned char *) malloc(paletteLength
* 3);
260 for (unsigned int i
= 0; i
< paletteLength
; i
++)
264 Palette_SetRGB(palette
, paletteLength
, i
, buf
[2], buf
[1], buf
[0]);
268 // Set the palette of the image.
269 image
->SetPalette(wxPalette((int) paletteLength
, &palette
[0],
270 &palette
[paletteLength
* 1], &palette
[paletteLength
* 2]));
271 #endif // wxUSE_PALETTE
275 wxON_BLOCK_EXIT1(free
, palette
);
277 // Handle the various TGA formats we support.
289 // No compression read the data directly to imageData.
291 stream
.Read(imageData
, imageSize
);
293 // If orientation == 0, then the image is stored upside down.
294 // We need to store it right side up.
296 if (orientation
== 0)
298 FlipTGA(imageData
, width
, height
, pixelSize
);
301 // Handle the different pixel depths.
309 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
311 Palette_GetRGB(palette
, paletteLength
,
312 imageData
[index
], &r
, &g
, &b
);
325 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
327 Palette_GetRGB(palette
, paletteLength
,
328 imageData
[index
], &r
, &g
, &b
);
333 *(alpha
++) = (imageData
[index
+ 1] & 0x80) ? 0 : 255;
339 return wxTGA_INVFORMAT
;
348 // No compression read the data directly to imageData.
350 stream
.Read(imageData
, imageSize
);
352 // If orientation == 0, then the image is stored upside down.
353 // We need to store it right side up.
355 if (orientation
== 0)
357 FlipTGA(imageData
, width
, height
, pixelSize
);
360 // Handle the different pixel depths.
370 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
372 temp
= (imageData
[index
+ 1] & 0x7c) << 1;
376 temp
= ((imageData
[index
+ 1] & 0x03) << 6) | ((imageData
[index
] & 0xe0) >> 2);
380 temp
= (imageData
[index
] & 0x1f) << 3;
384 *(alpha
++) = (imageData
[index
+ 1] & 0x80) ? 0 : 255;
393 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
395 *(dst
++) = imageData
[index
+ 2];
396 *(dst
++) = imageData
[index
+ 1];
397 *(dst
++) = imageData
[index
];
406 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
408 *(dst
++) = imageData
[index
+ 2];
409 *(dst
++) = imageData
[index
+ 1];
410 *(dst
++) = imageData
[index
];
411 *(alpha
++) = imageData
[index
+ 3];
417 return wxTGA_INVFORMAT
;
426 // No compression read the data directly to imageData.
428 stream
.Read(imageData
, imageSize
);
430 // If orientation == 0, then the image is stored upside down.
431 // We need to store it right side up.
433 if (orientation
== 0)
435 FlipTGA(imageData
, width
, height
, pixelSize
);
438 // Handle the different pixel depths.
446 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
448 *(dst
++) = imageData
[index
];
449 *(dst
++) = imageData
[index
];
450 *(dst
++) = imageData
[index
];
459 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
461 *(dst
++) = imageData
[index
];
462 *(dst
++) = imageData
[index
];
463 *(dst
++) = imageData
[index
];
464 *(alpha
++) = imageData
[index
+ 1];
470 return wxTGA_INVFORMAT
;
483 // Decode the RLE data.
485 int rc
= DecodeRLE(imageData
, imageSize
, pixelSize
, stream
);
486 if ( rc
!= wxTGA_OK
)
489 // If orientation == 0, then the image is stored upside down.
490 // We need to store it right side up.
492 if (orientation
== 0)
494 FlipTGA(imageData
, width
, height
, pixelSize
);
497 // Handle the different pixel depths.
505 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
507 Palette_GetRGB(palette
, paletteLength
,
508 imageData
[index
], &r
, &g
, &b
);
521 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
523 Palette_GetRGB(palette
, paletteLength
,
524 imageData
[index
], &r
, &g
, &b
);
529 *(alpha
++) = (imageData
[index
+ 1] & 0x80) ? 0 : 255;
535 return wxTGA_INVFORMAT
;
544 // Decode the RLE data.
546 int rc
= DecodeRLE(imageData
, imageSize
, pixelSize
, stream
);
547 if ( rc
!= wxTGA_OK
)
550 // If orientation == 0, then the image is stored upside down.
551 // We need to store it right side up.
553 if (orientation
== 0)
555 FlipTGA(imageData
, width
, height
, pixelSize
);
558 // Handle the different pixel depths.
568 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
570 temp
= (imageData
[index
+ 1] & 0x7c) << 1;
574 temp
= ((imageData
[index
+ 1] & 0x03) << 6) | ((imageData
[index
] & 0xe0) >> 2);
578 temp
= (imageData
[index
] & 0x1f) << 3;
582 *(alpha
++) = (imageData
[index
+ 1] & 0x80) ? 0 : 255;
591 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
593 *(dst
++) = imageData
[index
+ 2];
594 *(dst
++) = imageData
[index
+ 1];
595 *(dst
++) = imageData
[index
];
604 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
606 *(dst
++) = imageData
[index
+ 2];
607 *(dst
++) = imageData
[index
+ 1];
608 *(dst
++) = imageData
[index
];
609 *(alpha
++) = imageData
[index
+ 3];
615 return wxTGA_INVFORMAT
;
624 // Decode the RLE data.
626 int rc
= DecodeRLE(imageData
, imageSize
, pixelSize
, stream
);
627 if ( rc
!= wxTGA_OK
)
630 // If orientation == 0, then the image is stored upside down.
631 // We need to store it right side up.
633 if (orientation
== 0)
635 FlipTGA(imageData
, width
, height
, pixelSize
);
638 // Handle the different pixel depths.
646 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
648 *(dst
++) = imageData
[index
];
649 *(dst
++) = imageData
[index
];
650 *(dst
++) = imageData
[index
];
659 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
661 *(dst
++) = imageData
[index
];
662 *(dst
++) = imageData
[index
];
663 *(dst
++) = imageData
[index
];
664 *(alpha
++) = imageData
[index
+ 1];
670 return wxTGA_INVFORMAT
;
676 return wxTGA_INVFORMAT
;
683 int SaveTGA(const wxImage
& image
, wxOutputStream
*stream
)
685 bool hasAlpha
= image
.HasAlpha();
686 unsigned bytesPerPixel
= 3 + (hasAlpha
? 1 : 0);
687 wxSize size
= image
.GetSize();
688 size_t scanlineSize
= size
.x
* bytesPerPixel
;
689 unsigned char *scanlineData
= (unsigned char *) malloc(scanlineSize
);
695 wxON_BLOCK_EXIT1(free
, scanlineData
);
697 // Compose and write the TGA header
698 unsigned char hdr
[HDR_SIZE
];
699 (void) memset(&hdr
, 0, HDR_SIZE
);
701 hdr
[HDR_COLORTYPE
] = wxTGA_UNMAPPED
;
702 hdr
[HDR_IMAGETYPE
] = 2 /* Uncompressed truecolour */;
704 hdr
[HDR_WIDTH
] = size
.x
& 0xFF;
705 hdr
[HDR_WIDTH
+ 1] = (size
.x
>> 8) & 0xFF;
707 hdr
[HDR_HEIGHT
] = size
.y
& 0xFF;
708 hdr
[HDR_HEIGHT
+ 1] = (size
.y
>> 8) & 0xFF;
710 hdr
[HDR_BPP
] = hasAlpha
? 32 : 24;
711 hdr
[HDR_ORIENTATION
] = 1 << 5; // set bit to indicate top-down order
714 hdr
[HDR_ORIENTATION
] |= 8; // number of alpha bits
717 if ( !stream
->Write(hdr
, HDR_SIZE
) )
723 // Write image data, converting RGB to BGR and adding alpha if applicable
725 unsigned char *src
= image
.GetData();
726 unsigned char *alpha
= image
.GetAlpha();
727 for (int y
= 0; y
< size
.y
; ++y
)
729 unsigned char *dst
= scanlineData
;
730 for (int x
= 0; x
< size
.x
; ++x
)
740 dst
+= bytesPerPixel
;
742 if ( !stream
->Write(scanlineData
, scanlineSize
) )
751 // ----------------------------------------------------------------------------
753 // ----------------------------------------------------------------------------
755 bool wxTGAHandler::LoadFile(wxImage
* image
,
756 wxInputStream
& stream
,
760 if ( !CanRead(stream
) )
764 wxLogError(wxT("TGA: this is not a TGA file."));
772 int error
= ReadTGA(image
, stream
);
773 if ( error
!= wxTGA_OK
)
779 case wxTGA_INVFORMAT
:
780 wxLogError(wxT("TGA: image format unsupported."));
784 wxLogError(wxT("TGA: couldn't allocate memory."));
788 wxLogError(wxT("TGA: couldn't read image data."));
792 wxLogError(wxT("TGA: unknown error!"));
804 bool wxTGAHandler::SaveFile(wxImage
* image
, wxOutputStream
& stream
, bool verbose
)
806 int error
= SaveTGA(*image
, &stream
);
808 if ( error
!= wxTGA_OK
)
815 wxLogError(wxT("TGA: couldn't allocate memory."));
819 wxLogError(wxT("TGA: couldn't write image data."));
823 wxLogError(wxT("TGA: unknown error!"));
833 bool wxTGAHandler::DoCanRead(wxInputStream
& stream
)
835 // read the fixed-size TGA headers
836 unsigned char hdr
[HDR_SIZE
];
837 stream
.Read(hdr
, HDR_SIZE
); // it's ok to modify the stream position here
839 // Check wether we can read the file or not.
841 short colorType
= hdr
[HDR_COLORTYPE
];
842 if ( colorType
!= wxTGA_UNMAPPED
&& colorType
!= wxTGA_MAPPED
)
847 short imageType
= hdr
[HDR_IMAGETYPE
];
848 if ( imageType
== 0 || imageType
== 32 || imageType
== 33 )
853 short bpp
= hdr
[HDR_BPP
];
854 if ( bpp
!= 8 && bpp
!= 16 && bpp
!= 24 && bpp
!= 32 )
862 #endif // wxUSE_STREAMS
864 #endif // wxUSE_IMAGE && wxUSE_TGA