xref: /haiku/src/add-ons/translators/gif/SavePalette.cpp (revision d06cbe081b7ea043aea2012359744091de6d604d)
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 //	File: SavePalette.cpp
4 //
5 //	Date: December 1999
6 //
7 //	Author: Daniel Switkin
8 //
9 //	Copyright 2003 (c) by Daniel Switkin. This file is made publically available
10 //	under the BSD license, with the stipulations that this complete header must
11 //	remain at the top of the file indefinitely, and credit must be given to the
12 //	original author in any about box using this software.
13 //
14 ////////////////////////////////////////////////////////////////////////////////
15 
16 // Additional authors:	Stephan Aßmus, <superstippi@gmx.de>
17 //						John Scipione, <jscipione@gmail.com>
18 
19 
20 #include "SavePalette.h"
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include <new>
27 
28 #include <Bitmap.h>
29 
30 
31 using std::nothrow;
32 
33 extern bool debug;
34 
35 
36 // "web safe palette"
37 const rgb_color wsp[256] = {
38 	{0xff, 0xff, 0xff, 0xff}, {0xff, 0xff, 0xcc, 0xff},
39 	{0xff, 0xff, 0x99, 0xff}, {0xff, 0xff, 0x66, 0xff},
40 	{0xff, 0xff, 0x33, 0xff}, {0xff, 0xff, 0x00, 0xff},
41 	{0xff, 0xcc, 0xff, 0xff}, {0xff, 0xcc, 0xcc, 0xff},
42 	{0xff, 0xcc, 0x99, 0xff}, {0xff, 0xcc, 0x66, 0xff},
43 	{0xff, 0xcc, 0x33, 0xff}, {0xff, 0xcc, 0x00, 0xff},
44 	{0xff, 0x99, 0xff, 0xff}, {0xff, 0x99, 0xcc, 0xff},
45 	{0xff, 0x99, 0x99, 0xff}, {0xff, 0x99, 0x66, 0xff},
46 	{0xff, 0x99, 0x33, 0xff}, {0xff, 0x99, 0x00, 0xff},
47 	{0xff, 0x66, 0xff, 0xff}, {0xff, 0x66, 0xcc, 0xff},
48 	{0xff, 0x66, 0x99, 0xff}, {0xff, 0x66, 0x66, 0xff},
49 	{0xff, 0x66, 0x33, 0xff}, {0xff, 0x66, 0x00, 0xff},
50 	{0xff, 0x33, 0xff, 0xff}, {0xff, 0x33, 0xcc, 0xff},
51 	{0xff, 0x33, 0x99, 0xff}, {0xff, 0x33, 0x66, 0xff},
52 	{0xff, 0x33, 0x33, 0xff}, {0xff, 0x33, 0x00, 0xff},
53 	{0xff, 0x00, 0xff, 0xff}, {0xff, 0x00, 0xcc, 0xff},
54 	{0xff, 0x00, 0x99, 0xff}, {0xff, 0x00, 0x66, 0xff},
55 	{0xff, 0x00, 0x33, 0xff}, {0xff, 0x00, 0x00, 0xff},
56 	{0xcc, 0xff, 0xff, 0xff}, {0xcc, 0xff, 0xcc, 0xff},
57 	{0xcc, 0xff, 0x99, 0xff}, {0xcc, 0xff, 0x66, 0xff},
58 	{0xcc, 0xff, 0x33, 0xff}, {0xcc, 0xff, 0x00, 0xff},
59 	{0xcc, 0xcc, 0xff, 0xff}, {0xcc, 0xcc, 0xcc, 0xff},
60 	{0xcc, 0xcc, 0x99, 0xff}, {0xcc, 0xcc, 0x66, 0xff},
61 	{0xcc, 0xcc, 0x33, 0xff}, {0xcc, 0xcc, 0x00, 0xff},
62 	{0xcc, 0x99, 0xff, 0xff}, {0xcc, 0x99, 0xcc, 0xff},
63 	{0xcc, 0x99, 0x99, 0xff}, {0xcc, 0x99, 0x66, 0xff},
64 	{0xcc, 0x99, 0x33, 0xff}, {0xcc, 0x99, 0x00, 0xff},
65 	{0xcc, 0x66, 0xff, 0xff}, {0xcc, 0x66, 0xcc, 0xff},
66 	{0xcc, 0x66, 0x99, 0xff}, {0xcc, 0x66, 0x66, 0xff},
67 	{0xcc, 0x66, 0x33, 0xff}, {0xcc, 0x66, 0x00, 0xff},
68 	{0xcc, 0x33, 0xff, 0xff}, {0xcc, 0x33, 0xcc, 0xff},
69 	{0xcc, 0x33, 0x99, 0xff}, {0xcc, 0x33, 0x66, 0xff},
70 	{0xcc, 0x33, 0x33, 0xff}, {0xcc, 0x33, 0x00, 0xff},
71 	{0xcc, 0x00, 0xff, 0xff}, {0xcc, 0x00, 0xcc, 0xff},
72 	{0xcc, 0x00, 0x99, 0xff}, {0xcc, 0x00, 0x66, 0xff},
73 	{0xcc, 0x00, 0x33, 0xff}, {0xcc, 0x00, 0x00, 0xff},
74 	{0x99, 0xff, 0xff, 0xff}, {0x99, 0xff, 0xcc, 0xff},
75 	{0x99, 0xff, 0x99, 0xff}, {0x99, 0xff, 0x66, 0xff},
76 	{0x99, 0xff, 0x33, 0xff}, {0x99, 0xff, 0x00, 0xff},
77 	{0x99, 0xcc, 0xff, 0xff}, {0x99, 0xcc, 0xcc, 0xff},
78 	{0x99, 0xcc, 0x99, 0xff}, {0x99, 0xcc, 0x66, 0xff},
79 	{0x99, 0xcc, 0x33, 0xff}, {0x99, 0xcc, 0x00, 0xff},
80 	{0x99, 0x99, 0xff, 0xff}, {0x99, 0x99, 0xcc, 0xff},
81 	{0x99, 0x99, 0x99, 0xff}, {0x99, 0x99, 0x66, 0xff},
82 	{0x99, 0x99, 0x33, 0xff}, {0x99, 0x99, 0x00, 0xff},
83 	{0x99, 0x66, 0xff, 0xff}, {0x99, 0x66, 0xcc, 0xff},
84 	{0x99, 0x66, 0x99, 0xff}, {0x99, 0x66, 0x66, 0xff},
85 	{0x99, 0x66, 0x33, 0xff}, {0x99, 0x66, 0x00, 0xff},
86 	{0x99, 0x33, 0xff, 0xff}, {0x99, 0x33, 0xcc, 0xff},
87 	{0x99, 0x33, 0x99, 0xff}, {0x99, 0x33, 0x66, 0xff},
88 	{0x99, 0x33, 0x33, 0xff}, {0x99, 0x33, 0x00, 0xff},
89 	{0x99, 0x00, 0xff, 0xff}, {0x99, 0x00, 0xcc, 0xff},
90 	{0x99, 0x00, 0x99, 0xff}, {0x99, 0x00, 0x66, 0xff},
91 	{0x99, 0x00, 0x33, 0xff}, {0x99, 0x00, 0x00, 0xff},
92 	{0x66, 0xff, 0xff, 0xff}, {0x66, 0xff, 0xcc, 0xff},
93 	{0x66, 0xff, 0x99, 0xff}, {0x66, 0xff, 0x66, 0xff},
94 	{0x66, 0xff, 0x33, 0xff}, {0x66, 0xff, 0x00, 0xff},
95 	{0x66, 0xcc, 0xff, 0xff}, {0x66, 0xcc, 0xcc, 0xff},
96 	{0x66, 0xcc, 0x99, 0xff}, {0x66, 0xcc, 0x66, 0xff},
97 	{0x66, 0xcc, 0x33, 0xff}, {0x66, 0xcc, 0x00, 0xff},
98 	{0x66, 0x99, 0xff, 0xff}, {0x66, 0x99, 0xcc, 0xff},
99 	{0x66, 0x99, 0x99, 0xff}, {0x66, 0x99, 0x66, 0xff},
100 	{0x66, 0x99, 0x33, 0xff}, {0x66, 0x99, 0x00, 0xff},
101 	{0x66, 0x66, 0xff, 0xff}, {0x66, 0x66, 0xcc, 0xff},
102 	{0x66, 0x66, 0x99, 0xff}, {0x66, 0x66, 0x66, 0xff},
103 	{0x66, 0x66, 0x33, 0xff}, {0x66, 0x66, 0x00, 0xff},
104 	{0x66, 0x33, 0xff, 0xff}, {0x66, 0x33, 0xcc, 0xff},
105 	{0x66, 0x33, 0x99, 0xff}, {0x66, 0x33, 0x66, 0xff},
106 	{0x66, 0x33, 0x33, 0xff}, {0x66, 0x33, 0x00, 0xff},
107 	{0x66, 0x00, 0xff, 0xff}, {0x66, 0x00, 0xcc, 0xff},
108 	{0x66, 0x00, 0x99, 0xff}, {0x66, 0x00, 0x66, 0xff},
109 	{0x66, 0x00, 0x33, 0xff}, {0x66, 0x00, 0x00, 0xff},
110 	{0x33, 0xff, 0xff, 0xff}, {0x33, 0xff, 0xcc, 0xff},
111 	{0x33, 0xff, 0x99, 0xff}, {0x33, 0xff, 0x66, 0xff},
112 	{0x33, 0xff, 0x33, 0xff}, {0x33, 0xff, 0x00, 0xff},
113 	{0x33, 0xcc, 0xff, 0xff}, {0x33, 0xcc, 0xcc, 0xff},
114 	{0x33, 0xcc, 0x99, 0xff}, {0x33, 0xcc, 0x66, 0xff},
115 	{0x33, 0xcc, 0x33, 0xff}, {0x33, 0xcc, 0x00, 0xff},
116 	{0x33, 0x99, 0xff, 0xff}, {0x33, 0x99, 0xcc, 0xff},
117 	{0x33, 0x99, 0x99, 0xff}, {0x33, 0x99, 0x66, 0xff},
118 	{0x33, 0x99, 0x33, 0xff}, {0x33, 0x99, 0x00, 0xff},
119 	{0x33, 0x66, 0xff, 0xff}, {0x33, 0x66, 0xcc, 0xff},
120 	{0x33, 0x66, 0x99, 0xff}, {0x33, 0x66, 0x66, 0xff},
121 	{0x33, 0x66, 0x33, 0xff}, {0x33, 0x66, 0x00, 0xff},
122 	{0x33, 0x33, 0xff, 0xff}, {0x33, 0x33, 0xcc, 0xff},
123 	{0x33, 0x33, 0x99, 0xff}, {0x33, 0x33, 0x66, 0xff},
124 	{0x33, 0x33, 0x33, 0xff}, {0x33, 0x33, 0x00, 0xff},
125 	{0x33, 0x00, 0xff, 0xff}, {0x33, 0x00, 0xcc, 0xff},
126 	{0x33, 0x00, 0x99, 0xff}, {0x33, 0x00, 0x66, 0xff},
127 	{0x33, 0x00, 0x33, 0xff}, {0x33, 0x00, 0x00, 0xff},
128 	{0x00, 0xff, 0xff, 0xff}, {0x00, 0xff, 0xcc, 0xff},
129 	{0x00, 0xff, 0x99, 0xff}, {0x00, 0xff, 0x66, 0xff},
130 	{0x00, 0xff, 0x33, 0xff}, {0x00, 0xff, 0x00, 0xff},
131 	{0x00, 0xcc, 0xff, 0xff}, {0x00, 0xcc, 0xcc, 0xff},
132 	{0x00, 0xcc, 0x99, 0xff}, {0x00, 0xcc, 0x66, 0xff},
133 	{0x00, 0xcc, 0x33, 0xff}, {0x00, 0xcc, 0x00, 0xff},
134 	{0x00, 0x99, 0xff, 0xff}, {0x00, 0x99, 0xcc, 0xff},
135 	{0x00, 0x99, 0x99, 0xff}, {0x00, 0x99, 0x66, 0xff},
136 	{0x00, 0x99, 0x33, 0xff}, {0x00, 0x99, 0x00, 0xff},
137 	{0x00, 0x66, 0xff, 0xff}, {0x00, 0x66, 0xcc, 0xff},
138 	{0x00, 0x66, 0x99, 0xff}, {0x00, 0x66, 0x66, 0xff},
139 	{0x00, 0x66, 0x33, 0xff}, {0x00, 0x66, 0x00, 0xff},
140 	{0x00, 0x33, 0xff, 0xff}, {0x00, 0x33, 0xcc, 0xff},
141 	{0x00, 0x33, 0x99, 0xff}, {0x00, 0x33, 0x66, 0xff},
142 	{0x00, 0x33, 0x33, 0xff}, {0x00, 0x33, 0x00, 0xff},
143 	{0x00, 0x00, 0xff, 0xff}, {0x00, 0x00, 0xcc, 0xff},
144 	{0x00, 0x00, 0x99, 0xff}, {0x00, 0x00, 0x66, 0xff},
145 	{0x00, 0x00, 0x33, 0xff}, {0x00, 0x00, 0x00, 0xff},
146 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
147 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
148 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
149 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
150 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
151 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
152 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
153 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
154 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
155 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
156 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
157 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
158 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
159 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
160 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
161 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
162 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
163 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
164 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff},
165 	{0x00, 0x00, 0x00, 0xff}, {0x00, 0x00, 0x00, 0xff}
166 };
167 
168 
169 struct ColorItem : public HashItem {
170 	ColorItem(unsigned int k, unsigned int c,
171 		uint8 r, uint8 g, uint8 b)
172 		:
173 		count(c),
174 		red(r),
175 		green(g),
176 		blue(b)
177 	{
178 		key = k;
179 	}
180 
181 	unsigned int count;
182 	uint8 red;
183 	uint8 green;
184 	uint8 blue;
185 };
186 
187 
188 SavePalette::SavePalette(int mode)
189 	:
190 	pal(new(nothrow) rgb_color[256]),
191 	fSize(0),
192 	fSizeInBits(8),
193 	fMode(mode),
194 	fTransparentMode(NO_TRANSPARENCY),
195 	fTransparentIndex(-1),
196 	fBackgroundIndex(0),
197 	fFatalError(pal == NULL)
198 {
199 	if (IsValid()) {
200 		if (fMode == WEB_SAFE_PALETTE) {
201 			memcpy(pal, wsp, sizeof(rgb_color) * 256);
202 			fSize = 216;
203 		} else if (fMode == BEOS_SYSTEM_PALETTE) {
204 			color_map* map = (color_map*)system_colors();
205 			memcpy(pal, map->color_list, sizeof(rgb_color) * 256);
206 			fSize = 256;
207 		} else if (fMode == GREYSCALE_PALETTE) {
208 			for (int i = 0; i < 256; i++) {
209 				pal[i].red = pal[i].green = pal[i].blue = i;
210 				pal[i].alpha = 0xff;
211 			}
212 			fSize = 256;
213 		}
214 	}
215 }
216 
217 
218 static int
219 make_key(uint8 r, uint8 g, uint8 b, uint8 bits)
220 {
221 	r = r >> (8 - bits);
222 	g = g >> (8 - bits);
223 	b = b >> (8 - bits);
224 
225 	return (r << (bits * 2)) | (g << bits) | b;
226 }
227 
228 
229 static bool
230 touch_color_item(SFHash& hash, unsigned int key, uint8 r, uint8 g, uint8 b)
231 {
232 	ColorItem* ci = (ColorItem*)hash.GetItem(key);
233 	if (ci == NULL) {
234 		ci = new(nothrow) ColorItem(key, 1, r, g, b);
235 		if (ci == NULL) {
236 			if (debug)
237 				printf("Out of memory in touch_color_item()\n");
238 			return false;
239 		}
240 		hash.AddItem((HashItem*)ci);
241 	} else {
242 		ci->count++;
243 #if 0
244 		// use brightest color
245 		ci->red = max_c(ci->red, r);
246 		ci->green = max_c(ci->green, g);
247 		ci->blue = max_c(ci->blue, b);
248 #endif
249 	}
250 	return true;
251 }
252 
253 
254 SavePalette::SavePalette(BBitmap* bitmap, int32 maxSizeInBits)
255 	:
256 	pal(new(nothrow) rgb_color[256]),
257 	fSize(0),
258 	fSizeInBits(0),
259 	fMode(OPTIMAL_PALETTE),
260 	fTransparentMode(NO_TRANSPARENCY),
261 	fTransparentIndex(-1),
262 	fBackgroundIndex(0),
263 	fFatalError(pal == NULL)
264 {
265 	if (!IsValid())
266 		return;
267 
268 	SFHash hash(1 << 16);
269 	if (hash.fatalerror) {
270 		if (debug)
271 			printf("Out of memory in SavePalette(BBitmap*)\n");
272 		fFatalError = true;
273 		return;
274 	}
275 
276 	// reject unsupported color spaces
277 	// TODO: B_CMAP8 and B_GRAY8 should be really easy to support as well!!
278 	color_space cs = bitmap->ColorSpace();
279 	if (cs != B_RGB32 && cs != B_RGBA32 && cs != B_RGB32_BIG
280 		&& cs != B_RGBA32_BIG) {
281 		if (debug) {
282 			printf("Wrong color space given in SavePalette(BBitmap):\n");
283 			printf("%d %d %d %d or 0x%x\n",
284 				(cs & 0xff000000) >> 24, (cs & 0xff0000) >> 16,
285 				(cs & 0xff00) >> 8, cs & 0xff, cs);
286 		}
287 		fFatalError = true;
288 		return;
289 	}
290 
291 	BRect rect = bitmap->Bounds();
292 	uint32 height = rect.IntegerHeight() + 1;
293 	uint32 width = rect.IntegerWidth() + 1;
294 	uint8* bits = (uint8*)bitmap->Bits();
295 	uint32 bpr = bitmap->BytesPerRow();
296 
297 	uint8 r;
298 	uint8 g;
299 	uint8 b;
300 	uint8 useBits = 3 + maxSizeInBits / 2;
301 	if (cs == B_RGB32 || cs == B_RGBA32) {
302 		for (uint32 y = 0; y < height; y++) {
303 			uint8* handle = bits;
304 			for (uint32 x = 0; x < width; x++) {
305 				b = handle[0];
306 				g = handle[1];
307 				r = handle[2];
308 				handle += 4;
309 
310 				uint32 key = make_key(r, g, b, useBits);
311 				if (!touch_color_item(hash, key, r, g, b)) {
312 					if (debug)
313 						printf("Out of memory in SavePalette(BBitmap*)\n");
314 
315 					fFatalError = true;
316 					return;
317 				}
318 			}
319 			bits += bpr;
320 		}
321 	} else if ((cs == B_RGB32_BIG || cs == B_RGBA32_BIG)) {
322 		for (uint32 y = 0; y < height; y++) {
323 			uint8* handle = bits;
324 			for (uint32 x = 0; x < width; x++) {
325 				b = handle[2];
326 				g = handle[1];
327 				r = handle[0];
328 				handle += 4;
329 
330 				uint32 key = make_key(r, g, b, useBits);
331 				if (!touch_color_item(hash, key, r, g, b)) {
332 					if (debug)
333 						printf("Out of memory in SavePalette(BBitmap*)\n");
334 					fFatalError = true;
335 					return;
336 				}
337 			}
338 			bits += bpr;
339 		}
340 	}
341 
342 	int unique_colors = hash.CountItems();
343 	while (((1 << fSizeInBits) < unique_colors)
344 		&& (fSizeInBits < maxSizeInBits)) {
345 		fSizeInBits++;
346 	}
347 	fSize = 1 << fSizeInBits;
348 
349 	ColorItem** topcolors = (ColorItem**)malloc(fSize * sizeof(ColorItem*));
350 	if (topcolors == NULL) {
351 		if (debug) printf("Out of memory in SavePalette(BBitmap*)\n");
352 		fFatalError = true;
353 		return;
354 	}
355 	ColorItem* dummy = new ColorItem(0, 0, 0, 0, 0);
356 	for (int i = 0; i < fSize; i++)
357 		topcolors[i] = dummy;
358 
359 	for (int i = 0; i < unique_colors; i++) {
360 		ColorItem* ci = (ColorItem*)hash.NextItem();
361 		for (int j = 0; j < fSize; j++) {
362 			if (ci->count > topcolors[j]->count) {
363 				for (int k = fSize - 1; k > j; k--) {
364 					topcolors[k] = topcolors[k - 1];
365 				}
366 				topcolors[j] = ci;
367 				break;
368 			}
369 		}
370 	}
371 
372 	for (int i = 0; i < fSize; i++) {
373 		pal[i].red = topcolors[i]->red;
374 		pal[i].green = topcolors[i]->green;
375 		pal[i].blue = topcolors[i]->blue;
376 		pal[i].alpha = 0xff;
377 	}
378 
379 	delete dummy;
380 	free(topcolors);
381 }
382 
383 
384 SavePalette::~SavePalette()
385 {
386 	delete[] pal;
387 }
388 
389 
390 uint8
391 SavePalette::IndexForColor(uint8 red, uint8 green, uint8 blue, uint8 alpha)
392 {
393 	// standard mapping services once a palette is loaded
394 	if (fTransparentMode > NO_TRANSPARENCY && alpha < 128)
395 		return fTransparentIndex;
396 
397 	uint8 index = 0;
398 
399 	if (fMode == GREYSCALE_PALETTE) {
400 		index = (308 * red + 600 * green + 116 * blue) / 1024;
401 		// avoid transparent index
402 		if (fTransparentMode == AUTO_TRANSPARENCY && index == 1
403 			&& fTransparentIndex == 1) {
404 			index = 0;
405 		}
406 	} else {
407 		int closestDistance = 255 * 255 * 3;
408 
409 		if (fTransparentMode == AUTO_TRANSPARENCY) {
410 			for (int i = 0; i < fTransparentIndex && closestDistance != 0;
411 					i++) {
412 				int rd = (int)red - (int)pal[i].red;
413 				int gd = (int)green - (int)pal[i].green;
414 				int bd = (int)blue - (int)pal[i].blue;
415 				int distanceAtIndex = rd * rd + gd * gd + bd * bd;
416 				if (distanceAtIndex < closestDistance) {
417 					closestDistance = distanceAtIndex;
418 					index = i;
419 				}
420 			}
421 			for (int i = fTransparentIndex + 1;
422 					i < fSize && closestDistance != 0; i++) {
423 				int rd = (int)red - (int)pal[i].red;
424 				int gd = (int)green - (int)pal[i].green;
425 				int bd = (int)blue - (int)pal[i].blue;
426 				int distanceAtIndex = rd * rd + gd * gd + bd * bd;
427 				if (distanceAtIndex < closestDistance) {
428 					closestDistance = distanceAtIndex;
429 					index = i;
430 				}
431 			}
432 		} else {
433 			for (int i = 0; i < fSize && closestDistance != 0; i++) {
434 				int rd = (int)red - (int)pal[i].red;
435 				int gd = (int)green - (int)pal[i].green;
436 				int bd = (int)blue - (int)pal[i].blue;
437 				int distanceAtIndex = rd * rd + gd * gd + bd * bd;
438 				if (distanceAtIndex < closestDistance) {
439 					closestDistance = distanceAtIndex;
440 					index = i;
441 				}
442 			}
443 		}
444 	}
445 
446 	return index;
447 }
448 
449 
450 void
451 SavePalette::PrepareForAutoTransparency()
452 {
453 	fTransparentMode = AUTO_TRANSPARENCY;
454 	// TODO: in the SavePalette::SavePalette(BBitmap*),
455 	// we don't use more colors than necessary, however,
456 	// here we take a slot away for transparency, even if
457 	// we might still have used less colors than the user
458 	// wanted as a maximum
459 	// NOTE: the last index
460 	switch (fMode) {
461 		case WEB_SAFE_PALETTE:
462 			fTransparentIndex = 216;
463 			fSize = 217;
464 			break;
465 		case BEOS_SYSTEM_PALETTE:
466 			fTransparentIndex = 0;
467 			break;
468 		case GREYSCALE_PALETTE:
469 			fTransparentIndex = 1;
470 			break;
471 		case OPTIMAL_PALETTE:
472 			fTransparentIndex = fSize - 1;
473 			break;
474 	}
475 }
476 
477 
478 void
479 SavePalette::SetTransparentColor(uint8 red, uint8 green, uint8 blue)
480 {
481 	fTransparentMode = COLOR_KEY_TRANSPARENCY;
482 
483 	bool found = false;
484 	// try direct hit first
485 	for (int i = 0; i < fSize; i++) {
486 		if (pal[i].red == red &&
487 			pal[i].green == green &&
488 			pal[i].blue == blue) {
489 
490 			fTransparentIndex = i;
491 			found = true;
492 
493 			break;
494 		}
495 	}
496 	if (!found) {
497 		// find closest match
498 		fTransparentIndex = IndexForColor(red, green, blue);
499 		// NOTE: This is a tough decision:
500 		// -> the exact color might be contained within the image
501 		//    but have slipped through the net and is now not in the
502 		//    palette, the user still wants those pixels to be
503 		//    transparent of course, so it is best to match up a
504 		//    color from the palette
505 		// -> on the other hand, the setting might still be there
506 		//    from some previous image and the color might not
507 		//    even appear in the current image at all... but I guess
508 		//    handling it like below is the lesser evil.
509 		// match up color at index to provided transparent color,
510 		// to make sure it actually works
511 		pal[fTransparentIndex].red = red;
512 		pal[fTransparentIndex].green = green;
513 		pal[fTransparentIndex].blue = blue;
514 		found = true;
515 	}
516 }
517 
518 
519 void
520 SavePalette::GetColors(uint8* buffer, int size) const
521 {
522 	int maxIndex = max_c(size / 3, fSize) - 1;
523 	for (int i = 0; i <= maxIndex; i++) {
524 		*buffer++ = pal[i].red;
525 		*buffer++ = pal[i].green;
526 		*buffer++ = pal[i].blue;
527 	}
528 	int rest = (maxIndex + 1) * 3;
529 	if (rest < size)
530 		memset(buffer, 0, size - rest);
531 }
532