85 int depth, count, rbits, gbits,
bbits;
189#ifndef MSF_GIF_ALREADY_IMPLEMENTED_IN_THIS_TRANSLATION_UNIT
190#define MSF_GIF_ALREADY_IMPLEMENTED_IN_THIS_TRANSLATION_UNIT
193#if defined(MSF_GIF_MALLOC) && defined(MSF_GIF_REALLOC) && defined(MSF_GIF_FREE)
194#elif !defined(MSF_GIF_MALLOC) && !defined(MSF_GIF_REALLOC) && !defined(MSF_GIF_FREE)
196#error "You must either define all of MSF_GIF_MALLOC, MSF_GIF_REALLOC, and MSF_GIF_FREE, or define none of them"
200#if !defined(MSF_GIF_MALLOC)
202#define MSF_GIF_MALLOC(contextPointer, newSize) malloc(newSize)
203#define MSF_GIF_REALLOC(contextPointer, oldMemory, oldSize, newSize) realloc(oldMemory, newSize)
204#define MSF_GIF_FREE(contextPointer, oldMemory, oldSize) free(oldMemory)
208#ifdef MSF_GIF_ENABLE_TRACING
209#define MsfTimeFunc TimeFunc
210#define MsfTimeLoop TimeLoop
211#define msf_init_profiling_thread init_profiling_thread
214#define MsfTimeLoop(name)
215#define msf_init_profiling_thread()
222static inline int msf_bit_log(
int i) {
return 32 - __builtin_clz(i); }
223#elif defined(_MSC_VER)
225static inline int msf_bit_log(
int i) {
unsigned long idx; _BitScanReverse(&idx, i);
return idx + 1; }
228static inline int msf_bit_log(
int i) {
229 static const int MultiplyDeBruijnBitPosition[32] = {
230 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
231 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31,
238 return MultiplyDeBruijnBitPosition[(
uint32_t)(i * 0x07C4ACDDU) >> 27] + 1;
241static inline int msf_imin(
int a,
int b) {
return a < b? a : b; }
242static inline int msf_imax(
int a,
int b) {
return b < a? a : b; }
248#if (defined (__SSE2__) || defined (_M_X64) || _M_IX86_FP == 2) && !defined(MSF_GIF_NO_SSE2)
249#include <emmintrin.h>
256 int width,
int height,
int pitch,
int depth)
259 const static int rdepthsArray[17] = { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5 };
260 const static int gdepthsArray[17] = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6 };
261 const static int bdepthsArray[17] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5 };
265 const int * gdepths = gdepthsArray;
268 const static int ditherKernel[16] = {
269 0 << 12, 8 << 12, 2 << 12, 10 << 12,
270 12 << 12, 4 << 12, 14 << 12, 6 << 12,
271 3 << 12, 11 << 12, 1 << 12, 9 << 12,
272 15 << 12, 7 << 12, 13 << 12, 5 << 12,
277 MsfTimeLoop(
"do") do {
278 int rbits = rdepths[depth], gbits = gdepths[depth], bbits = bdepths[depth];
279 int paletteSize = (1 << (rbits + gbits + bbits)) + 1;
280 memset(used, 0, paletteSize *
sizeof(
uint8_t));
283 int rdiff = (1 << (8 - rbits)) - 1;
284 int gdiff = (1 << (8 - gbits)) - 1;
285 int bdiff = (1 << (8 - bbits)) - 1;
286 short rmul = (short) ((255.0f - rdiff) / 255.0f * 257);
287 short gmul = (short) ((255.0f - gdiff) / 255.0f * 257);
288 short bmul = (short) ((255.0f - bdiff) / 255.0f * 257);
290 int gmask = ((1 << gbits) - 1) << rbits;
291 int bmask = ((1 << bbits) - 1) << rbits << gbits;
293 MsfTimeLoop(
"cook") for (
int y = 0; y < height; ++y) {
296 #if (defined (__SSE2__) || defined (_M_X64) || _M_IX86_FP == 2) && !defined(MSF_GIF_NO_SSE2)
297 __m128i k = _mm_loadu_si128((__m128i *) &ditherKernel[(y & 3) * 4]);
298 __m128i k2 = _mm_or_si128(_mm_srli_epi32(k, rbits), _mm_slli_epi32(_mm_srli_epi32(k, bbits), 16));
299 for (; x < width - 3; x += 4) {
300 uint8_t * pixels = &raw[y * pitch + x * 4];
301 __m128i p = _mm_loadu_si128((__m128i *) pixels);
303 __m128i rb = _mm_and_si128(p, _mm_set1_epi32(0x00FF00FF));
304 __m128i rb1 = _mm_mullo_epi16(rb, _mm_set_epi16(bmul, rmul, bmul, rmul, bmul, rmul, bmul, rmul));
305 __m128i rb2 = _mm_adds_epu16(rb1, k2);
306 __m128i r3 = _mm_srli_epi32(_mm_and_si128(rb2, _mm_set1_epi32(0x0000FFFF)), 16 - rbits);
307 __m128i b3 = _mm_and_si128(_mm_srli_epi32(rb2, 32 - rbits - gbits - bbits), _mm_set1_epi32(bmask));
309 __m128i g = _mm_and_si128(_mm_srli_epi32(p, 8), _mm_set1_epi32(0x000000FF));
310 __m128i g1 = _mm_mullo_epi16(g, _mm_set1_epi32(gmul));
311 __m128i g2 = _mm_adds_epu16(g1, _mm_srli_epi32(k, gbits));
312 __m128i g3 = _mm_and_si128(_mm_srli_epi32(g2, 16 - rbits - gbits), _mm_set1_epi32(gmask));
314 __m128i out = _mm_or_si128(_mm_or_si128(r3, g3), b3);
320 out = _mm_or_si128(_mm_and_si128(invAlphaMask, _mm_set1_epi32(paletteSize - 1)), _mm_andnot_si128(invAlphaMask, out));
323 uint32_t * c = &cooked[y * width + x];
324 _mm_storeu_si128((__m128i *) c, out);
329 for (; x < width; ++x) {
330 uint8_t * p = &raw[y * pitch + x * 4];
334 cooked[y * width + x] = paletteSize - 1;
338 int dx = x & 3, dy = y & 3;
339 int k = ditherKernel[dy * 4 + dx];
340 cooked[y * width + x] =
341 (msf_imin(65535, p[2] * bmul + (k >> bbits)) >> (16 - rbits - gbits - bbits) & bmask) |
342 (msf_imin(65535, p[1] * gmul + (k >> gbits)) >> (16 - rbits - gbits ) & gmask) |
343 msf_imin(65535, p[0] * rmul + (k >> rbits)) >> (16 - rbits );
348 MsfTimeLoop(
"mark") for (
int i = 0; i < width * height; ++i) {
353 MsfTimeLoop(
"count") for (
int j = 0; j < paletteSize - 1; ++j) {
356 }
while (count >= 256 && --depth);
358 MsfCookedFrame ret = { cooked, depth, count, rdepths[depth], gdepths[depth], bdepths[depth] };
368 int idx = *blockBits / 8;
369 int bit = *blockBits % 8;
370 (*writeHead)[idx + 0] |= code << bit ;
371 (*writeHead)[idx + 1] |= code >> ( 8 - bit);
372 (*writeHead)[idx + 2] |= code >> (16 - bit);
376 if (*blockBits >= 256 * 8) {
377 *blockBits -= 255 * 8;
379 (*writeHead)[2] = (*writeHead)[1];
380 (*writeHead)[1] = (*writeHead)[0];
381 (*writeHead)[0] = 255;
382 memset((*writeHead) + 4, 0, 256);
392static inline void msf_lzw_reset(MsfStridedList * lzw,
int tableSize,
int stride) { MsfTimeFunc
393 memset(lzw->data, 0xFF, 4096 * stride *
sizeof(
int16_t));
394 lzw->len = tableSize + 2;
395 lzw->stride = stride;
398static MsfGifBuffer * msf_compress_frame(
void * allocContext,
int width,
int height,
int centiSeconds,
403 int maxBufSize = offsetof(
MsfGifBuffer, data) + 32 + 256 * 3 + width * height * 3 / 2;
405 if (!buffer) {
return NULL; }
407 MsfStridedList lzw = { lzwMem };
411 int tlbSize = (1 << totalBits) + 1;
415 typedef struct {
uint8_t r, g, b; } Color3;
416 Color3 table[256] = { {0} };
420 MsfTimeLoop(
"table") for (
int i = 0; i < tlbSize-1; ++i) {
423 int rmask = (1 << frame.
rbits) - 1;
424 int gmask = (1 << frame.
gbits) - 1;
427 int g = i >> frame.
rbits & gmask;
430 r <<= 8 - frame.
rbits;
431 g <<= 8 - frame.
gbits;
432 b <<= 8 - frame.
bbits;
433 table[tableIdx].r = r | r >> frame.
rbits | r >> (frame.
rbits * 2) | r >> (frame.
rbits * 3);
434 table[tableIdx].g = g | g >> frame.
gbits | g >> (frame.
gbits * 2) | g >> (frame.
gbits * 3);
435 table[tableIdx].b = b | b >> frame.
bbits | b >> (frame.
bbits * 2) | b >> (frame.
bbits * 3);
437 uint8_t temp = table[tableIdx].r;
438 table[tableIdx].r = table[tableIdx].b;
439 table[tableIdx].b = temp;
444 int hasTransparentPixels = used[tlbSize-1];
448 int tableBits = msf_imax(2, msf_bit_log(tableIdx - 1));
449 int tableSize = 1 << tableBits;
453 int framesCompatible = hasSamePal && !hasTransparentPixels;
456 char headerBytes[19] =
"\x21\xF9\x04\x05\0\0\0\0" "\x2C\0\0\0\0\0\0\0\0\x80";
462 memcpy(&headerBytes[4], ¢iSeconds, 2);
463 memcpy(&headerBytes[13], &width, 2);
464 memcpy(&headerBytes[15], &height, 2);
465 headerBytes[17] |= tableBits - 1;
466 memcpy(writeHead, headerBytes, 18);
470 memcpy(writeHead, table, tableSize *
sizeof(Color3));
471 writeHead += tableSize *
sizeof(Color3);
472 *writeHead++ = tableBits;
475 memset(writeHead, 0, 260);
480 msf_lzw_reset(&lzw, tableSize, tableIdx);
481 msf_put_code(&writeHead, &blockBits, msf_bit_log(lzw.len - 1), tableSize);
483 int lastCode = framesCompatible && frame.
pixels[0] == previous.
pixels[0]? 0 : tlb[frame.
pixels[0]];
484 MsfTimeLoop(
"compress") for (
int i = 1; i < width * height; ++i) {
486 int color = framesCompatible && frame.
pixels[i] == previous.
pixels[i]? 0 : tlb[frame.
pixels[i]];
487 int code = (&lzw.data[lastCode * lzw.stride])[color];
490 int codeBits = msf_bit_log(lzw.len - 1);
491 msf_put_code(&writeHead, &blockBits, codeBits, lastCode);
493 if (lzw.len > 4095) {
495 msf_put_code(&writeHead, &blockBits, codeBits, tableSize);
496 msf_lzw_reset(&lzw, tableSize, tableIdx);
498 (&lzw.data[lastCode * lzw.stride])[color] = lzw.len;
509 msf_put_code(&writeHead, &blockBits, msf_imin(12, msf_bit_log(lzw.len - 1)), lastCode);
510 msf_put_code(&writeHead, &blockBits, msf_imin(12, msf_bit_log(lzw.len)), tableSize + 1);
514 int bytes = (blockBits + 7) / 8;
515 writeHead[0] = bytes - 1;
522 buffer->
size = writeHead - buffer->
data;
525 if (!moved) {
MSF_GIF_FREE(allocContext, buffer, maxBufSize);
return NULL; }
533static const int lzwAllocSize = 4096 * 256 *
sizeof(
int16_t);
537static void msf_free_gif_state(
MsfGifState * handle) {
556 handle->
width = width;
574 msf_free_gif_state(handle);
582 char headerBytes[33] =
"GIF89a\0\0\0\0\x70\0\0" "\x21\xFF\x0BNETSCAPE2.0\x03\x01\0\0\0";
583 memcpy(&headerBytes[6], &width, 2);
584 memcpy(&headerBytes[8], &height, 2);
591 if (!handle->
listHead) {
return 0; }
593 maxBitDepth = msf_imax(1, msf_imin(16, maxBitDepth));
594 if (pitchInBytes == 0) pitchInBytes = handle->
width * 4;
595 if (pitchInBytes < 0) pixelData -= pitchInBytes * (handle->
height - 1);
603 if (!buffer) { msf_free_gif_state(handle);
return 0; }
628 memcpy(writeHead, node->data, node->size);
629 writeHead += node->size;
635 msf_free_gif_state(handle);
656 if (!
msf_gif_frame(handle, pixelData, centiSecondsPerFame, maxBitDepth, pitchInBytes)) {
return 0; }
int msf_gif_begin_to_file(MsfGifState *handle, int width, int height, MsfGifFileWriteFunc func, void *filePointer)
void msf_gif_free(MsfGifResult result)
int msf_gif_frame(MsfGifState *handle, uint8_t *pixelData, int centiSecondsPerFame, int maxBitDepth, int pitchInBytes)
size_t(* MsfGifFileWriteFunc)(const void *buffer, size_t size, size_t count, void *stream)
int msf_gif_alpha_threshold
int msf_gif_frame_to_file(MsfGifState *handle, uint8_t *pixelData, int centiSecondsPerFame, int maxBitDepth, int pitchInBytes)
int msf_gif_end_to_file(MsfGifState *handle)
MsfGifResult msf_gif_end(MsfGifState *handle)
int msf_gif_begin(MsfGifState *handle, int width, int height)
#define MSF_GIF_MALLOC(contextPointer, newSize)
#define MSF_GIF_FREE(contextPointer, oldMemory, oldSize)
#define MSF_GIF_REALLOC(contextPointer, oldMemory, oldSize, newSize)
struct MsfGifBuffer * next
void * customAllocatorContext
MsfGifFileWriteFunc fileWriteFunc
MsfCookedFrame currentFrame
MsfCookedFrame previousFrame