1// Aseprite FLIC Library
2// Copyright (c) 2015 David Capello
3//
4// This file is released under the terms of the MIT license.
5// Read LICENSE.txt for more information.
6
7#include "flic.h"
8#include "flic_details.h"
9
10namespace flic {
11
12template<typename Iterator>
13static int count_consecutive_values(Iterator begin, Iterator end)
14{
15 Iterator prev = nullptr;
16 int same = 0;
17 for (Iterator it=begin; it!=end; prev=it, ++it) {
18 if (!prev || *prev == *it)
19 ++same;
20 else
21 break;
22 }
23 return same;
24}
25
26template<typename Iterator>
27static int count_max_consecutive_values(Iterator begin, Iterator end, Iterator* maxStart)
28{
29 Iterator prev = nullptr;
30 Iterator curStart = nullptr;
31 *maxStart = nullptr;
32 int max = 0;
33 int same = 0;
34 for (Iterator it=begin; it!=end; prev=it, ++it) {
35 if (!prev || *prev == *it) {
36 if (!curStart)
37 curStart = it;
38
39 ++same;
40 if (max < same) {
41 max = same;
42 *maxStart = curStart;
43 }
44 }
45 else {
46 same = 0;
47 curStart = nullptr;
48 }
49 }
50 return max;
51}
52
53template<typename Iterator1, typename Iterator2>
54static int count_max_consecutive_equal_values(Iterator1 begin1, Iterator1 end1,
55 Iterator2 begin2, Iterator2 end2,
56 Iterator2* maxStart)
57{
58 Iterator1 it1 = begin1;
59 Iterator2 it2 = begin2;
60 Iterator2 curStart = nullptr;
61 *maxStart = nullptr;
62 int max = 0;
63 int same = 0;
64 for (; it1!=end1 && it2!=end2; ++it1, ++it2) {
65 if (*it1 == *it2) {
66 if (!curStart)
67 curStart = it2;
68
69 ++same;
70 if (max < same) {
71 max = same;
72 *maxStart = curStart;
73 }
74 }
75 else {
76 same = 0;
77 curStart = nullptr;
78 }
79 }
80 return max;
81}
82
83Encoder::Encoder(FileInterface* file)
84 : m_file(file)
85 , m_frameCount(0)
86 , m_offsetFrame1(0)
87 , m_offsetFrame2(0)
88{
89}
90
91Encoder::~Encoder()
92{
93 // Fill header information
94 if (m_file->ok()) {
95 uint32_t size = m_file->tell();
96 m_file->seek(0);
97
98 write32(size); // Write file size
99 write16(FLC_MAGIC_NUMBER); // Always as FLC file
100 write16(m_frameCount); // Number of frames
101
102 m_file->seek(80);
103 write32(m_offsetFrame1);
104 write32(m_offsetFrame2);
105 }
106}
107
108void Encoder::writeHeader(const Header& header)
109{
110 write32(0); // File size, to be completed in ~Encoder()
111 write16(0); // File type
112 write16(0); // Number of frames
113 write16(m_width = header.width);
114 write16(m_height = header.height);
115 write16(8);
116 write16(0); // Flags
117 write32(header.speed);
118 m_file->seek(128);
119}
120
121void Encoder::writeFrame(const Frame& frame)
122{
123 uint32_t frameStartPos = m_file->tell();
124 int nchunks = 0;
125
126 switch (m_frameCount) {
127 case 0: m_offsetFrame1 = frameStartPos; break;
128 case 1: m_offsetFrame2 = frameStartPos; break;
129 }
130
131 write32(0); // Frame size will be written at the end of this function
132 write16(0); // Magic number
133 write16(0); // Number of chunks
134 write32(0); // Padding
135 write32(0);
136
137 if (m_frameCount == 0 || m_prevColormap != frame.colormap) {
138 writeColorChunk(frame);
139 ++nchunks;
140 }
141
142 if (m_frameCount == 0) {
143 writeBrunChunk(frame);
144 ++nchunks;
145
146 // Create the buffer to store previous frame pixels
147 m_prevFrameData.resize(m_height*frame.rowstride);
148 std::copy(frame.pixels,
149 frame.pixels+m_height*frame.rowstride,
150 m_prevFrameData.begin());
151 }
152 else {
153 writeLcChunk(frame);
154 ++nchunks;
155 }
156
157 size_t frameEndPos = m_file->tell();
158 m_file->seek(frameStartPos);
159 write32(frameEndPos - frameStartPos); // Frame size
160 write16(FLI_FRAME_MAGIC_NUMBER); // Chunk type
161 write16(nchunks); // Number of chunks
162
163 m_file->seek(frameEndPos);
164 ++m_frameCount;
165}
166
167void Encoder::writeRingFrame(const Frame& frame)
168{
169 writeFrame(frame);
170 --m_frameCount;
171}
172
173void Encoder::writeColorChunk(const Frame& frame)
174{
175 // Chunk header
176 size_t chunkBeginPos = m_file->tell();
177 write32(0); // Chunk size (this will be re-written below)
178 write16(0); // Chunk type
179 write16(0); // Write number of packets in this chunk
180
181 // Write packets
182 int npackets = 0;
183 int skip = 0;
184 for (int i=0; i<256; ) {
185 if (m_frameCount == 0 ||
186 m_prevColormap[i] != frame.colormap[i]) {
187 int ncolors;
188 if (m_frameCount == 0) {
189 ncolors = 256;
190 }
191 else {
192 ncolors = 1;
193 for (int j=i+1; j<256; ++j) {
194 if (m_prevColormap[j] != frame.colormap[j])
195 ++ncolors;
196 }
197 }
198
199 assert(ncolors > 0);
200
201 ++npackets;
202 m_file->write8(skip); // How many colors to skip from previous packet
203 m_file->write8(ncolors == 256 ? 0: ncolors); // 0 means 256 colors
204
205 // Write colors
206 for (int j=i; j<ncolors; ++j) {
207 const Color a = frame.colormap[j];
208 m_file->write8(a.r);
209 m_file->write8(a.g);
210 m_file->write8(a.b);
211 }
212
213 i += ncolors;
214 skip = 0;
215 }
216 else {
217 ++skip;
218 ++i;
219 }
220 }
221
222 assert(npackets > 0);
223
224 // Update chunk size
225 size_t chunkEndPos = m_file->tell();
226 m_file->seek(chunkBeginPos);
227
228 if ((chunkEndPos - chunkBeginPos) & 1) // Avoid odd chunk size
229 ++chunkEndPos;
230
231 write32(chunkEndPos - chunkBeginPos); // Chunk size
232 write16(FLI_COLOR_256_CHUNK); // Chunk type
233 write16(npackets); // Number of packets
234 m_file->seek(chunkEndPos);
235
236 m_prevColormap = frame.colormap;
237}
238
239void Encoder::writeBrunChunk(const Frame& frame)
240{
241 // Chunk header
242 size_t chunkBeginPos = m_file->tell();
243 write32(0); // Chunk size (this will be re-written below)
244 write16(FLI_BRUN_CHUNK);
245
246 for (int y=0; y<m_height; ++y)
247 writeBrunLineChunk(frame, y);
248
249 // Update chunk size
250 size_t chunkEndPos = m_file->tell();
251 m_file->seek(chunkBeginPos);
252
253 if ((chunkEndPos - chunkBeginPos) & 1) // Avoid odd chunk size
254 ++chunkEndPos;
255
256 write32(chunkEndPos - chunkBeginPos);
257 m_file->seek(chunkEndPos);
258}
259
260void Encoder::writeBrunLineChunk(const Frame& frame, int y)
261{
262 size_t npacketsPos = m_file->tell();
263 m_file->write8(0); // Number of packets, it will be re-written later
264
265 // Number of packets
266 int npackets = 0;
267
268 uint8_t* it = frame.pixels + y*frame.rowstride;
269 for (int x=0; x<m_width; ) {
270 int remain = (m_width-x);
271 uint8_t* maxSameStart = nullptr;
272
273 int samePixels = count_consecutive_values(it, it+remain);
274 int maxSamePixels = count_max_consecutive_values(it, it+remain, &maxSameStart);
275
276 // We can compress 127 equal pixels in one packet
277 if (samePixels > 127)
278 samePixels = 127;
279
280 if (samePixels >= 4) {
281 // One packet to compress "samePixels"
282 ++npackets;
283 m_file->write8(samePixels);
284 m_file->write8(*it);
285
286 it += samePixels;
287 x += samePixels;
288 }
289 else {
290 // We can include 128 pixels in one packet
291 if (remain > 128)
292 remain = 128;
293
294 // Is it better to reduce this packet just to compress future same pixels?
295 if (maxSamePixels >= 4 && remain > (maxSameStart-it))
296 remain = (maxSameStart-it);
297
298 assert(remain > 0);
299
300 ++npackets;
301 m_file->write8(-remain);
302 for (int i=0; i<remain; ++i, ++it)
303 m_file->write8(*it);
304
305 x += remain;
306 }
307 }
308
309 size_t restorePos = m_file->tell();
310 m_file->seek(npacketsPos);
311 m_file->write8(npackets < 255 ? npackets: 255);
312 m_file->seek(restorePos);
313}
314
315void Encoder::writeLcChunk(const Frame& frame)
316{
317 int skipLines = 0;
318 for (int y=0; y<m_height; ++y) {
319 std::vector<uint8_t>::iterator prevIt =
320 m_prevFrameData.begin() + y*frame.rowstride;
321 uint8_t* it = frame.pixels + y*frame.rowstride;
322
323 for (int x=0; x<m_width; ++x, ++it, ++prevIt) {
324 if (*prevIt != *it)
325 goto firstScanDone;
326 }
327
328 ++skipLines;
329 }
330
331firstScanDone:;
332
333 int skipEndLines = 0;
334 for (int y=m_height-1; y > skipLines; --y) {
335 std::vector<uint8_t>::iterator prevIt =
336 m_prevFrameData.begin() + y*frame.rowstride;
337 uint8_t* it = frame.pixels + y*frame.rowstride;
338
339 for (int x=0; x<m_width; ++x, ++it, ++prevIt) {
340 if (*prevIt != *it)
341 goto secondScanDone;
342 }
343
344 ++skipEndLines;
345 }
346
347secondScanDone:;
348
349 int nlines = (m_height - skipEndLines - skipLines);
350
351 // Chunk header
352 size_t chunkBeginPos = m_file->tell();
353 write32(0); // Chunk size (this will be re-written below)
354 write16(FLI_LC_CHUNK);
355 write16(skipLines); // How many lines to skip
356 write16(nlines);
357
358 for (int y=skipLines; y<skipLines+nlines; ++y)
359 writeLcLineChunk(frame, y);
360
361 // Update the previous frame data
362 if (nlines > 0)
363 std::copy(frame.pixels+(skipLines*frame.rowstride),
364 frame.pixels+((skipLines+nlines)*frame.rowstride),
365 m_prevFrameData.begin()+(skipLines*frame.rowstride));
366
367 // Update chunk size
368 size_t chunkEndPos = m_file->tell();
369 m_file->seek(chunkBeginPos);
370
371 if ((chunkEndPos - chunkBeginPos) & 1) // Avoid odd chunk size
372 ++chunkEndPos;
373
374 write32(chunkEndPos - chunkBeginPos);
375 m_file->seek(chunkEndPos);
376}
377
378void Encoder::writeLcLineChunk(const Frame& frame, int y)
379{
380 size_t npacketsPos = m_file->tell();
381 m_file->write8(0); // Number of packets, it will be re-written later
382
383 // Number of packets
384 int npackets = 0;
385 int skipPixels = 0;
386
387 std::vector<uint8_t>::iterator prevIt =
388 m_prevFrameData.begin() + y*frame.rowstride;
389 uint8_t* it = frame.pixels + y*frame.rowstride;
390
391 for (int x=0; x<m_width; ) {
392 if (*prevIt != *it) {
393 while (skipPixels > 255) {
394 // One empty packet to skip 255 pixels that are equal to the previous frame
395 ++npackets;
396 m_file->write8(255);
397 m_file->write8(0);
398
399 skipPixels -= 255;
400 }
401
402 // New packet
403 ++npackets;
404 m_file->write8(skipPixels);
405
406 int remain = (m_width-x);
407 if (remain > 128)
408 remain = 128;
409
410 // Calculate if there is a strip of equal pixels with the
411 // previous frame in the following pixels
412 uint8_t* maxUnchangedStart = nullptr;
413 int maxUnchangedPixels =
414 count_max_consecutive_equal_values(prevIt, prevIt+remain,
415 it, it+remain,
416 &maxUnchangedStart);
417 if (maxUnchangedPixels > 4 && remain > (maxUnchangedStart-it))
418 remain = (maxUnchangedStart-it);
419
420 // Check if we can create a compressed packet
421 uint8_t* maxSameStart = nullptr;
422 int samePixels = count_consecutive_values(it, it+remain);
423 int maxSamePixels = count_max_consecutive_values(it, it+remain, &maxSameStart);
424
425 // We can compress 128 equal pixels in one packet
426 if (samePixels > 128)
427 samePixels = 128;
428
429 if (samePixels >= 4) {
430 // One packet to compress "samePixels"
431 m_file->write8(-samePixels);
432 m_file->write8(*it);
433
434 prevIt += samePixels;
435 it += samePixels;
436 x += samePixels;
437 }
438 else {
439 // We can include 127 pixels in one packet
440 if (remain > 127)
441 remain = 127;
442
443 // Is it better to reduce this packet just to compress future same pixels?
444 if (maxSamePixels >= 4 && remain > (maxSameStart-it))
445 remain = (maxSameStart-it);
446
447 assert(remain > 0);
448
449 m_file->write8(remain);
450 for (int i=0; i<remain; ++i, ++it)
451 m_file->write8(*it);
452
453 prevIt += remain;
454 x += remain;
455 }
456
457 skipPixels = 0;
458 }
459 else {
460 ++skipPixels;
461 ++prevIt;
462 ++it;
463 ++x;
464 }
465 }
466
467 if (skipPixels != m_width) {
468 assert(npackets != 0);
469
470 size_t restorePos = m_file->tell();
471 m_file->seek(npacketsPos);
472 m_file->write8(npackets < 255 ? npackets: 255);
473 m_file->seek(restorePos);
474 }
475 else {
476 assert(npackets == 0);
477 }
478}
479
480void Encoder::write16(uint16_t value)
481{
482 // Little endian
483 m_file->write8(value & 0x00FF);
484 m_file->write8((value & 0xFF00) >> 8);
485}
486
487void Encoder::write32(uint32_t value)
488{
489 // Little endian
490 m_file->write8((int)value & 0x00FF);
491 m_file->write8((int)((value & 0x0000FF00L) >> 8));
492 m_file->write8((int)((value & 0x00FF0000L) >> 16));
493 m_file->write8((int)((value & 0xFF000000L) >> 24));
494}
495
496} // namespace flic
497