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 
10 namespace flic {
11 
12 template<typename Iterator>
count_consecutive_values(Iterator begin,Iterator end)13 static 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 
26 template<typename Iterator>
count_max_consecutive_values(Iterator begin,Iterator end,Iterator * maxStart)27 static 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 
53 template<typename Iterator1, typename Iterator2>
count_max_consecutive_equal_values(Iterator1 begin1,Iterator1 end1,Iterator2 begin2,Iterator2 end2,Iterator2 * maxStart)54 static 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 
Encoder(FileInterface * file)83 Encoder::Encoder(FileInterface* file)
84   : m_file(file)
85   , m_frameCount(0)
86   , m_offsetFrame1(0)
87   , m_offsetFrame2(0)
88 {
89 }
90 
~Encoder()91 Encoder::~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 
writeHeader(const Header & header)108 void 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 
writeFrame(const Frame & frame)121 void 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 
writeRingFrame(const Frame & frame)167 void Encoder::writeRingFrame(const Frame& frame)
168 {
169   writeFrame(frame);
170   --m_frameCount;
171 }
172 
writeColorChunk(const Frame & frame)173 void 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 
writeBrunChunk(const Frame & frame)239 void 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 
writeBrunLineChunk(const Frame & frame,int y)260 void 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 
writeLcChunk(const Frame & frame)315 void 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 
331 firstScanDone:;
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 
347 secondScanDone:;
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 
writeLcLineChunk(const Frame & frame,int y)378 void 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 
write16(uint16_t value)480 void Encoder::write16(uint16_t value)
481 {
482   // Little endian
483   m_file->write8(value & 0x00FF);
484   m_file->write8((value & 0xFF00) >> 8);
485 }
486 
write32(uint32_t value)487 void 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