| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
|
| 17 |
|
| 18 |
|
| 19 |
|
| 20 |
|
| 21 |
|
| 22 |
|
| 23 |
|
| 24 |
|
| 25 |
|
| 26 |
|
| 27 |
|
| 28 |
|
| 29 |
|
| 30 |
|
| 31 |
#ifndef gif_h |
| 32 |
#define gif_h |
| 33 |
|
| 34 |
#include <stdio.h> |
| 35 |
#include <string.h> |
| 36 |
#include <stdint.h> |
| 37 |
|
| 38 |
|
| 39 |
|
| 40 |
|
| 41 |
|
| 42 |
|
| 43 |
|
| 44 |
|
| 45 |
|
| 46 |
#ifndef GIF_TEMP_MALLOC |
| 47 |
#include <stdlib.h> |
| 48 |
#define GIF_TEMP_MALLOC malloc |
| 49 |
#endif |
| 50 |
|
| 51 |
#ifndef GIF_TEMP_FREE |
| 52 |
#include <stdlib.h> |
| 53 |
#define GIF_TEMP_FREE free |
| 54 |
#endif |
| 55 |
|
| 56 |
#ifndef GIF_MALLOC |
| 57 |
#include <stdlib.h> |
| 58 |
#define GIF_MALLOC malloc |
| 59 |
#endif |
| 60 |
|
| 61 |
#ifndef GIF_FREE |
| 62 |
#include <stdlib.h> |
| 63 |
#define GIF_FREE free |
| 64 |
#endif |
| 65 |
|
| 66 |
const int kGifTransIndex = 0; |
| 67 |
|
| 68 |
typedef struct { |
| 69 |
int bitDepth; |
| 70 |
|
| 71 |
uint8_t r[256]; |
| 72 |
uint8_t g[256]; |
| 73 |
uint8_t b[256]; |
| 74 |
|
| 75 |
|
| 76 |
|
| 77 |
|
| 78 |
uint8_t treeSplitElt[255]; |
| 79 |
uint8_t treeSplit[255]; |
| 80 |
} GifPalette; |
| 81 |
|
| 82 |
|
| 83 |
int GifIMax(int l, int r) { return l > r ? l : r; } |
| 84 |
int GifIMin(int l, int r) { return l < r ? l : r; } |
| 85 |
int GifIAbs(int i) { return i < 0 ? -i : i; } |
| 86 |
|
| 87 |
|
| 88 |
|
| 89 |
|
| 90 |
|
| 91 |
void GifGetClosestPaletteColor(GifPalette* pPal, int r, int g, int b, |
| 92 |
int* bestInd, int* bestDiff, int treeRoot) { |
| 93 |
|
| 94 |
if (treeRoot > (1 << pPal->bitDepth) - 1) { |
| 95 |
int ind = treeRoot - (1 << pPal->bitDepth); |
| 96 |
if (ind == kGifTransIndex) return; |
| 97 |
|
| 98 |
|
| 99 |
int r_err = r - ((int32_t)pPal->r[ind]); |
| 100 |
int g_err = g - ((int32_t)pPal->g[ind]); |
| 101 |
int b_err = b - ((int32_t)pPal->b[ind]); |
| 102 |
int diff = GifIAbs(r_err) + GifIAbs(g_err) + GifIAbs(b_err); |
| 103 |
|
| 104 |
if (diff < *bestDiff) { |
| 105 |
*bestInd = ind; |
| 106 |
*bestDiff = diff; |
| 107 |
} |
| 108 |
|
| 109 |
return; |
| 110 |
} |
| 111 |
|
| 112 |
|
| 113 |
int comps[3]; |
| 114 |
comps[0] = r; |
| 115 |
comps[1] = g; |
| 116 |
comps[2] = b; |
| 117 |
int splitComp = comps[pPal->treeSplitElt[treeRoot]]; |
| 118 |
|
| 119 |
int splitPos = pPal->treeSplit[treeRoot]; |
| 120 |
if (splitPos > splitComp) { |
| 121 |
|
| 122 |
GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot * 2); |
| 123 |
if (*bestDiff > splitPos - splitComp) { |
| 124 |
|
| 125 |
|
| 126 |
GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, |
| 127 |
treeRoot * 2 + 1); |
| 128 |
} |
| 129 |
} else { |
| 130 |
GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, |
| 131 |
treeRoot * 2 + 1); |
| 132 |
if (*bestDiff > splitComp - splitPos) { |
| 133 |
GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot * 2); |
| 134 |
} |
| 135 |
} |
| 136 |
} |
| 137 |
|
| 138 |
void GifSwapPixels(uint8_t* image, int pixA, int pixB) { |
| 139 |
uint8_t rA = image[pixA * 4]; |
| 140 |
uint8_t gA = image[pixA * 4 + 1]; |
| 141 |
uint8_t bA = image[pixA * 4 + 2]; |
| 142 |
uint8_t aA = image[pixA * 4 + 3]; |
| 143 |
|
| 144 |
uint8_t rB = image[pixB * 4]; |
| 145 |
uint8_t gB = image[pixB * 4 + 1]; |
| 146 |
uint8_t bB = image[pixB * 4 + 2]; |
| 147 |
uint8_t aB = image[pixA * 4 + 3]; |
| 148 |
|
| 149 |
image[pixA * 4] = rB; |
| 150 |
image[pixA * 4 + 1] = gB; |
| 151 |
image[pixA * 4 + 2] = bB; |
| 152 |
image[pixA * 4 + 3] = aB; |
| 153 |
|
| 154 |
image[pixB * 4] = rA; |
| 155 |
image[pixB * 4 + 1] = gA; |
| 156 |
image[pixB * 4 + 2] = bA; |
| 157 |
image[pixB * 4 + 3] = aA; |
| 158 |
} |
| 159 |
|
| 160 |
|
| 161 |
int GifPartition(uint8_t* image, const int left, const int right, const int elt, |
| 162 |
int pivotIndex) { |
| 163 |
const int pivotValue = image[(pivotIndex)*4 + elt]; |
| 164 |
GifSwapPixels(image, pivotIndex, right - 1); |
| 165 |
int storeIndex = left; |
| 166 |
bool split = 0; |
| 167 |
for (int ii = left; ii < right - 1; ++ii) { |
| 168 |
int arrayVal = image[ii * 4 + elt]; |
| 169 |
if (arrayVal < pivotValue) { |
| 170 |
GifSwapPixels(image, ii, storeIndex); |
| 171 |
++storeIndex; |
| 172 |
} else if (arrayVal == pivotValue) { |
| 173 |
if (split) { |
| 174 |
GifSwapPixels(image, ii, storeIndex); |
| 175 |
++storeIndex; |
| 176 |
} |
| 177 |
split = !split; |
| 178 |
} |
| 179 |
} |
| 180 |
GifSwapPixels(image, storeIndex, right - 1); |
| 181 |
return storeIndex; |
| 182 |
} |
| 183 |
|
| 184 |
|
| 185 |
|
| 186 |
void GifPartitionByMedian(uint8_t* image, int left, int right, int com, |
| 187 |
int neededCenter) { |
| 188 |
if (left < right - 1) { |
| 189 |
int pivotIndex = left + (right - left) / 2; |
| 190 |
|
| 191 |
pivotIndex = GifPartition(image, left, right, com, pivotIndex); |
| 192 |
|
| 193 |
|
| 194 |
if (pivotIndex > neededCenter) |
| 195 |
GifPartitionByMedian(image, left, pivotIndex, com, neededCenter); |
| 196 |
|
| 197 |
if (pivotIndex < neededCenter) |
| 198 |
GifPartitionByMedian(image, pivotIndex + 1, right, com, neededCenter); |
| 199 |
} |
| 200 |
} |
| 201 |
|
| 202 |
|
| 203 |
void GifSplitPalette(uint8_t* image, int numPixels, int firstElt, int lastElt, |
| 204 |
int splitElt, int splitDist, int treeNode, |
| 205 |
bool buildForDither, GifPalette* pal) { |
| 206 |
if (lastElt <= firstElt || numPixels == 0) return; |
| 207 |
|
| 208 |
|
| 209 |
if (lastElt == firstElt + 1) { |
| 210 |
if (buildForDither) { |
| 211 |
|
| 212 |
|
| 213 |
|
| 214 |
if (firstElt == 1) { |
| 215 |
|
| 216 |
uint32_t r = 255, g = 255, b = 255; |
| 217 |
for (int ii = 0; ii < numPixels; ++ii) { |
| 218 |
r = GifIMin(r, image[ii * 4 + 0]); |
| 219 |
g = GifIMin(g, image[ii * 4 + 1]); |
| 220 |
b = GifIMin(b, image[ii * 4 + 2]); |
| 221 |
} |
| 222 |
|
| 223 |
pal->r[firstElt] = r; |
| 224 |
pal->g[firstElt] = g; |
| 225 |
pal->b[firstElt] = b; |
| 226 |
|
| 227 |
return; |
| 228 |
} |
| 229 |
|
| 230 |
if (firstElt == (1 << pal->bitDepth) - 1) { |
| 231 |
|
| 232 |
uint32_t r = 0, g = 0, b = 0; |
| 233 |
for (int ii = 0; ii < numPixels; ++ii) { |
| 234 |
r = GifIMax(r, image[ii * 4 + 0]); |
| 235 |
g = GifIMax(g, image[ii * 4 + 1]); |
| 236 |
b = GifIMax(b, image[ii * 4 + 2]); |
| 237 |
} |
| 238 |
|
| 239 |
pal->r[firstElt] = r; |
| 240 |
pal->g[firstElt] = g; |
| 241 |
pal->b[firstElt] = b; |
| 242 |
|
| 243 |
return; |
| 244 |
} |
| 245 |
} |
| 246 |
|
| 247 |
|
| 248 |
uint64_t r = 0, g = 0, b = 0; |
| 249 |
for (int ii = 0; ii < numPixels; ++ii) { |
| 250 |
r += image[ii * 4 + 0]; |
| 251 |
g += image[ii * 4 + 1]; |
| 252 |
b += image[ii * 4 + 2]; |
| 253 |
} |
| 254 |
|
| 255 |
r += numPixels / 2; |
| 256 |
g += numPixels / 2; |
| 257 |
b += numPixels / 2; |
| 258 |
|
| 259 |
r /= numPixels; |
| 260 |
g /= numPixels; |
| 261 |
b /= numPixels; |
| 262 |
|
| 263 |
pal->r[firstElt] = (uint8_t)r; |
| 264 |
pal->g[firstElt] = (uint8_t)g; |
| 265 |
pal->b[firstElt] = (uint8_t)b; |
| 266 |
|
| 267 |
return; |
| 268 |
} |
| 269 |
|
| 270 |
|
| 271 |
int minR = 255, maxR = 0; |
| 272 |
int minG = 255, maxG = 0; |
| 273 |
int minB = 255, maxB = 0; |
| 274 |
for (int ii = 0; ii < numPixels; ++ii) { |
| 275 |
int r = image[ii * 4 + 0]; |
| 276 |
int g = image[ii * 4 + 1]; |
| 277 |
int b = image[ii * 4 + 2]; |
| 278 |
|
| 279 |
if (r > maxR) maxR = r; |
| 280 |
if (r < minR) minR = r; |
| 281 |
|
| 282 |
if (g > maxG) maxG = g; |
| 283 |
if (g < minG) minG = g; |
| 284 |
|
| 285 |
if (b > maxB) maxB = b; |
| 286 |
if (b < minB) minB = b; |
| 287 |
} |
| 288 |
|
| 289 |
int rRange = maxR - minR; |
| 290 |
int gRange = maxG - minG; |
| 291 |
int bRange = maxB - minB; |
| 292 |
|
| 293 |
|
| 294 |
|
| 295 |
int splitCom = 1; |
| 296 |
if (bRange > gRange) splitCom = 2; |
| 297 |
if (rRange > bRange && rRange > gRange) splitCom = 0; |
| 298 |
|
| 299 |
int subPixelsA = numPixels * (splitElt - firstElt) / (lastElt - firstElt); |
| 300 |
int subPixelsB = numPixels - subPixelsA; |
| 301 |
|
| 302 |
GifPartitionByMedian(image, 0, numPixels, splitCom, subPixelsA); |
| 303 |
|
| 304 |
pal->treeSplitElt[treeNode] = splitCom; |
| 305 |
pal->treeSplit[treeNode] = image[subPixelsA * 4 + splitCom]; |
| 306 |
|
| 307 |
GifSplitPalette(image, subPixelsA, firstElt, splitElt, splitElt - splitDist, |
| 308 |
splitDist / 2, treeNode * 2, buildForDither, pal); |
| 309 |
GifSplitPalette(image + subPixelsA * 4, subPixelsB, splitElt, lastElt, |
| 310 |
splitElt + splitDist, splitDist / 2, treeNode * 2 + 1, |
| 311 |
buildForDither, pal); |
| 312 |
} |
| 313 |
|
| 314 |
|
| 315 |
|
| 316 |
|
| 317 |
|
| 318 |
int GifPickChangedPixels(const uint8_t* lastFrame, uint8_t* frame, |
| 319 |
int numPixels) { |
| 320 |
int numChanged = 0; |
| 321 |
uint8_t* writeIter = frame; |
| 322 |
|
| 323 |
for (int ii = 0; ii < numPixels; ++ii) { |
| 324 |
if (lastFrame[0] != frame[0] || lastFrame[1] != frame[1] || |
| 325 |
lastFrame[2] != frame[2]) { |
| 326 |
writeIter[0] = frame[0]; |
| 327 |
writeIter[1] = frame[1]; |
| 328 |
writeIter[2] = frame[2]; |
| 329 |
++numChanged; |
| 330 |
writeIter += 4; |
| 331 |
} |
| 332 |
lastFrame += 4; |
| 333 |
frame += 4; |
| 334 |
} |
| 335 |
|
| 336 |
return numChanged; |
| 337 |
} |
| 338 |
|
| 339 |
|
| 340 |
|
| 341 |
|
| 342 |
void GifMakePalette(const uint8_t* lastFrame, const uint8_t* nextFrame, |
| 343 |
uint32_t width, uint32_t height, int bitDepth, |
| 344 |
bool buildForDither, GifPalette* pPal) { |
| 345 |
pPal->bitDepth = bitDepth; |
| 346 |
|
| 347 |
|
| 348 |
|
| 349 |
int imageSize = width * height * 4 * sizeof(uint8_t); |
| 350 |
uint8_t* destroyableImage = (uint8_t*)GIF_TEMP_MALLOC(imageSize); |
| 351 |
memcpy(destroyableImage, nextFrame, imageSize); |
| 352 |
|
| 353 |
int numPixels = width * height; |
| 354 |
if (lastFrame) |
| 355 |
numPixels = GifPickChangedPixels(lastFrame, destroyableImage, numPixels); |
| 356 |
|
| 357 |
const int lastElt = 1 << bitDepth; |
| 358 |
const int splitElt = lastElt / 2; |
| 359 |
const int splitDist = splitElt / 2; |
| 360 |
|
| 361 |
GifSplitPalette(destroyableImage, numPixels, 1, lastElt, splitElt, splitDist, |
| 362 |
1, buildForDither, pPal); |
| 363 |
|
| 364 |
GIF_TEMP_FREE(destroyableImage); |
| 365 |
|
| 366 |
|
| 367 |
pPal->treeSplit[1 << (bitDepth - 1)] = 0; |
| 368 |
pPal->treeSplitElt[1 << (bitDepth - 1)] = 0; |
| 369 |
|
| 370 |
pPal->r[0] = pPal->g[0] = pPal->b[0] = 0; |
| 371 |
} |
| 372 |
|
| 373 |
|
| 374 |
void GifDitherImage(const uint8_t* lastFrame, const uint8_t* nextFrame, |
| 375 |
uint8_t* outFrame, uint32_t width, uint32_t height, |
| 376 |
GifPalette* pPal) { |
| 377 |
int numPixels = width * height; |
| 378 |
|
| 379 |
|
| 380 |
|
| 381 |
|
| 382 |
int32_t* quantPixels = |
| 383 |
(int32_t*)GIF_TEMP_MALLOC(sizeof(int32_t) * numPixels * 4); |
| 384 |
|
| 385 |
for (int ii = 0; ii < numPixels * 4; ++ii) { |
| 386 |
uint8_t pix = nextFrame[ii]; |
| 387 |
int32_t pix16 = (int32_t)pix * 256; |
| 388 |
quantPixels[ii] = pix16; |
| 389 |
} |
| 390 |
|
| 391 |
for (uint32_t yy = 0; yy < height; ++yy) { |
| 392 |
for (uint32_t xx = 0; xx < width; ++xx) { |
| 393 |
int32_t* nextPix = quantPixels + 4 * (yy * width + xx); |
| 394 |
const uint8_t* lastPix = |
| 395 |
lastFrame ? lastFrame + 4 * (yy * width + xx) : NULL; |
| 396 |
|
| 397 |
|
| 398 |
int32_t rr = (nextPix[0] + 127) / 256; |
| 399 |
int32_t gg = (nextPix[1] + 127) / 256; |
| 400 |
int32_t bb = (nextPix[2] + 127) / 256; |
| 401 |
|
| 402 |
|
| 403 |
|
| 404 |
|
| 405 |
if (lastFrame && lastPix[0] == rr && lastPix[1] == gg && |
| 406 |
lastPix[2] == bb) { |
| 407 |
nextPix[0] = rr; |
| 408 |
nextPix[1] = gg; |
| 409 |
nextPix[2] = bb; |
| 410 |
nextPix[3] = kGifTransIndex; |
| 411 |
continue; |
| 412 |
} |
| 413 |
|
| 414 |
int32_t bestDiff = 1000000; |
| 415 |
int32_t bestInd = kGifTransIndex; |
| 416 |
|
| 417 |
|
| 418 |
GifGetClosestPaletteColor(pPal, rr, gg, bb, &bestInd, &bestDiff, 1); |
| 419 |
|
| 420 |
|
| 421 |
int32_t r_err = nextPix[0] - (int32_t)(pPal->r[bestInd]) * 256; |
| 422 |
int32_t g_err = nextPix[1] - (int32_t)(pPal->g[bestInd]) * 256; |
| 423 |
int32_t b_err = nextPix[2] - (int32_t)(pPal->b[bestInd]) * 256; |
| 424 |
|
| 425 |
nextPix[0] = pPal->r[bestInd]; |
| 426 |
nextPix[1] = pPal->g[bestInd]; |
| 427 |
nextPix[2] = pPal->b[bestInd]; |
| 428 |
nextPix[3] = bestInd; |
| 429 |
|
| 430 |
|
| 431 |
|
| 432 |
int quantloc_7 = (yy * width + xx + 1); |
| 433 |
int quantloc_3 = (yy * width + width + xx - 1); |
| 434 |
int quantloc_5 = (yy * width + width + xx); |
| 435 |
int quantloc_1 = (yy * width + width + xx + 1); |
| 436 |
|
| 437 |
if (quantloc_7 < numPixels) { |
| 438 |
int32_t* pix7 = quantPixels + 4 * quantloc_7; |
| 439 |
pix7[0] += GifIMax(-pix7[0], r_err * 7 / 16); |
| 440 |
pix7[1] += GifIMax(-pix7[1], g_err * 7 / 16); |
| 441 |
pix7[2] += GifIMax(-pix7[2], b_err * 7 / 16); |
| 442 |
} |
| 443 |
|
| 444 |
if (quantloc_3 < numPixels) { |
| 445 |
int32_t* pix3 = quantPixels + 4 * quantloc_3; |
| 446 |
pix3[0] += GifIMax(-pix3[0], r_err * 3 / 16); |
| 447 |
pix3[1] += GifIMax(-pix3[1], g_err * 3 / 16); |
| 448 |
pix3[2] += GifIMax(-pix3[2], b_err * 3 / 16); |
| 449 |
} |
| 450 |
|
| 451 |
if (quantloc_5 < numPixels) { |
| 452 |
int32_t* pix5 = quantPixels + 4 * quantloc_5; |
| 453 |
pix5[0] += GifIMax(-pix5[0], r_err * 5 / 16); |
| 454 |
pix5[1] += GifIMax(-pix5[1], g_err * 5 / 16); |
| 455 |
pix5[2] += GifIMax(-pix5[2], b_err * 5 / 16); |
| 456 |
} |
| 457 |
|
| 458 |
if (quantloc_1 < numPixels) { |
| 459 |
int32_t* pix1 = quantPixels + 4 * quantloc_1; |
| 460 |
pix1[0] += GifIMax(-pix1[0], r_err / 16); |
| 461 |
pix1[1] += GifIMax(-pix1[1], g_err / 16); |
| 462 |
pix1[2] += GifIMax(-pix1[2], b_err / 16); |
| 463 |
} |
| 464 |
} |
| 465 |
} |
| 466 |
|
| 467 |
|
| 468 |
for (int ii = 0; ii < numPixels * 4; ++ii) { |
| 469 |
outFrame[ii] = quantPixels[ii]; |
| 470 |
} |
| 471 |
|
| 472 |
GIF_TEMP_FREE(quantPixels); |
| 473 |
} |
| 474 |
|
| 475 |
|
| 476 |
void GifThresholdImage(const uint8_t* lastFrame, const uint8_t* nextFrame, |
| 477 |
uint8_t* outFrame, uint32_t width, uint32_t height, |
| 478 |
GifPalette* pPal) { |
| 479 |
uint32_t numPixels = width * height; |
| 480 |
for (uint32_t ii = 0; ii < numPixels; ++ii) { |
| 481 |
|
| 482 |
|
| 483 |
if (lastFrame && lastFrame[0] == nextFrame[0] && |
| 484 |
lastFrame[1] == nextFrame[1] && lastFrame[2] == nextFrame[2]) { |
| 485 |
outFrame[0] = lastFrame[0]; |
| 486 |
outFrame[1] = lastFrame[1]; |
| 487 |
outFrame[2] = lastFrame[2]; |
| 488 |
outFrame[3] = kGifTransIndex; |
| 489 |
} else { |
| 490 |
|
| 491 |
int32_t bestDiff = 1000000; |
| 492 |
int32_t bestInd = 1; |
| 493 |
GifGetClosestPaletteColor(pPal, nextFrame[0], nextFrame[1], nextFrame[2], |
| 494 |
&bestInd, &bestDiff, 1); |
| 495 |
|
| 496 |
|
| 497 |
outFrame[0] = pPal->r[bestInd]; |
| 498 |
outFrame[1] = pPal->g[bestInd]; |
| 499 |
outFrame[2] = pPal->b[bestInd]; |
| 500 |
outFrame[3] = bestInd; |
| 501 |
} |
| 502 |
|
| 503 |
if (lastFrame) lastFrame += 4; |
| 504 |
outFrame += 4; |
| 505 |
nextFrame += 4; |
| 506 |
} |
| 507 |
} |
| 508 |
|
| 509 |
|
| 510 |
|
| 511 |
typedef struct { |
| 512 |
uint8_t bitIndex; |
| 513 |
uint8_t byte; |
| 514 |
|
| 515 |
uint32_t chunkIndex; |
| 516 |
uint8_t chunk[256]; |
| 517 |
|
| 518 |
} GifBitStatus; |
| 519 |
|
| 520 |
|
| 521 |
void GifWriteBit(GifBitStatus* stat, uint32_t bit) { |
| 522 |
bit = bit & 1; |
| 523 |
bit = bit << stat->bitIndex; |
| 524 |
stat->byte |= bit; |
| 525 |
|
| 526 |
++stat->bitIndex; |
| 527 |
if (stat->bitIndex > 7) { |
| 528 |
|
| 529 |
stat->chunk[stat->chunkIndex++] = stat->byte; |
| 530 |
|
| 531 |
stat->bitIndex = 0; |
| 532 |
stat->byte = 0; |
| 533 |
} |
| 534 |
} |
| 535 |
|
| 536 |
|
| 537 |
void GifWriteChunk(FILE* f, GifBitStatus* stat) { |
| 538 |
fputc(stat->chunkIndex, f); |
| 539 |
fwrite(stat->chunk, 1, stat->chunkIndex, f); |
| 540 |
|
| 541 |
stat->bitIndex = 0; |
| 542 |
stat->byte = 0; |
| 543 |
stat->chunkIndex = 0; |
| 544 |
} |
| 545 |
|
| 546 |
void GifWriteCode(FILE* f, GifBitStatus* stat, uint32_t code, uint32_t length) { |
| 547 |
for (uint32_t ii = 0; ii < length; ++ii) { |
| 548 |
GifWriteBit(stat, code); |
| 549 |
code = code >> 1; |
| 550 |
|
| 551 |
if (stat->chunkIndex == 255) { |
| 552 |
GifWriteChunk(f, stat); |
| 553 |
} |
| 554 |
} |
| 555 |
} |
| 556 |
|
| 557 |
|
| 558 |
|
| 559 |
typedef struct { |
| 560 |
uint16_t m_next[256]; |
| 561 |
} GifLzwNode; |
| 562 |
|
| 563 |
|
| 564 |
void GifWritePalette(const GifPalette* pPal, FILE* f) { |
| 565 |
fputc(0, f); |
| 566 |
fputc(0, f); |
| 567 |
fputc(0, f); |
| 568 |
|
| 569 |
for (int ii = 1; ii < (1 << pPal->bitDepth); ++ii) { |
| 570 |
uint32_t r = pPal->r[ii]; |
| 571 |
uint32_t g = pPal->g[ii]; |
| 572 |
uint32_t b = pPal->b[ii]; |
| 573 |
|
| 574 |
fputc(b, f); |
| 575 |
fputc(g, f); |
| 576 |
fputc(r, f); |
| 577 |
} |
| 578 |
} |
| 579 |
|
| 580 |
|
| 581 |
void GifWriteLzwImage(FILE* f, uint8_t* image, uint32_t left, uint32_t top, |
| 582 |
uint32_t width, uint32_t height, uint32_t delay, |
| 583 |
GifPalette* pPal) { |
| 584 |
|
| 585 |
fputc(0x21, f); |
| 586 |
fputc(0xf9, f); |
| 587 |
fputc(0x04, f); |
| 588 |
fputc(0x05, f); |
| 589 |
fputc(delay & 0xff, f); |
| 590 |
fputc((delay >> 8) & 0xff, f); |
| 591 |
fputc(kGifTransIndex, f); |
| 592 |
fputc(0, f); |
| 593 |
|
| 594 |
fputc(0x2c, f); |
| 595 |
|
| 596 |
fputc(left & 0xff, f); |
| 597 |
fputc((left >> 8) & 0xff, f); |
| 598 |
fputc(top & 0xff, f); |
| 599 |
fputc((top >> 8) & 0xff, f); |
| 600 |
|
| 601 |
fputc(width & 0xff, f); |
| 602 |
fputc((width >> 8) & 0xff, f); |
| 603 |
fputc(height & 0xff, f); |
| 604 |
fputc((height >> 8) & 0xff, f); |
| 605 |
|
| 606 |
|
| 607 |
|
| 608 |
|
| 609 |
fputc(0x80 + pPal->bitDepth - 1, |
| 610 |
f); |
| 611 |
GifWritePalette(pPal, f); |
| 612 |
|
| 613 |
const int minCodeSize = pPal->bitDepth; |
| 614 |
const uint32_t clearCode = 1 << pPal->bitDepth; |
| 615 |
|
| 616 |
fputc(minCodeSize, f); |
| 617 |
|
| 618 |
GifLzwNode* codetree = |
| 619 |
(GifLzwNode*)GIF_TEMP_MALLOC(sizeof(GifLzwNode) * 4096); |
| 620 |
|
| 621 |
memset(codetree, 0, sizeof(GifLzwNode) * 4096); |
| 622 |
int32_t curCode = -1; |
| 623 |
uint32_t codeSize = minCodeSize + 1; |
| 624 |
uint32_t maxCode = clearCode + 1; |
| 625 |
|
| 626 |
GifBitStatus stat; |
| 627 |
stat.byte = 0; |
| 628 |
stat.bitIndex = 0; |
| 629 |
stat.chunkIndex = 0; |
| 630 |
|
| 631 |
GifWriteCode(f, &stat, clearCode, |
| 632 |
codeSize); |
| 633 |
|
| 634 |
for (uint32_t yy = 0; yy < height; ++yy) { |
| 635 |
for (uint32_t xx = 0; xx < width; ++xx) { |
| 636 |
uint8_t nextValue = image[(yy * width + xx) * 4 + 3]; |
| 637 |
|
| 638 |
|
| 639 |
|
| 640 |
|
| 641 |
|
| 642 |
|
| 643 |
if (curCode < 0) { |
| 644 |
|
| 645 |
curCode = nextValue; |
| 646 |
} else if (codetree[curCode].m_next[nextValue]) { |
| 647 |
|
| 648 |
curCode = codetree[curCode].m_next[nextValue]; |
| 649 |
} else { |
| 650 |
|
| 651 |
GifWriteCode(f, &stat, curCode, codeSize); |
| 652 |
|
| 653 |
|
| 654 |
codetree[curCode].m_next[nextValue] = ++maxCode; |
| 655 |
|
| 656 |
if (maxCode >= (1ul << codeSize)) { |
| 657 |
|
| 658 |
|
| 659 |
codeSize++; |
| 660 |
} |
| 661 |
if (maxCode == 4095) { |
| 662 |
|
| 663 |
GifWriteCode(f, &stat, clearCode, codeSize); |
| 664 |
|
| 665 |
memset(codetree, 0, sizeof(GifLzwNode) * 4096); |
| 666 |
curCode = -1; |
| 667 |
codeSize = minCodeSize + 1; |
| 668 |
maxCode = clearCode + 1; |
| 669 |
} |
| 670 |
|
| 671 |
curCode = nextValue; |
| 672 |
} |
| 673 |
} |
| 674 |
} |
| 675 |
|
| 676 |
|
| 677 |
GifWriteCode(f, &stat, curCode, codeSize); |
| 678 |
GifWriteCode(f, &stat, clearCode, codeSize); |
| 679 |
GifWriteCode(f, &stat, clearCode + 1, minCodeSize + 1); |
| 680 |
|
| 681 |
|
| 682 |
while (stat.bitIndex) GifWriteBit(&stat, 0); |
| 683 |
if (stat.chunkIndex) GifWriteChunk(f, &stat); |
| 684 |
|
| 685 |
fputc(0, f); |
| 686 |
|
| 687 |
GIF_TEMP_FREE(codetree); |
| 688 |
} |
| 689 |
|
| 690 |
typedef struct { |
| 691 |
FILE* f; |
| 692 |
uint8_t* oldImage; |
| 693 |
bool firstFrame; |
| 694 |
} GifWriter; |
| 695 |
|
| 696 |
|
| 697 |
|
| 698 |
|
| 699 |
|
| 700 |
bool GifBegin(GifWriter* writer, const char* filename, uint32_t width, |
| 701 |
uint32_t height, uint32_t delay, int32_t bitDepth, |
| 702 |
bool dither) { |
| 703 |
#if _MSC_VER >= 1400 |
| 704 |
writer->f = 0; |
| 705 |
fopen_s(&writer->f, filename, "wb"); |
| 706 |
#else |
| 707 |
writer->f = fopen(filename, "wb"); |
| 708 |
#endif |
| 709 |
if (!writer->f) return false; |
| 710 |
|
| 711 |
writer->firstFrame = true; |
| 712 |
|
| 713 |
|
| 714 |
writer->oldImage = (uint8_t*)GIF_MALLOC(width * height * 4); |
| 715 |
|
| 716 |
fputs("GIF89a", writer->f); |
| 717 |
|
| 718 |
|
| 719 |
fputc(width & 0xff, writer->f); |
| 720 |
fputc((width >> 8) & 0xff, writer->f); |
| 721 |
fputc(height & 0xff, writer->f); |
| 722 |
fputc((height >> 8) & 0xff, writer->f); |
| 723 |
|
| 724 |
fputc(0xf0, |
| 725 |
writer->f); |
| 726 |
fputc(0, writer->f); |
| 727 |
fputc(0, writer->f); |
| 728 |
|
| 729 |
|
| 730 |
|
| 731 |
|
| 732 |
fputc(0, writer->f); |
| 733 |
fputc(0, writer->f); |
| 734 |
fputc(0, writer->f); |
| 735 |
|
| 736 |
fputc(0, writer->f); |
| 737 |
fputc(0, writer->f); |
| 738 |
fputc(0, writer->f); |
| 739 |
|
| 740 |
if (delay != 0) { |
| 741 |
|
| 742 |
fputc(0x21, writer->f); |
| 743 |
fputc(0xff, writer->f); |
| 744 |
fputc(11, writer->f); |
| 745 |
fputs("NETSCAPE2.0", writer->f); |
| 746 |
fputc(3, writer->f); |
| 747 |
|
| 748 |
fputc(1, writer->f); |
| 749 |
fputc(0, writer->f); |
| 750 |
fputc(0, writer->f); |
| 751 |
|
| 752 |
fputc(0, writer->f); |
| 753 |
} |
| 754 |
|
| 755 |
return true; |
| 756 |
} |
| 757 |
|
| 758 |
|
| 759 |
|
| 760 |
|
| 761 |
|
| 762 |
|
| 763 |
bool GifWriteFrame(GifWriter* writer, const uint8_t* image, uint32_t width, |
| 764 |
uint32_t height, uint32_t delay, int bitDepth, |
| 765 |
bool dither) { |
| 766 |
if (!writer->f) return false; |
| 767 |
|
| 768 |
const uint8_t* oldImage = writer->firstFrame ? NULL : writer->oldImage; |
| 769 |
writer->firstFrame = false; |
| 770 |
|
| 771 |
GifPalette pal; |
| 772 |
GifMakePalette((dither ? NULL : oldImage), image, width, height, bitDepth, |
| 773 |
dither, &pal); |
| 774 |
|
| 775 |
if (dither) |
| 776 |
GifDitherImage(oldImage, image, writer->oldImage, width, height, &pal); |
| 777 |
else |
| 778 |
GifThresholdImage(oldImage, image, writer->oldImage, width, height, &pal); |
| 779 |
|
| 780 |
GifWriteLzwImage(writer->f, writer->oldImage, 0, 0, width, height, delay, |
| 781 |
&pal); |
| 782 |
|
| 783 |
return true; |
| 784 |
} |
| 785 |
|
| 786 |
|
| 787 |
|
| 788 |
|
| 789 |
|
| 790 |
|
| 791 |
bool GifEnd(GifWriter* writer) { |
| 792 |
if (!writer->f) return false; |
| 793 |
|
| 794 |
fputc(0x3b, writer->f); |
| 795 |
fclose(writer->f); |
| 796 |
GIF_FREE(writer->oldImage); |
| 797 |
|
| 798 |
writer->f = NULL; |
| 799 |
writer->oldImage = NULL; |
| 800 |
|
| 801 |
return true; |
| 802 |
} |
| 803 |
|
| 804 |
#endif |