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
26using std::nothrow;
27
28extern bool debug;
29
30// "web safe palette"
31const 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
162class 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
180SavePalette::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
209static int
210make_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
219static bool
220touch_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
242SavePalette::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
362SavePalette::~SavePalette()
363{
364	delete[] pal;
365}
366
367// IndexForColor
368//
369// standard mapping services once a palette is loaded
370uint8
371SavePalette::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
424void
425SavePalette::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
452void
453SavePalette::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
493void
494SavePalette::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