1 | // Aseprite TGA Library |
2 | // Copyright (C) 2020-2021 Igara Studio S.A. |
3 | // |
4 | // This file is released under the terms of the MIT license. |
5 | // Read LICENSE.txt for more information. |
6 | |
7 | #ifndef TGA_TGA_H_INCLUDED |
8 | #define TGA_TGA_H_INCLUDED |
9 | #pragma once |
10 | |
11 | #include <stdint.h> |
12 | |
13 | #include <cstdio> |
14 | #include <string> |
15 | #include <vector> |
16 | |
17 | namespace tga { |
18 | |
19 | enum ImageType { |
20 | NoImage = 0, |
21 | UncompressedIndexed = 1, |
22 | UncompressedRgb = 2, |
23 | UncompressedGray = 3, |
24 | RleIndexed = 9, |
25 | RleRgb = 10, |
26 | RleGray = 11, |
27 | }; |
28 | |
29 | typedef uint32_t color_t; |
30 | |
31 | const color_t color_r_shift = 0; |
32 | const color_t color_g_shift = 8; |
33 | const color_t color_b_shift = 16; |
34 | const color_t color_a_shift = 24; |
35 | const color_t color_r_mask = 0x000000ff; |
36 | const color_t color_g_mask = 0x0000ff00; |
37 | const color_t color_b_mask = 0x00ff0000; |
38 | const color_t color_rgb_mask = 0x00ffffff; |
39 | const color_t color_a_mask = 0xff000000; |
40 | |
41 | inline uint8_t getr(color_t c) { return (c >> color_r_shift) & 0xff; } |
42 | inline uint8_t getg(color_t c) { return (c >> color_g_shift) & 0xff; } |
43 | inline uint8_t getb(color_t c) { return (c >> color_b_shift) & 0xff; } |
44 | inline uint8_t geta(color_t c) { return (c >> color_a_shift) & 0xff; } |
45 | inline color_t rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) { |
46 | return ((r << color_r_shift) | |
47 | (g << color_g_shift) | |
48 | (b << color_b_shift) | |
49 | (a << color_a_shift)); |
50 | } |
51 | |
52 | class Colormap { |
53 | public: |
54 | Colormap() { } |
55 | Colormap(const int n) : m_color(n) { } |
56 | |
57 | int size() const { return int(m_color.size()); } |
58 | |
59 | const color_t& operator[](int i) const { |
60 | return m_color[i]; |
61 | } |
62 | |
63 | color_t& operator[](int i) { |
64 | return m_color[i]; |
65 | } |
66 | |
67 | bool operator==(const Colormap& o) const { |
68 | for (int i=0; i<int(m_color.size()); ++i) { |
69 | if (m_color[i] != o[i]) |
70 | return false; |
71 | } |
72 | return true; |
73 | } |
74 | |
75 | bool operator!=(const Colormap& o) const { |
76 | return !operator==(o); |
77 | } |
78 | |
79 | private: |
80 | std::vector<color_t> m_color; |
81 | }; |
82 | |
83 | struct Image { |
84 | uint8_t* pixels; |
85 | uint32_t bytesPerPixel; |
86 | uint32_t rowstride; |
87 | }; |
88 | |
89 | struct { |
90 | uint8_t ; |
91 | uint8_t ; |
92 | uint8_t ; |
93 | uint16_t ; |
94 | uint16_t ; |
95 | uint8_t ; |
96 | uint16_t ; |
97 | uint16_t ; |
98 | uint16_t ; |
99 | uint16_t ; |
100 | uint8_t ; |
101 | uint8_t ; |
102 | std::string ; |
103 | Colormap ; |
104 | |
105 | bool () const { return !(imageDescriptor & 0x10); } |
106 | bool () const { return (imageDescriptor & 0x20); } |
107 | |
108 | bool () const { |
109 | return (colormapLength > 0); |
110 | } |
111 | |
112 | bool () const { |
113 | return (imageType == UncompressedRgb || |
114 | imageType == RleRgb); |
115 | } |
116 | |
117 | bool () const { |
118 | return (imageType == UncompressedIndexed || |
119 | imageType == RleIndexed); |
120 | } |
121 | |
122 | bool () const { |
123 | return (imageType == UncompressedGray || |
124 | imageType == RleGray); |
125 | } |
126 | |
127 | bool () const { |
128 | return (imageType == UncompressedIndexed || |
129 | imageType == UncompressedRgb || |
130 | imageType == UncompressedGray); |
131 | } |
132 | |
133 | bool () const { |
134 | return (imageType == RleIndexed || |
135 | imageType == RleRgb || |
136 | imageType == RleGray); |
137 | } |
138 | |
139 | bool () const { |
140 | return |
141 | // Indexed with palette |
142 | (isIndexed() && bitsPerPixel == 8 && colormapType == 1) || |
143 | // Grayscale without palette |
144 | (isGray() && bitsPerPixel == 8 && colormapType == 0) || |
145 | // Non-indexed without palette |
146 | (bitsPerPixel > 8 && colormapType == 0); |
147 | } |
148 | |
149 | bool () const { |
150 | switch (imageType) { |
151 | case UncompressedIndexed: |
152 | case RleIndexed: |
153 | return (bitsPerPixel == 8); |
154 | case UncompressedRgb: |
155 | case RleRgb: |
156 | return (bitsPerPixel == 15 || |
157 | bitsPerPixel == 16 || |
158 | bitsPerPixel == 24 || |
159 | bitsPerPixel == 32); |
160 | case UncompressedGray: |
161 | case RleGray: |
162 | return (bitsPerPixel == 8); |
163 | } |
164 | return false; |
165 | } |
166 | |
167 | // Returns the number of bytes per pixel needed in an image |
168 | // created with this Header information. |
169 | int () const { |
170 | if (isRgb()) |
171 | return 4; |
172 | else |
173 | return 1; |
174 | } |
175 | |
176 | }; |
177 | |
178 | namespace details { |
179 | |
180 | class ImageIterator { |
181 | public: |
182 | ImageIterator(); |
183 | (const Header& , Image& image); |
184 | |
185 | // Put a pixel value into the image and advance the iterator. |
186 | template<typename T> |
187 | bool putPixel(const T value) { |
188 | *((T*)m_ptr) = value; |
189 | return advance(); |
190 | } |
191 | |
192 | // Get one pixel from the image and advance the iterator. |
193 | template<typename T> |
194 | T getPixel() { |
195 | T value = *((T*)m_ptr); |
196 | advance(); |
197 | return value; |
198 | } |
199 | |
200 | public: |
201 | bool advance(); |
202 | void calcPtr(); |
203 | |
204 | Image* m_image; |
205 | int m_x, m_y; |
206 | int m_w, m_h; |
207 | int m_dx, m_dy; |
208 | uint8_t* m_ptr; |
209 | }; |
210 | |
211 | } // namespace details |
212 | |
213 | class FileInterface { |
214 | public: |
215 | virtual ~FileInterface() { } |
216 | |
217 | // Returns true if we can read/write bytes from/into the file |
218 | virtual bool ok() const = 0; |
219 | |
220 | // Current position in the file |
221 | virtual size_t tell() = 0; |
222 | |
223 | // Jump to the given position in the file |
224 | virtual void seek(size_t absPos) = 0; |
225 | |
226 | // Returns the next byte in the file or 0 if ok() = false |
227 | virtual uint8_t read8() = 0; |
228 | |
229 | // Writes one byte in the file (or do nothing if ok() = false) |
230 | virtual void write8(uint8_t value) = 0; |
231 | }; |
232 | |
233 | class StdioFileInterface : public tga::FileInterface { |
234 | public: |
235 | StdioFileInterface(FILE* file); |
236 | bool ok() const override; |
237 | size_t tell() override; |
238 | void seek(size_t absPos) override; |
239 | uint8_t read8() override; |
240 | void write8(uint8_t value) override; |
241 | |
242 | private: |
243 | FILE* m_file; |
244 | bool m_ok; |
245 | }; |
246 | |
247 | class Delegate { |
248 | public: |
249 | virtual ~Delegate() {} |
250 | // Must return true if we should continue the decoding process. |
251 | virtual bool notifyProgress(double progress) = 0; |
252 | }; |
253 | |
254 | class Decoder { |
255 | public: |
256 | Decoder(FileInterface* file); |
257 | |
258 | bool hasAlpha() const { return m_hasAlpha; } |
259 | |
260 | // Reads the header + colormap (if the file has a |
261 | // colormap). Returns true if the header is valid. |
262 | bool (Header& ); |
263 | |
264 | // Reads the image. |
265 | bool (const Header& , |
266 | Image& image, |
267 | Delegate* delegate = nullptr); |
268 | |
269 | // Fixes alpha channel for images with invalid alpha values (this |
270 | // is optional, in case you want to preserve the original data |
271 | // from the file, don't use it). |
272 | void (const Header& , |
273 | Image& image); |
274 | |
275 | private: |
276 | void (Header& ); |
277 | |
278 | template<typename T> |
279 | bool readUncompressedData(const int w, uint32_t (Decoder::*readPixel)()); |
280 | |
281 | template<typename T> |
282 | bool readRleData(const int w, uint32_t (Decoder::*readPixel)()); |
283 | |
284 | uint8_t read8(); |
285 | uint16_t read16(); |
286 | uint32_t read32(); |
287 | |
288 | color_t read32AsRgb(); |
289 | color_t read24AsRgb(); |
290 | color_t read16AsRgb(); |
291 | color_t read8Color() { return (color_t)read8(); } |
292 | |
293 | FileInterface* m_file; |
294 | bool m_hasAlpha = false; |
295 | details::ImageIterator m_iterator; |
296 | }; |
297 | |
298 | class Encoder { |
299 | public: |
300 | Encoder(FileInterface* file); |
301 | |
302 | // Writes the header + colormap |
303 | void (const Header& ); |
304 | void (const Header& , |
305 | const Image& image, |
306 | Delegate* delegate = nullptr); |
307 | void (); |
308 | |
309 | private: |
310 | template<typename T> |
311 | void writeRleScanline(const int w, const Image& image, |
312 | void (Encoder::*writePixel)(color_t)); |
313 | |
314 | template<typename T> |
315 | void countRepeatedPixels(const int w, const Image& image, |
316 | int x0, int& offset, int& count); |
317 | |
318 | void write8(uint8_t value); |
319 | void write16(uint16_t value); |
320 | void write32(uint32_t value); |
321 | |
322 | void write8Color(color_t c) { write8(uint8_t(c)); } |
323 | void write16Rgb(color_t c); |
324 | void write24Rgb(color_t c); |
325 | void write32Rgb(color_t c); |
326 | |
327 | FileInterface* m_file; |
328 | bool m_hasAlpha = false; |
329 | details::ImageIterator m_iterator; |
330 | }; |
331 | |
332 | } // namespace tga |
333 | |
334 | #endif |
335 | |