1 ////////////////////////////////////////////////////////////////////////////////
3 // File: SavePalette.cpp
7 // Author: Daniel Switkin
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.
14 ////////////////////////////////////////////////////////////////////////////////
16 // Additional authors: Stephan Aßmus, <superstippi@gmx.de>
17 // John Scipione, <jscipione@gmail.com>
20 #include "SavePalette.h"
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}
169 struct ColorItem
: public HashItem
{
170 ColorItem(unsigned int k
, unsigned int c
,
171 uint8 r
, uint8 g
, uint8 b
)
188 SavePalette::SavePalette(int mode
)
190 pal(new(nothrow
) rgb_color
[256]),
194 fTransparentMode(NO_TRANSPARENCY
),
195 fTransparentIndex(-1),
197 fFatalError(pal
== NULL
)
200 if (fMode
== WEB_SAFE_PALETTE
) {
201 memcpy(pal
, wsp
, sizeof(rgb_color
) * 256);
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);
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
;
219 make_key(uint8 r
, uint8 g
, uint8 b
, uint8 bits
)
225 return (r
<< (bits
* 2)) | (g
<< bits
) | b
;
230 touch_color_item(SFHash
& hash
, unsigned int key
, uint8 r
, uint8 g
, uint8 b
)
232 ColorItem
* ci
= (ColorItem
*)hash
.GetItem(key
);
234 ci
= new(nothrow
) ColorItem(key
, 1, r
, g
, b
);
237 printf("Out of memory in touch_color_item()\n");
240 hash
.AddItem((HashItem
*)ci
);
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
);
254 SavePalette::SavePalette(BBitmap
* bitmap
, int32 maxSizeInBits
)
256 pal(new(nothrow
) rgb_color
[256]),
259 fMode(OPTIMAL_PALETTE
),
260 fTransparentMode(NO_TRANSPARENCY
),
261 fTransparentIndex(-1),
263 fFatalError(pal
== NULL
)
268 SFHash
hash(1 << 16);
269 if (hash
.fatalerror
) {
271 printf("Out of memory in SavePalette(BBitmap*)\n");
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
) {
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
);
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();
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
++) {
310 uint32 key
= make_key(r
, g
, b
, useBits
);
311 if (!touch_color_item(hash
, key
, r
, g
, b
)) {
313 printf("Out of memory in SavePalette(BBitmap*)\n");
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
++) {
330 uint32 key
= make_key(r
, g
, b
, useBits
);
331 if (!touch_color_item(hash
, key
, r
, g
, b
)) {
333 printf("Out of memory in SavePalette(BBitmap*)\n");
342 int unique_colors
= hash
.CountItems();
343 while (((1 << fSizeInBits
) < unique_colors
)
344 && (fSizeInBits
< maxSizeInBits
)) {
347 fSize
= 1 << fSizeInBits
;
349 ColorItem
** topcolors
= (ColorItem
**)malloc(fSize
* sizeof(ColorItem
*));
350 if (topcolors
== NULL
) {
351 if (debug
) printf("Out of memory in SavePalette(BBitmap*)\n");
355 ColorItem
* dummy
= new ColorItem(0, 0, 0, 0, 0);
356 for (int i
= 0; i
< fSize
; i
++)
357 topcolors
[i
] = dummy
;
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];
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
;
384 SavePalette::~SavePalette()
391 SavePalette::IndexForColor(uint8 red
, uint8 green
, uint8 blue
, uint8 alpha
)
393 // standard mapping services once a palette is loaded
394 if (fTransparentMode
> NO_TRANSPARENCY
&& alpha
< 128)
395 return fTransparentIndex
;
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) {
407 int closestDistance
= 255 * 255 * 3;
409 if (fTransparentMode
== AUTO_TRANSPARENCY
) {
410 for (int i
= 0; i
< fTransparentIndex
&& closestDistance
!= 0;
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
;
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
;
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
;
451 SavePalette::PrepareForAutoTransparency()
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
461 case WEB_SAFE_PALETTE
:
462 fTransparentIndex
= 216;
465 case BEOS_SYSTEM_PALETTE
:
466 fTransparentIndex
= 0;
468 case GREYSCALE_PALETTE
:
469 fTransparentIndex
= 1;
471 case OPTIMAL_PALETTE
:
472 fTransparentIndex
= fSize
- 1;
479 SavePalette::SetTransparentColor(uint8 red
, uint8 green
, uint8 blue
)
481 fTransparentMode
= COLOR_KEY_TRANSPARENCY
;
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
) {
490 fTransparentIndex
= i
;
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
;
520 SavePalette::GetColors(uint8
* buffer
, int size
) const
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
;
528 int rest
= (maxIndex
+ 1) * 3;
530 memset(buffer
, 0, size
- rest
);