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
17namespace 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 Header {
90 uint8_t idLength;
91 uint8_t colormapType;
92 uint8_t imageType;
93 uint16_t colormapOrigin;
94 uint16_t colormapLength;
95 uint8_t colormapDepth;
96 uint16_t xOrigin;
97 uint16_t yOrigin;
98 uint16_t width;
99 uint16_t height;
100 uint8_t bitsPerPixel;
101 uint8_t imageDescriptor;
102 std::string imageId;
103 Colormap colormap;
104
105 bool leftToRight() const { return !(imageDescriptor & 0x10); }
106 bool topToBottom() const { return (imageDescriptor & 0x20); }
107
108 bool hasColormap() const {
109 return (colormapLength > 0);
110 }
111
112 bool isRgb() const {
113 return (imageType == UncompressedRgb ||
114 imageType == RleRgb);
115 }
116
117 bool isIndexed() const {
118 return (imageType == UncompressedIndexed ||
119 imageType == RleIndexed);
120 }
121
122 bool isGray() const {
123 return (imageType == UncompressedGray ||
124 imageType == RleGray);
125 }
126
127 bool isUncompressed() const {
128 return (imageType == UncompressedIndexed ||
129 imageType == UncompressedRgb ||
130 imageType == UncompressedGray);
131 }
132
133 bool isRle() const {
134 return (imageType == RleIndexed ||
135 imageType == RleRgb ||
136 imageType == RleGray);
137 }
138
139 bool validColormapType() 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 valid() 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 bytesPerPixel() 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 ImageIterator(const Header& 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 readHeader(Header& header);
263
264 // Reads the image.
265 bool readImage(const Header& 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 postProcessImage(const Header& header,
273 Image& image);
274
275 private:
276 void readColormap(Header& 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 writeHeader(const Header& header);
304 void writeImage(const Header& header,
305 const Image& image,
306 Delegate* delegate = nullptr);
307 void writeFooter();
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