]>
git.saurik.com Git - wxWidgets.git/blob - src/common/imagtga.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/imagtga.cpp
3 // Purpose: wxImage TGA handler
4 // Author: Seth Jackson
5 // Copyright: (c) 2005 Seth Jackson
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
9 // ============================================================================
11 // ============================================================================
13 // ----------------------------------------------------------------------------
15 // ----------------------------------------------------------------------------
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
24 #if wxUSE_IMAGE && wxUSE_TGA
27 #include "wx/palette.h"
30 #include "wx/imagtga.h"
32 #include "wx/scopeguard.h"
34 // ----------------------------------------------------------------------------
36 // ----------------------------------------------------------------------------
54 HDR_PALETTELENGTH
= 5,
72 // ============================================================================
74 // ============================================================================
76 IMPLEMENT_DYNAMIC_CLASS(wxTGAHandler
, wxImageHandler
)
80 // ----------------------------------------------------------------------------
82 // ----------------------------------------------------------------------------
85 void FlipTGA(unsigned char* imageData
, int width
, int height
, short pixelSize
)
87 int lineLength
= width
* pixelSize
;
88 unsigned char *line1
= imageData
;
89 unsigned char *line2
= &imageData
[lineLength
* (height
- 1)];
92 for ( ; line1
< line2
; line2
-= (lineLength
* 2))
94 for (int index
= 0; index
< lineLength
; line1
++, line2
++, index
++)
103 // return wxTGA_OK or wxTGA_IOERR
105 int DecodeRLE(unsigned char* imageData
, unsigned long imageSize
,
106 short pixelSize
, wxInputStream
& stream
)
108 unsigned long index
= 0;
109 unsigned char current
;
111 unsigned char buf
[4];
113 while (index
< imageSize
)
115 int ch
= stream
.GetC();
122 if ( current
& 0x80 )
124 // Get the run length of the packet.
131 index
+= current
* pixelSize
;
133 if (index
>= imageSize
)
138 // Repeat the pixel length times.
139 if ( !stream
.Read(buf
, pixelSize
) )
142 for (unsigned int i
= 0; i
< length
; i
++)
144 memcpy(imageData
, buf
, pixelSize
);
146 imageData
+= pixelSize
;
151 // Get the run length of the packet.
154 length
= current
* pixelSize
;
158 if (index
>= imageSize
)
163 // Write the next length pixels directly to the image data.
164 if ( !stream
.Read(imageData
, length
) )
175 Mimic the behaviour of wxPalette.GetRGB and the way the TGA image handler
176 used it. That is: don't check the return value of GetRGB and continue decoding
177 using previous RGB values.
179 It might be better to check for palette index bounds and stop decoding if
180 it's out of range (and add something like wxTGA_DATAERR to indicate unexpected
184 void Palette_GetRGB(const unsigned char *palette
, unsigned int paletteCount
,
186 unsigned char *red
, unsigned char *green
, unsigned char *blue
)
188 if (index
>= paletteCount
)
193 *red
= palette
[index
];
194 *green
= palette
[(paletteCount
* 1) + index
];
195 *blue
= palette
[(paletteCount
* 2) + index
];
199 void Palette_SetRGB(unsigned char *palette
, unsigned int paletteCount
,
201 unsigned char red
, unsigned char green
, unsigned char blue
)
203 palette
[index
] = red
;
204 palette
[(paletteCount
* 1) + index
] = green
;
205 palette
[(paletteCount
* 2) + index
] = blue
;
209 int ReadTGA(wxImage
* image
, wxInputStream
& stream
)
211 // Read in the TGA header
212 unsigned char hdr
[HDR_SIZE
];
213 stream
.Read(hdr
, HDR_SIZE
);
215 short offset
= hdr
[HDR_OFFSET
] + HDR_SIZE
;
216 short colorType
= hdr
[HDR_COLORTYPE
];
217 short imageType
= hdr
[HDR_IMAGETYPE
];
218 unsigned int paletteLength
= hdr
[HDR_PALETTELENGTH
]
219 + 256 * hdr
[HDR_PALETTELENGTH
+ 1];
220 int width
= (hdr
[HDR_WIDTH
] + 256 * hdr
[HDR_WIDTH
+ 1]) -
221 (hdr
[HDR_XORIGIN
] + 256 * hdr
[HDR_XORIGIN
+ 1]);
222 int height
= (hdr
[HDR_HEIGHT
] + 256 * hdr
[HDR_HEIGHT
+ 1]) -
223 (hdr
[HDR_YORIGIN
] + 256 * hdr
[HDR_YORIGIN
+ 1]);
224 short bpp
= hdr
[HDR_BPP
];
225 short orientation
= hdr
[HDR_ORIENTATION
] & 0x20;
227 image
->Create(width
, height
);
234 const short pixelSize
= bpp
/ 8;
236 const unsigned long imageSize
= width
* height
* pixelSize
;
238 unsigned char *imageData
= (unsigned char* )malloc(imageSize
);
245 wxON_BLOCK_EXIT1(free
, imageData
);
247 unsigned char *dst
= image
->GetData();
249 unsigned char* alpha
= NULL
;
250 if (bpp
== 16 || bpp
== 32)
254 alpha
= image
->GetAlpha();
257 // Seek from the offset we got from the TGA header.
258 if (stream
.SeekI(offset
, wxFromStart
) == wxInvalidOffset
)
259 return wxTGA_INVFORMAT
;
261 unsigned char *palette
= NULL
;
262 // Load a palette if we have one.
263 if (colorType
== wxTGA_MAPPED
)
265 unsigned char buf
[3];
267 palette
= (unsigned char *) malloc(paletteLength
* 3);
269 for (unsigned int i
= 0; i
< paletteLength
; i
++)
273 Palette_SetRGB(palette
, paletteLength
, i
, buf
[2], buf
[1], buf
[0]);
277 // Set the palette of the image.
278 image
->SetPalette(wxPalette((int) paletteLength
, &palette
[0],
279 &palette
[paletteLength
* 1], &palette
[paletteLength
* 2]));
280 #endif // wxUSE_PALETTE
284 wxON_BLOCK_EXIT1(free
, palette
);
286 // Handle the various TGA formats we support.
298 // No compression read the data directly to imageData.
300 stream
.Read(imageData
, imageSize
);
302 // If orientation == 0, then the image is stored upside down.
303 // We need to store it right side up.
305 if (orientation
== 0)
307 FlipTGA(imageData
, width
, height
, pixelSize
);
310 // Handle the different pixel depths.
318 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
320 Palette_GetRGB(palette
, paletteLength
,
321 imageData
[index
], &r
, &g
, &b
);
334 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
336 Palette_GetRGB(palette
, paletteLength
,
337 imageData
[index
], &r
, &g
, &b
);
342 *(alpha
++) = (imageData
[index
+ 1] & 0x80) ? 0 : 255;
348 return wxTGA_INVFORMAT
;
357 // No compression read the data directly to imageData.
359 stream
.Read(imageData
, imageSize
);
361 // If orientation == 0, then the image is stored upside down.
362 // We need to store it right side up.
364 if (orientation
== 0)
366 FlipTGA(imageData
, width
, height
, pixelSize
);
369 // Handle the different pixel depths.
379 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
381 temp
= (imageData
[index
+ 1] & 0x7c) << 1;
385 temp
= ((imageData
[index
+ 1] & 0x03) << 6) | ((imageData
[index
] & 0xe0) >> 2);
389 temp
= (imageData
[index
] & 0x1f) << 3;
393 *(alpha
++) = (imageData
[index
+ 1] & 0x80) ? 0 : 255;
402 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
404 *(dst
++) = imageData
[index
+ 2];
405 *(dst
++) = imageData
[index
+ 1];
406 *(dst
++) = imageData
[index
];
415 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
417 *(dst
++) = imageData
[index
+ 2];
418 *(dst
++) = imageData
[index
+ 1];
419 *(dst
++) = imageData
[index
];
420 *(alpha
++) = imageData
[index
+ 3];
426 return wxTGA_INVFORMAT
;
435 // No compression read the data directly to imageData.
437 stream
.Read(imageData
, imageSize
);
439 // If orientation == 0, then the image is stored upside down.
440 // We need to store it right side up.
442 if (orientation
== 0)
444 FlipTGA(imageData
, width
, height
, pixelSize
);
447 // Handle the different pixel depths.
455 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
457 *(dst
++) = imageData
[index
];
458 *(dst
++) = imageData
[index
];
459 *(dst
++) = imageData
[index
];
468 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
470 *(dst
++) = imageData
[index
];
471 *(dst
++) = imageData
[index
];
472 *(dst
++) = imageData
[index
];
473 *(alpha
++) = imageData
[index
+ 1];
479 return wxTGA_INVFORMAT
;
492 // Decode the RLE data.
494 int rc
= DecodeRLE(imageData
, imageSize
, pixelSize
, stream
);
495 if ( rc
!= wxTGA_OK
)
498 // If orientation == 0, then the image is stored upside down.
499 // We need to store it right side up.
501 if (orientation
== 0)
503 FlipTGA(imageData
, width
, height
, pixelSize
);
506 // Handle the different pixel depths.
514 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
516 Palette_GetRGB(palette
, paletteLength
,
517 imageData
[index
], &r
, &g
, &b
);
530 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
532 Palette_GetRGB(palette
, paletteLength
,
533 imageData
[index
], &r
, &g
, &b
);
538 *(alpha
++) = (imageData
[index
+ 1] & 0x80) ? 0 : 255;
544 return wxTGA_INVFORMAT
;
553 // Decode the RLE data.
555 int rc
= DecodeRLE(imageData
, imageSize
, pixelSize
, stream
);
556 if ( rc
!= wxTGA_OK
)
559 // If orientation == 0, then the image is stored upside down.
560 // We need to store it right side up.
562 if (orientation
== 0)
564 FlipTGA(imageData
, width
, height
, pixelSize
);
567 // Handle the different pixel depths.
577 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
579 temp
= (imageData
[index
+ 1] & 0x7c) << 1;
583 temp
= ((imageData
[index
+ 1] & 0x03) << 6) | ((imageData
[index
] & 0xe0) >> 2);
587 temp
= (imageData
[index
] & 0x1f) << 3;
591 *(alpha
++) = (imageData
[index
+ 1] & 0x80) ? 0 : 255;
600 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
602 *(dst
++) = imageData
[index
+ 2];
603 *(dst
++) = imageData
[index
+ 1];
604 *(dst
++) = imageData
[index
];
613 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
615 *(dst
++) = imageData
[index
+ 2];
616 *(dst
++) = imageData
[index
+ 1];
617 *(dst
++) = imageData
[index
];
618 *(alpha
++) = imageData
[index
+ 3];
624 return wxTGA_INVFORMAT
;
633 // Decode the RLE data.
635 int rc
= DecodeRLE(imageData
, imageSize
, pixelSize
, stream
);
636 if ( rc
!= wxTGA_OK
)
639 // If orientation == 0, then the image is stored upside down.
640 // We need to store it right side up.
642 if (orientation
== 0)
644 FlipTGA(imageData
, width
, height
, pixelSize
);
647 // Handle the different pixel depths.
655 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
657 *(dst
++) = imageData
[index
];
658 *(dst
++) = imageData
[index
];
659 *(dst
++) = imageData
[index
];
668 for (unsigned long index
= 0; index
< imageSize
; index
+= pixelSize
)
670 *(dst
++) = imageData
[index
];
671 *(dst
++) = imageData
[index
];
672 *(dst
++) = imageData
[index
];
673 *(alpha
++) = imageData
[index
+ 1];
679 return wxTGA_INVFORMAT
;
685 return wxTGA_INVFORMAT
;
692 int SaveTGA(const wxImage
& image
, wxOutputStream
*stream
)
694 bool hasAlpha
= image
.HasAlpha();
695 unsigned bytesPerPixel
= 3 + (hasAlpha
? 1 : 0);
696 wxSize size
= image
.GetSize();
697 size_t scanlineSize
= size
.x
* bytesPerPixel
;
698 unsigned char *scanlineData
= (unsigned char *) malloc(scanlineSize
);
704 wxON_BLOCK_EXIT1(free
, scanlineData
);
706 // Compose and write the TGA header
707 unsigned char hdr
[HDR_SIZE
];
708 (void) memset(&hdr
, 0, HDR_SIZE
);
710 hdr
[HDR_COLORTYPE
] = wxTGA_UNMAPPED
;
711 hdr
[HDR_IMAGETYPE
] = 2 /* Uncompressed truecolour */;
713 hdr
[HDR_WIDTH
] = size
.x
& 0xFF;
714 hdr
[HDR_WIDTH
+ 1] = (size
.x
>> 8) & 0xFF;
716 hdr
[HDR_HEIGHT
] = size
.y
& 0xFF;
717 hdr
[HDR_HEIGHT
+ 1] = (size
.y
>> 8) & 0xFF;
719 hdr
[HDR_BPP
] = hasAlpha
? 32 : 24;
720 hdr
[HDR_ORIENTATION
] = 1 << 5; // set bit to indicate top-down order
723 hdr
[HDR_ORIENTATION
] |= 8; // number of alpha bits
726 if ( !stream
->Write(hdr
, HDR_SIZE
) )
732 // Write image data, converting RGB to BGR and adding alpha if applicable
734 unsigned char *src
= image
.GetData();
735 unsigned char *alpha
= image
.GetAlpha();
736 for (int y
= 0; y
< size
.y
; ++y
)
738 unsigned char *dst
= scanlineData
;
739 for (int x
= 0; x
< size
.x
; ++x
)
749 dst
+= bytesPerPixel
;
751 if ( !stream
->Write(scanlineData
, scanlineSize
) )
760 // ----------------------------------------------------------------------------
762 // ----------------------------------------------------------------------------
764 bool wxTGAHandler::LoadFile(wxImage
* image
,
765 wxInputStream
& stream
,
769 if ( !CanRead(stream
) )
773 wxLogError(wxT("TGA: this is not a TGA file."));
781 int error
= ReadTGA(image
, stream
);
782 if ( error
!= wxTGA_OK
)
788 case wxTGA_INVFORMAT
:
789 wxLogError(wxT("TGA: image format unsupported."));
793 wxLogError(wxT("TGA: couldn't allocate memory."));
797 wxLogError(wxT("TGA: couldn't read image data."));
801 wxLogError(wxT("TGA: unknown error!"));
813 bool wxTGAHandler::SaveFile(wxImage
* image
, wxOutputStream
& stream
, bool verbose
)
815 int error
= SaveTGA(*image
, &stream
);
817 if ( error
!= wxTGA_OK
)
824 wxLogError(wxT("TGA: couldn't allocate memory."));
828 wxLogError(wxT("TGA: couldn't write image data."));
832 wxLogError(wxT("TGA: unknown error!"));
842 bool wxTGAHandler::DoCanRead(wxInputStream
& stream
)
844 // read the fixed-size TGA headers
845 unsigned char hdr
[HDR_SIZE
];
846 stream
.Read(hdr
, HDR_SIZE
); // it's ok to modify the stream position here
848 // Check whether we can read the file or not.
850 short colorType
= hdr
[HDR_COLORTYPE
];
851 if ( colorType
!= wxTGA_UNMAPPED
&& colorType
!= wxTGA_MAPPED
)
856 short imageType
= hdr
[HDR_IMAGETYPE
];
857 if ( imageType
== 0 || imageType
== 32 || imageType
== 33 )
862 short bpp
= hdr
[HDR_BPP
];
863 if ( bpp
!= 8 && bpp
!= 16 && bpp
!= 24 && bpp
!= 32 )
871 #endif // wxUSE_STREAMS
873 #endif // wxUSE_IMAGE && wxUSE_TGA