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