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
31using std::nothrow;
32
33extern bool debug;
34
35
36// "web safe palette"
37const 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
169struct 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
188SavePalette::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((void*)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((void*)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
218static int
219make_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
229static bool
230touch_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
254SavePalette::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
384SavePalette::~SavePalette()
385{
386	delete[] pal;
387}
388
389
390uint8
391SavePalette::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
450void
451SavePalette::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
478void
479SavePalette::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
519void
520SavePalette::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