Provide-gif-quantization-for-giflib-5.2.patch 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. From: Sven Eckelmann <sven@narfation.org>
  2. Date: Sat, 31 Aug 2019 14:13:11 +0200
  3. Subject: Provide gif quantization for giflib >= 5.2
  4. Bug-Debian: https://bugs.debian.org/939031
  5. diff -Naur exact-image-1.0.2.orig/codecs/Makefile exact-image-1.0.2/codecs/Makefile
  6. --- exact-image-1.0.2.orig/codecs/Makefile 2016-06-18 21:49:25.000000000 +0200
  7. +++ exact-image-1.0.2/codecs/Makefile 2020-12-11 15:30:44.374504000 +0100
  8. @@ -21,7 +21,7 @@
  9. ifeq "$(WITHLIBGIF)" "1"
  10. LDFLAGS += -lgif
  11. else
  12. -NOT_SRCS += gif.cc
  13. +NOT_SRCS += gif.cc gif_quantization.c
  14. endif
  15. ifeq "$(WITHJASPER)" "1"
  16. diff -Naur exact-image-1.0.2.orig/codecs/gif.cc exact-image-1.0.2/codecs/gif.cc
  17. --- exact-image-1.0.2.orig/codecs/gif.cc 2017-07-21 16:19:01.000000000 +0200
  18. +++ exact-image-1.0.2/codecs/gif.cc 2020-12-11 15:34:55.340504000 +0100
  19. @@ -160,6 +160,17 @@
  20. return true;
  21. }
  22. +extern "C" int
  23. +eiGifQuantizeBuffer(unsigned int Width,
  24. + unsigned int Height,
  25. + int *ColorMapSize,
  26. + GifByteType * RedInput,
  27. + GifByteType * GreenInput,
  28. + GifByteType * BlueInput,
  29. + GifByteType * OutputBuffer,
  30. + GifColorType * OutputColorMap);
  31. +
  32. +
  33. bool GIFCodec::writeImage (std::ostream* stream, Image& image, int quality,
  34. const std::string& compress)
  35. {
  36. @@ -203,8 +214,7 @@
  37. *bptr++ = b;
  38. }
  39. -
  40. - if (GifQuantizeBuffer(image.w, image.h, &ColorMapSize,
  41. + if (eiGifQuantizeBuffer(image.w, image.h, &ColorMapSize,
  42. RedBuffer, GreenBuffer, BlueBuffer,
  43. OutputBuffer, OutputColorMap->Colors) == GIF_ERROR) {
  44. return false;
  45. diff -Naur exact-image-1.0.2.orig/codecs/gif_quantization.c exact-image-1.0.2/codecs/gif_quantization.c
  46. --- exact-image-1.0.2.orig/codecs/gif_quantization.c 1970-01-01 01:00:00.000000000 +0100
  47. +++ exact-image-1.0.2/codecs/gif_quantization.c 2020-12-11 15:30:19.181504000 +0100
  48. @@ -0,0 +1,331 @@
  49. +/*****************************************************************************
  50. +
  51. + quantize.c - quantize a high resolution image into lower one
  52. +
  53. + Based on: "Color Image Quantization for frame buffer Display", by
  54. + Paul Heckbert SIGGRAPH 1982 page 297-307.
  55. +
  56. + This doesn't really belong in the core library, was undocumented,
  57. + and was removed in 4.2. Then it turned out some client apps were
  58. + actually using it, so it was restored in 5.0.
  59. +
  60. +SPDX-License-Identifier: MIT
  61. +
  62. +******************************************************************************/
  63. +
  64. +#include <stdlib.h>
  65. +#include <stdio.h>
  66. +#include "gif_lib.h"
  67. +
  68. +#define ABS(x) ((x) > 0 ? (x) : (-(x)))
  69. +
  70. +#define COLOR_ARRAY_SIZE 32768
  71. +#define BITS_PER_PRIM_COLOR 5
  72. +#define MAX_PRIM_COLOR 0x1f
  73. +
  74. +static int SortRGBAxis;
  75. +
  76. +typedef struct QuantizedColorType {
  77. + GifByteType RGB[3];
  78. + GifByteType NewColorIndex;
  79. + long Count;
  80. + struct QuantizedColorType *Pnext;
  81. +} QuantizedColorType;
  82. +
  83. +typedef struct NewColorMapType {
  84. + GifByteType RGBMin[3], RGBWidth[3];
  85. + unsigned int NumEntries; /* # of QuantizedColorType in linked list below */
  86. + unsigned long Count; /* Total number of pixels in all the entries */
  87. + QuantizedColorType *QuantizedColors;
  88. +} NewColorMapType;
  89. +
  90. +static int SubdivColorMap(NewColorMapType * NewColorSubdiv,
  91. + unsigned int ColorMapSize,
  92. + unsigned int *NewColorMapSize);
  93. +static int SortCmpRtn(const void *Entry1, const void *Entry2);
  94. +
  95. +/******************************************************************************
  96. + Quantize high resolution image into lower one. Input image consists of a
  97. + 2D array for each of the RGB colors with size Width by Height. There is no
  98. + Color map for the input. Output is a quantized image with 2D array of
  99. + indexes into the output color map.
  100. + Note input image can be 24 bits at the most (8 for red/green/blue) and
  101. + the output has 256 colors at the most (256 entries in the color map.).
  102. + ColorMapSize specifies size of color map up to 256 and will be updated to
  103. + real size before returning.
  104. + Also non of the parameter are allocated by this routine.
  105. + This function returns GIF_OK if successful, GIF_ERROR otherwise.
  106. +******************************************************************************/
  107. +int
  108. +eiGifQuantizeBuffer(unsigned int Width,
  109. + unsigned int Height,
  110. + int *ColorMapSize,
  111. + GifByteType * RedInput,
  112. + GifByteType * GreenInput,
  113. + GifByteType * BlueInput,
  114. + GifByteType * OutputBuffer,
  115. + GifColorType * OutputColorMap) {
  116. +
  117. + unsigned int Index, NumOfEntries;
  118. + int i, j, MaxRGBError[3];
  119. + unsigned int NewColorMapSize;
  120. + long Red, Green, Blue;
  121. + NewColorMapType NewColorSubdiv[256];
  122. + QuantizedColorType *ColorArrayEntries, *QuantizedColor;
  123. +
  124. + ColorArrayEntries = (QuantizedColorType *)malloc(
  125. + sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE);
  126. + if (ColorArrayEntries == NULL) {
  127. + return GIF_ERROR;
  128. + }
  129. +
  130. + for (i = 0; i < COLOR_ARRAY_SIZE; i++) {
  131. + ColorArrayEntries[i].RGB[0] = i >> (2 * BITS_PER_PRIM_COLOR);
  132. + ColorArrayEntries[i].RGB[1] = (i >> BITS_PER_PRIM_COLOR) &
  133. + MAX_PRIM_COLOR;
  134. + ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR;
  135. + ColorArrayEntries[i].Count = 0;
  136. + }
  137. +
  138. + /* Sample the colors and their distribution: */
  139. + for (i = 0; i < (int)(Width * Height); i++) {
  140. + Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
  141. + (2 * BITS_PER_PRIM_COLOR)) +
  142. + ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
  143. + BITS_PER_PRIM_COLOR) +
  144. + (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
  145. + ColorArrayEntries[Index].Count++;
  146. + }
  147. +
  148. + /* Put all the colors in the first entry of the color map, and call the
  149. + * recursive subdivision process. */
  150. + for (i = 0; i < 256; i++) {
  151. + NewColorSubdiv[i].QuantizedColors = NULL;
  152. + NewColorSubdiv[i].Count = NewColorSubdiv[i].NumEntries = 0;
  153. + for (j = 0; j < 3; j++) {
  154. + NewColorSubdiv[i].RGBMin[j] = 0;
  155. + NewColorSubdiv[i].RGBWidth[j] = 255;
  156. + }
  157. + }
  158. +
  159. + /* Find the non empty entries in the color table and chain them: */
  160. + for (i = 0; i < COLOR_ARRAY_SIZE; i++)
  161. + if (ColorArrayEntries[i].Count > 0)
  162. + break;
  163. + QuantizedColor = NewColorSubdiv[0].QuantizedColors = &ColorArrayEntries[i];
  164. + NumOfEntries = 1;
  165. + while (++i < COLOR_ARRAY_SIZE)
  166. + if (ColorArrayEntries[i].Count > 0) {
  167. + QuantizedColor->Pnext = &ColorArrayEntries[i];
  168. + QuantizedColor = &ColorArrayEntries[i];
  169. + NumOfEntries++;
  170. + }
  171. + QuantizedColor->Pnext = NULL;
  172. +
  173. + NewColorSubdiv[0].NumEntries = NumOfEntries; /* Different sampled colors */
  174. + NewColorSubdiv[0].Count = ((long)Width) * Height; /* Pixels */
  175. + NewColorMapSize = 1;
  176. + if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &NewColorMapSize) !=
  177. + GIF_OK) {
  178. + free((char *)ColorArrayEntries);
  179. + return GIF_ERROR;
  180. + }
  181. + if (NewColorMapSize < *ColorMapSize) {
  182. + /* And clear rest of color map: */
  183. + for (i = NewColorMapSize; i < *ColorMapSize; i++)
  184. + OutputColorMap[i].Red = OutputColorMap[i].Green =
  185. + OutputColorMap[i].Blue = 0;
  186. + }
  187. +
  188. + /* Average the colors in each entry to be the color to be used in the
  189. + * output color map, and plug it into the output color map itself. */
  190. + for (i = 0; i < NewColorMapSize; i++) {
  191. + if ((j = NewColorSubdiv[i].NumEntries) > 0) {
  192. + QuantizedColor = NewColorSubdiv[i].QuantizedColors;
  193. + Red = Green = Blue = 0;
  194. + while (QuantizedColor) {
  195. + QuantizedColor->NewColorIndex = i;
  196. + Red += QuantizedColor->RGB[0];
  197. + Green += QuantizedColor->RGB[1];
  198. + Blue += QuantizedColor->RGB[2];
  199. + QuantizedColor = QuantizedColor->Pnext;
  200. + }
  201. + OutputColorMap[i].Red = (Red << (8 - BITS_PER_PRIM_COLOR)) / j;
  202. + OutputColorMap[i].Green = (Green << (8 - BITS_PER_PRIM_COLOR)) / j;
  203. + OutputColorMap[i].Blue = (Blue << (8 - BITS_PER_PRIM_COLOR)) / j;
  204. + }
  205. + }
  206. +
  207. + /* Finally scan the input buffer again and put the mapped index in the
  208. + * output buffer. */
  209. + MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0;
  210. + for (i = 0; i < (int)(Width * Height); i++) {
  211. + Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
  212. + (2 * BITS_PER_PRIM_COLOR)) +
  213. + ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
  214. + BITS_PER_PRIM_COLOR) +
  215. + (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
  216. + Index = ColorArrayEntries[Index].NewColorIndex;
  217. + OutputBuffer[i] = Index;
  218. + if (MaxRGBError[0] < ABS(OutputColorMap[Index].Red - RedInput[i]))
  219. + MaxRGBError[0] = ABS(OutputColorMap[Index].Red - RedInput[i]);
  220. + if (MaxRGBError[1] < ABS(OutputColorMap[Index].Green - GreenInput[i]))
  221. + MaxRGBError[1] = ABS(OutputColorMap[Index].Green - GreenInput[i]);
  222. + if (MaxRGBError[2] < ABS(OutputColorMap[Index].Blue - BlueInput[i]))
  223. + MaxRGBError[2] = ABS(OutputColorMap[Index].Blue - BlueInput[i]);
  224. + }
  225. +
  226. +#ifdef DEBUG
  227. + fprintf(stderr,
  228. + "Quantization L(0) errors: Red = %d, Green = %d, Blue = %d.\n",
  229. + MaxRGBError[0], MaxRGBError[1], MaxRGBError[2]);
  230. +#endif /* DEBUG */
  231. +
  232. + free((char *)ColorArrayEntries);
  233. +
  234. + *ColorMapSize = NewColorMapSize;
  235. +
  236. + return GIF_OK;
  237. +}
  238. +
  239. +/******************************************************************************
  240. + Routine to subdivide the RGB space recursively using median cut in each
  241. + axes alternatingly until ColorMapSize different cubes exists.
  242. + The biggest cube in one dimension is subdivide unless it has only one entry.
  243. + Returns GIF_ERROR if failed, otherwise GIF_OK.
  244. +*******************************************************************************/
  245. +static int
  246. +SubdivColorMap(NewColorMapType * NewColorSubdiv,
  247. + unsigned int ColorMapSize,
  248. + unsigned int *NewColorMapSize) {
  249. +
  250. + unsigned int i, j, Index = 0;
  251. + QuantizedColorType *QuantizedColor, **SortArray;
  252. +
  253. + while (ColorMapSize > *NewColorMapSize) {
  254. + /* Find candidate for subdivision: */
  255. + long Sum, Count;
  256. + int MaxSize = -1;
  257. + unsigned int NumEntries, MinColor, MaxColor;
  258. + for (i = 0; i < *NewColorMapSize; i++) {
  259. + for (j = 0; j < 3; j++) {
  260. + if ((((int)NewColorSubdiv[i].RGBWidth[j]) > MaxSize) &&
  261. + (NewColorSubdiv[i].NumEntries > 1)) {
  262. + MaxSize = NewColorSubdiv[i].RGBWidth[j];
  263. + Index = i;
  264. + SortRGBAxis = j;
  265. + }
  266. + }
  267. + }
  268. +
  269. + if (MaxSize == -1)
  270. + return GIF_OK;
  271. +
  272. + /* Split the entry Index into two along the axis SortRGBAxis: */
  273. +
  274. + /* Sort all elements in that entry along the given axis and split at
  275. + * the median. */
  276. + SortArray = (QuantizedColorType **)malloc(
  277. + sizeof(QuantizedColorType *) *
  278. + NewColorSubdiv[Index].NumEntries);
  279. + if (SortArray == NULL)
  280. + return GIF_ERROR;
  281. + for (j = 0, QuantizedColor = NewColorSubdiv[Index].QuantizedColors;
  282. + j < NewColorSubdiv[Index].NumEntries && QuantizedColor != NULL;
  283. + j++, QuantizedColor = QuantizedColor->Pnext)
  284. + SortArray[j] = QuantizedColor;
  285. +
  286. + /*
  287. + * Because qsort isn't stable, this can produce differing
  288. + * results for the order of tuples depending on platform
  289. + * details of how qsort() is implemented.
  290. + *
  291. + * We mitigate this problem by sorting on all three axes rather
  292. + * than only the one specied by SortRGBAxis; that way the instability
  293. + * can only become an issue if there are multiple color indices
  294. + * referring to identical RGB tuples. Older versions of this
  295. + * sorted on only the one axis.
  296. + */
  297. + qsort(SortArray, NewColorSubdiv[Index].NumEntries,
  298. + sizeof(QuantizedColorType *), SortCmpRtn);
  299. +
  300. + /* Relink the sorted list into one: */
  301. + for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++)
  302. + SortArray[j]->Pnext = SortArray[j + 1];
  303. + SortArray[NewColorSubdiv[Index].NumEntries - 1]->Pnext = NULL;
  304. + NewColorSubdiv[Index].QuantizedColors = QuantizedColor = SortArray[0];
  305. + free((char *)SortArray);
  306. +
  307. + /* Now simply add the Counts until we have half of the Count: */
  308. + Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor->Count;
  309. + NumEntries = 1;
  310. + Count = QuantizedColor->Count;
  311. + while (QuantizedColor->Pnext != NULL &&
  312. + (Sum -= QuantizedColor->Pnext->Count) >= 0 &&
  313. + QuantizedColor->Pnext->Pnext != NULL) {
  314. + QuantizedColor = QuantizedColor->Pnext;
  315. + NumEntries++;
  316. + Count += QuantizedColor->Count;
  317. + }
  318. + /* Save the values of the last color of the first half, and first
  319. + * of the second half so we can update the Bounding Boxes later.
  320. + * Also as the colors are quantized and the BBoxes are full 0..255,
  321. + * they need to be rescaled.
  322. + */
  323. + MaxColor = QuantizedColor->RGB[SortRGBAxis]; /* Max. of first half */
  324. + /* coverity[var_deref_op] */
  325. + MinColor = QuantizedColor->Pnext->RGB[SortRGBAxis]; /* of second */
  326. + MaxColor <<= (8 - BITS_PER_PRIM_COLOR);
  327. + MinColor <<= (8 - BITS_PER_PRIM_COLOR);
  328. +
  329. + /* Partition right here: */
  330. + NewColorSubdiv[*NewColorMapSize].QuantizedColors =
  331. + QuantizedColor->Pnext;
  332. + QuantizedColor->Pnext = NULL;
  333. + NewColorSubdiv[*NewColorMapSize].Count = Count;
  334. + NewColorSubdiv[Index].Count -= Count;
  335. + NewColorSubdiv[*NewColorMapSize].NumEntries =
  336. + NewColorSubdiv[Index].NumEntries - NumEntries;
  337. + NewColorSubdiv[Index].NumEntries = NumEntries;
  338. + for (j = 0; j < 3; j++) {
  339. + NewColorSubdiv[*NewColorMapSize].RGBMin[j] =
  340. + NewColorSubdiv[Index].RGBMin[j];
  341. + NewColorSubdiv[*NewColorMapSize].RGBWidth[j] =
  342. + NewColorSubdiv[Index].RGBWidth[j];
  343. + }
  344. + NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] =
  345. + NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] +
  346. + NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] - MinColor;
  347. + NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor;
  348. +
  349. + NewColorSubdiv[Index].RGBWidth[SortRGBAxis] =
  350. + MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis];
  351. +
  352. + (*NewColorMapSize)++;
  353. + }
  354. +
  355. + return GIF_OK;
  356. +}
  357. +
  358. +/****************************************************************************
  359. + Routine called by qsort to compare two entries.
  360. +*****************************************************************************/
  361. +
  362. +static int
  363. +SortCmpRtn(const void *Entry1,
  364. + const void *Entry2) {
  365. + QuantizedColorType *entry1 = (*((QuantizedColorType **) Entry1));
  366. + QuantizedColorType *entry2 = (*((QuantizedColorType **) Entry2));
  367. +
  368. + /* sort on all axes of the color space! */
  369. + int hash1 = entry1->RGB[SortRGBAxis] * 256 * 256
  370. + + entry1->RGB[(SortRGBAxis+1) % 3] * 256
  371. + + entry1->RGB[(SortRGBAxis+2) % 3];
  372. + int hash2 = entry2->RGB[SortRGBAxis] * 256 * 256
  373. + + entry2->RGB[(SortRGBAxis+1) % 3] * 256
  374. + + entry2->RGB[(SortRGBAxis+2) % 3];
  375. +
  376. + return hash1 - hash2;
  377. +}
  378. +
  379. +/* end */