1// Aseprite TGA Library
2// Copyright (C) 2020 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#include "tga.h"
8
9#include <algorithm>
10#include <cassert>
11
12namespace tga {
13
14template<typename T>
15inline color_t get_pixel(Image& image, int x, int y) {
16 return *(T*)(image.pixels + y*image.rowstride + x*image.bytesPerPixel);
17}
18
19Encoder::Encoder(FileInterface* file)
20 : m_file(file)
21{
22}
23
24void Encoder::writeHeader(const Header& header)
25{
26 write8(header.idLength);
27 write8(header.colormapType);
28 write8(header.imageType);
29 write16(header.colormapOrigin);
30 write16(header.colormapLength);
31 write8(header.colormapDepth);
32 write16(header.xOrigin);
33 write16(header.yOrigin);
34 write16(header.width);
35 write16(header.height);
36 write8(header.bitsPerPixel);
37 write8(header.imageDescriptor);
38
39 m_hasAlpha = (header.bitsPerPixel == 16 ||
40 header.bitsPerPixel == 32);
41
42 assert(header.colormapLength == header.colormap.size());
43
44 // Write colormap
45 if (header.colormapType == 1) {
46 for (int i=0; i<header.colormap.size(); ++i) {
47 color_t c = header.colormap[i];
48 switch (header.colormapDepth) {
49 case 15:
50 case 16: write16Rgb(c); break;
51 case 24: write24Rgb(c); break;
52 case 32: write32Rgb(c); break;
53 }
54 }
55 }
56 else {
57 assert(header.colormapLength == 0);
58 }
59}
60
61void Encoder::writeFooter()
62{
63 const char* tga2_footer = "\0\0\0\0\0\0\0\0TRUEVISION-XFILE.\0";
64 for (int i=0; i<26; ++i)
65 write8(tga2_footer[i]);
66}
67
68void Encoder::writeImage(const Header& header,
69 const Image& image,
70 Delegate* delegate)
71{
72 const int w = header.width;
73 const int h = header.height;
74
75 m_iterator = details::ImageIterator(header, const_cast<Image&>(image));
76
77 switch (header.imageType) {
78
79 case tga::UncompressedIndexed:
80 case tga::UncompressedGray:
81 for (int y=0; y<h; ++y) {
82 for (int x=0; x<w; ++x)
83 write8(m_iterator.getPixel<uint8_t>());
84
85 if (delegate && !delegate->notifyProgress(float(y) / float(h)))
86 return;
87 }
88 break;
89
90 case tga::RleIndexed:
91 case tga::RleGray:
92 for (int y=0; y<h; ++y) {
93 writeRleScanline<uint8_t>(w, image, &Encoder::write8Color);
94
95 if (delegate && !delegate->notifyProgress(float(y) / float(h)))
96 return;
97 }
98 break;
99
100 case tga::UncompressedRgb: {
101 switch (header.bitsPerPixel) {
102 case 15:
103 case 16:
104 for (int y=0; y<h; ++y) {
105 for (int x=0; x<w; ++x)
106 write16Rgb(m_iterator.getPixel<uint32_t>());
107
108 if (delegate && !delegate->notifyProgress(float(y) / float(h)))
109 return;
110 }
111 break;
112 case 24:
113 for (int y=0; y<h; ++y) {
114 for (int x=0; x<w; ++x)
115 write24Rgb(m_iterator.getPixel<uint32_t>());
116
117 if (delegate && !delegate->notifyProgress(float(y) / float(h)))
118 return;
119 }
120 break;
121 case 32:
122 for (int y=0; y<h; ++y) {
123 for (int x=0; x<w; ++x)
124 write32Rgb(m_iterator.getPixel<uint32_t>());
125
126 if (delegate && !delegate->notifyProgress(float(y) / float(h)))
127 return;
128 }
129 break;
130 }
131 }
132 break;
133
134 case tga::RleRgb:
135 for (int y=0; y<h; ++y) {
136 switch (header.bitsPerPixel) {
137 case 15:
138 case 16: writeRleScanline<uint32_t>(w, image, &Encoder::write16Rgb); break;
139 case 24: writeRleScanline<uint32_t>(w, image, &Encoder::write24Rgb); break;
140 case 32: writeRleScanline<uint32_t>(w, image, &Encoder::write32Rgb); break;
141 default:
142 assert(false);
143 return;
144 }
145 if (delegate && !delegate->notifyProgress(float(y) / float(h)))
146 return;
147 }
148 break;
149
150 }
151}
152
153template<typename T>
154void Encoder::writeRleScanline(const int w,
155 const Image& image,
156 void (Encoder::*writePixel)(color_t))
157{
158 int x = 0;
159 while (x < w) {
160 int count, offset;
161 countRepeatedPixels<T>(w, image, x, offset, count);
162
163 // Save a sequence of pixels with different colors
164 while (offset > 0) {
165 const int n = std::min(offset, 128);
166
167 assert(n >= 1 && n <= 128);
168 write8(static_cast<uint8_t>(n - 1));
169 for (int i=0; i<n; ++i) {
170 const color_t c = m_iterator.getPixel<T>();
171 (this->*writePixel)(c);
172 }
173 offset -= n;
174 x += n;
175 }
176
177 // Save a sequence of pixels with the same color
178 while (count*image.bytesPerPixel > 1+image.bytesPerPixel) {
179 const int n = std::min(count, 128);
180 const color_t c = m_iterator.getPixel<T>();
181
182 for (int i=1; i<n; ++i) {
183 const color_t c2 = m_iterator.getPixel<T>();
184 assert(c == c2);
185 }
186
187 assert(n >= 1 && n <= 128);
188 write8(0x80 | static_cast<uint8_t>(n - 1));
189 (this->*writePixel)(c);
190 count -= n;
191 x += n;
192 }
193 }
194 assert(x == w);
195}
196
197template<typename T>
198void Encoder::countRepeatedPixels(const int w,
199 const Image& image, int x0,
200 int& offset, int& count)
201{
202 auto it = m_iterator;
203
204 for (int x=x0; x<w; ) {
205 const color_t p = it.getPixel<T>();
206
207 int u = x+1;
208 auto next_it = it;
209
210 for (; u<w; ++u) {
211 const color_t q = it.getPixel<T>();
212 if (p != q)
213 break;
214 }
215
216 if ((u - x)*image.bytesPerPixel > 1+image.bytesPerPixel) {
217 offset = x - x0;
218 count = u - x;
219 return;
220 }
221
222 ++x;
223 it = next_it;
224 }
225
226 offset = w - x0;
227 count = 0;
228}
229
230void Encoder::write8(uint8_t value)
231{
232 m_file->write8(value);
233}
234
235void Encoder::write16(uint16_t value)
236{
237 // Little endian
238 m_file->write8(value & 0x00FF);
239 m_file->write8((value & 0xFF00) >> 8);
240}
241
242void Encoder::write32(uint32_t value)
243{
244 // Little endian
245 m_file->write8(value & 0xFF);
246 m_file->write8((value >> 8) & 0xFF);
247 m_file->write8((value >> 16) & 0xFF);
248 m_file->write8((value >> 24) & 0xFF);
249}
250
251void Encoder::write16Rgb(color_t c)
252{
253 const uint8_t r = getr(c);
254 const uint8_t g = getg(c);
255 const uint8_t b = getb(c);
256 const uint8_t a = geta(c);
257 const uint16_t v =
258 ((r>>3) << 10) |
259 ((g>>3) << 5) |
260 ((b>>3)) |
261 (m_hasAlpha && a >= 128 ? 0x8000: 0); // TODO configurable threshold
262 write16(v);
263}
264
265void Encoder::write24Rgb(color_t c)
266{
267 write8(getb(c));
268 write8(getg(c));
269 write8(getr(c));
270}
271
272void Encoder::write32Rgb(color_t c)
273{
274 write8(getb(c));
275 write8(getg(c));
276 write8(getr(c));
277 write8(geta(c));
278}
279
280} // namespace tga
281