summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util/palette.c
blob: e659f152830cb586c77de89d1cf280cbe0656216 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
/******************************************************************************

    palette.c

    Palette handling functions.

    Copyright Nicola Salmoria and the MAME Team.
    Visit http://mamedev.org for licensing and usage restrictions.

******************************************************************************/

#include "palette.h"
#include <math.h>



/***************************************************************************
    TYPE DEFINITIONS
***************************************************************************/

/* object to track dirty states */
typedef struct _dirty_state dirty_state;
struct _dirty_state
{
	UINT32 *		dirty;						/* bitmap of dirty entries */
	UINT32			mindirty;					/* minimum dirty entry */
	UINT32			maxdirty;					/* minimum dirty entry */
};


/* a single palette client */
struct _palette_client
{
	palette_client *next;						/* pointer to next client */
	palette_t *		palette;					/* reference to the palette */
	dirty_state		live;						/* live dirty state */
	dirty_state		previous;					/* previous dirty state */
};


/* a palette object */
struct _palette_t
{
	UINT32			refcount;					/* reference count on the palette */
	UINT32			numcolors;					/* number of colors in the palette */
	UINT32			numgroups;					/* number of groups in the palette */

	float			brightness;					/* overall brightness value */
	float			contrast;					/* overall contrast value */
	float			gamma;						/* overall gamma value */
	UINT8			gamma_map[256];				/* gamma map */

	rgb_t *			entry_color;				/* array of raw colors */
	float *			entry_contrast;				/* contrast value for each entry */
	rgb_t *			adjusted_color;				/* array of adjusted colors */
	rgb_t *			adjusted_rgb15;				/* array of adjusted colors as RGB15 */

	float *			group_bright;				/* brightness value for each group */
	float *			group_contrast;				/* contrast value for each group */

	palette_client *client_list;				/* list of clients for this palette */
};



/***************************************************************************
    FUNCTION PROTOTYPES
***************************************************************************/

static void internal_palette_free(palette_t *palette);
static void update_adjusted_color(palette_t *palette, UINT32 group, UINT32 index);



/***************************************************************************
    INLINE FUNCTIONS
***************************************************************************/

/*-------------------------------------------------
    adjust_palette_entry - adjust a palette
    entry for brightness
-------------------------------------------------*/

INLINE rgb_t adjust_palette_entry(rgb_t entry, float brightness, float contrast, const UINT8 *gamma_map)
{
	int r = rgb_clamp((float)gamma_map[RGB_RED(entry)] * contrast + brightness);
	int g = rgb_clamp((float)gamma_map[RGB_GREEN(entry)] * contrast + brightness);
	int b = rgb_clamp((float)gamma_map[RGB_BLUE(entry)] * contrast + brightness);
	int a = RGB_ALPHA(entry);
	return MAKE_ARGB(a,r,g,b);
}



/***************************************************************************
    PALETTE ALLOCATION
***************************************************************************/

/*-------------------------------------------------
    palette_alloc - allocate a new palette object
    and take a single reference on it
-------------------------------------------------*/

palette_t *palette_alloc(UINT32 numcolors, UINT32 numgroups)
{
	palette_t *palette;
	UINT32 index;

	/* allocate memory */
	palette = malloc(sizeof(*palette));
	if (palette == NULL)
		goto error;
	memset(palette, 0, sizeof(*palette));

	/* initialize overall controls */
	palette->brightness = 0.0f;
	palette->contrast = 1.0f;
	palette->gamma = 1.0f;
	for (index = 0; index < 256; index++)
		palette->gamma_map[index] = index;

	/* allocate an array of palette entries and individual contrasts for each */
	palette->entry_color = malloc(sizeof(*palette->entry_color) * numcolors);
	palette->entry_contrast = malloc(sizeof(*palette->entry_contrast) * numcolors);
	if (palette->entry_color == NULL || palette->entry_contrast == NULL)
		goto error;

	/* initialize the entries */
	for (index = 0; index < numcolors; index++)
	{
		palette->entry_color[index] = RGB_BLACK;
		palette->entry_contrast[index] = 1.0f;
	}

	/* allocate an array of brightness and contrast for each group */
	palette->group_bright = malloc(sizeof(*palette->group_bright) * numgroups);
	palette->group_contrast = malloc(sizeof(*palette->group_contrast) * numgroups);
	if (palette->group_bright == NULL || palette->group_contrast == NULL)
		goto error;

	/* initialize the entries */
	for (index = 0; index < numgroups; index++)
	{
		palette->group_bright[index] = 0.0f;
		palette->group_contrast[index] = 1.0f;
	}

	/* allocate arrays for the adjusted colors */
	palette->adjusted_color = malloc(sizeof(*palette->adjusted_color) * (numcolors * numgroups + 2));
	palette->adjusted_rgb15 = malloc(sizeof(*palette->adjusted_rgb15) * (numcolors * numgroups + 2));
	if (palette->adjusted_color == NULL || palette->adjusted_rgb15 == NULL)
		goto error;

	/* initialize the arrays */
	for (index = 0; index < numcolors * numgroups; index++)
	{
		palette->adjusted_color[index] = RGB_BLACK;
		palette->adjusted_rgb15[index] = rgb_to_rgb15(RGB_BLACK);
	}

	/* add black and white as the last two colors */
	palette->adjusted_color[index] = RGB_BLACK;
	palette->adjusted_rgb15[index++] = rgb_to_rgb15(RGB_BLACK);
	palette->adjusted_color[index] = RGB_WHITE;
	palette->adjusted_rgb15[index++] = rgb_to_rgb15(RGB_WHITE);

	/* initialize the remainder of the structure */
	palette->refcount = 1;
	palette->numcolors = numcolors;
	palette->numgroups = numgroups;
	return palette;

error:
	if (palette != NULL)
		internal_palette_free(palette);

	return NULL;
}


/*-------------------------------------------------
    palette_ref - reference a palette object,
    incrementing its reference count
-------------------------------------------------*/

void palette_ref(palette_t *palette)
{
	palette->refcount++;
}


/*-------------------------------------------------
    palette_deref - dereference a palette object;
    if the reference count goes to 0, it is freed
-------------------------------------------------*/

void palette_deref(palette_t *palette)
{
	/* if the reference count goes to 0, free */
	if (--palette->refcount == 0)
		internal_palette_free(palette);
}



/***************************************************************************
    PALETTE INFORMATION
***************************************************************************/

/*-------------------------------------------------
    palette_get_num_colors - return the number of
    colors allocated in the palette
-------------------------------------------------*/

int palette_get_num_colors(palette_t *palette)
{
	return palette->numcolors;
}


/*-------------------------------------------------
    palette_get_num_groups - return the number of
    groups managed by the palette
-------------------------------------------------*/

int palette_get_num_groups(palette_t *palette)
{
	return palette->numgroups;
}


/*-------------------------------------------------
    palette_get_max_index - return the maximum 
    allowed index (i.e., length of arrays returned 
    by palette_entry_list*)
-------------------------------------------------*/

int palette_get_max_index(palette_t *palette)
{
	return palette->numcolors * palette->numgroups + 2;
}


/*-------------------------------------------------
    palette_get_black_entry - return the index of
    the black entry
-------------------------------------------------*/

UINT32 palette_get_black_entry(palette_t *palette)
{
	return palette->numcolors * palette->numgroups + 0;
}


/*-------------------------------------------------
    palette_get_white_entry - return the index of
    the white entry
-------------------------------------------------*/

UINT32 palette_get_white_entry(palette_t *palette)
{
	return palette->numcolors * palette->numgroups + 1;
}



/***************************************************************************
    PALETTE CLIENTS
***************************************************************************/

/*-------------------------------------------------
    palette_client_alloc - add a new client to a
    palette
-------------------------------------------------*/

palette_client *palette_client_alloc(palette_t *palette)
{
	UINT32 total_colors = palette->numcolors * palette->numgroups;
	UINT32 dirty_dwords = (total_colors + 31) / 32;
	palette_client *client;

	/* allocate memory for the client */
	client = malloc(sizeof(*client));
	if (client == NULL)
		goto error;
	memset(client, 0, sizeof(*client));

	/* allocate dirty lists */
	client->live.dirty = malloc(dirty_dwords * sizeof(UINT32));
	client->previous.dirty = malloc(dirty_dwords * sizeof(UINT32));
	if (client->live.dirty == NULL || client->previous.dirty == NULL)
		goto error;

	/* mark everything dirty to start with */
	memset(client->live.dirty, 0xff, dirty_dwords * sizeof(UINT32));
	memset(client->previous.dirty, 0xff, dirty_dwords * sizeof(UINT32));
	client->live.dirty[dirty_dwords - 1] &= (1 << (total_colors % 32)) - 1;
	client->previous.dirty[dirty_dwords - 1] &= (1 << (total_colors % 32)) - 1;

	/* initialize the rest of the structure and add a reference to a palette */
	client->palette = palette;
	palette_ref(palette);
	client->live.mindirty = 0;
	client->live.maxdirty = total_colors - 1;

	/* now add us to the list of clients */
	client->next = palette->client_list;
	palette->client_list = client;
	return client;

error:
	if (client != NULL)
	{
		if (client->live.dirty != NULL)
			free(client->live.dirty);
		if (client->previous.dirty != NULL)
			free(client->previous.dirty);
		free(client);
	}
	return NULL;
}


/*-------------------------------------------------
    palette_client_free - remove a client from a
    palette
-------------------------------------------------*/

void palette_client_free(palette_client *client)
{
	palette_client **curptr;

	/* first locate and remove ourself from the palette's list */
	for (curptr = &client->palette->client_list; *curptr != NULL; curptr = &(*curptr)->next)
		if (*curptr == client)
		{
			*curptr = client->next;
			break;
		}

	/* now deref the palette */
	palette_deref(client->palette);

	/* free our data */
	if (client->live.dirty != NULL)
		free(client->live.dirty);
	if (client->previous.dirty != NULL)
		free(client->previous.dirty);
	free(client);
}


/*-------------------------------------------------
    palette_client_get_palette - return a pointer
    to the palette for this client
-------------------------------------------------*/

palette_t *palette_client_get_palette(palette_client *client)
{
	return client->palette;
}


/*-------------------------------------------------
    palette_client_get_dirty_list - atomically get
    the current dirty list for a client
-------------------------------------------------*/

const UINT32 *palette_client_get_dirty_list(palette_client *client, UINT32 *mindirty, UINT32 *maxdirty)
{
	dirty_state temp;

	/* fill in the mindirty/maxdirty */
	if (mindirty != NULL)
		*mindirty = client->live.mindirty;
	if (maxdirty != NULL)
		*maxdirty = client->live.maxdirty;

	/* if nothing to report, report nothing and don't swap */
	if (client->live.mindirty > client->live.maxdirty)
		return NULL;

	/* swap the live and previous lists */
	temp = client->live;
	client->live = client->previous;
	client->previous = temp;

	/* erase relevant entries in the new live one */
	if (client->live.mindirty <= client->live.maxdirty)
		memset(client->live.dirty, client->live.mindirty / 8, (client->live.maxdirty / 8) + 1 - (client->live.mindirty / 8));
	client->live.mindirty = client->palette->numcolors * client->palette->numgroups;
	client->live.maxdirty = 0;

	/* return a pointer to the previous table */
	return client->previous.dirty;
}



/***************************************************************************
    PALETTE COLOR MANAGEMENT
***************************************************************************/

/*-------------------------------------------------
    palette_entry_set_color - set the raw RGB
    color for a given palette index
-------------------------------------------------*/

void palette_entry_set_color(palette_t *palette, UINT32 index, rgb_t rgb)
{
	int groupnum;

	/* if out of range, or unchanged, ignore */
	if (index >= palette->numcolors || palette->entry_color[index] == rgb)
		return;

	/* set the color */
	palette->entry_color[index] = rgb;

	/* update across all groups */
	for (groupnum = 0; groupnum < palette->numgroups; groupnum++)
		update_adjusted_color(palette, groupnum, index);
}


/*-------------------------------------------------
    palette_entry_get_color - return the raw RGB
    color for a given palette index
-------------------------------------------------*/

rgb_t palette_entry_get_color(palette_t *palette, UINT32 index)
{
	return (index < palette->numcolors) ? palette->entry_color[index] : RGB_BLACK;
}


/*-------------------------------------------------
    palette_entry_get_adjusted_color - return the
    adjusted RGB color (after all adjustments) for
    a given palette index
-------------------------------------------------*/

rgb_t palette_entry_get_adjusted_color(palette_t *palette, UINT32 index)
{
	return (index < palette->numcolors * palette->numgroups) ? palette->adjusted_color[index] : RGB_BLACK;
}


/*-------------------------------------------------
    palette_entry_list_raw - return the entire
    palette as an array of raw RGB values
-------------------------------------------------*/

const rgb_t *palette_entry_list_raw(palette_t *palette)
{
	return palette->entry_color;
}


/*-------------------------------------------------
    palette_entry_list_adjusted - return the
    entire palette as an array of adjusted RGB
    values
-------------------------------------------------*/

const rgb_t *palette_entry_list_adjusted(palette_t *palette)
{
	return palette->adjusted_color;
}


/*-------------------------------------------------
    palette_entry_list_adjusted_rgb15 - return the
    entire palette as an array of adjusted RGB-15
    values
-------------------------------------------------*/

const rgb_t *palette_entry_list_adjusted_rgb15(palette_t *palette)
{
	return palette->adjusted_rgb15;
}



/***************************************************************************
    PALETTE ADJUSTMENTS
***************************************************************************/

/*-------------------------------------------------
    palette_set_brightness - set the overall
    brightness for the palette
-------------------------------------------------*/

void palette_set_brightness(palette_t *palette, float brightness)
{
	int groupnum, index;

	/* convert incoming value to normalized result */
	brightness = (brightness - 1.0f) * 256.0f;

	/* set the global brightness if changed */
	if (palette->brightness == brightness)
		return;
	palette->brightness = brightness;

	/* update across all indices in all groups */
	for (groupnum = 0; groupnum < palette->numgroups; groupnum++)
		for (index = 0; index < palette->numcolors; index++)
			update_adjusted_color(palette, groupnum, index);
}


/*-------------------------------------------------
    palette_set_contrast - set the overall
    contrast for the palette
-------------------------------------------------*/

void palette_set_contrast(palette_t *palette, float contrast)
{
	int groupnum, index;

	/* set the global contrast if changed */
	if (palette->contrast == contrast)
		return;
	palette->contrast = contrast;

	/* update across all indices in all groups */
	for (groupnum = 0; groupnum < palette->numgroups; groupnum++)
		for (index = 0; index < palette->numcolors; index++)
			update_adjusted_color(palette, groupnum, index);
}


/*-------------------------------------------------
    palette_set_gamma - set the overall
    gamma for the palette
-------------------------------------------------*/

void palette_set_gamma(palette_t *palette, float gamma)
{
	int groupnum, index;

	/* set the global gamma if changed */
	if (palette->gamma == gamma)
		return;
	palette->gamma = gamma;

	/* recompute the gamma map */
	gamma = 1.0f / gamma;
	for (index = 0; index < 256; index++)
	{
		float fval = (float)index * (1.0f / 255.0f);
		float fresult = pow(fval, gamma);
		palette->gamma_map[index] = rgb_clamp(255.0f * fresult);
	}

	/* update across all indices in all groups */
	for (groupnum = 0; groupnum < palette->numgroups; groupnum++)
		for (index = 0; index < palette->numcolors; index++)
			update_adjusted_color(palette, groupnum, index);
}


/*-------------------------------------------------
    palette_entry_set_contrast - set the contrast
    adjustment for a single palette index
-------------------------------------------------*/

void palette_entry_set_contrast(palette_t *palette, UINT32 index, float contrast)
{
	int groupnum;

	/* if out of range, or unchanged, ignore */
	if (index >= palette->numcolors || palette->entry_contrast[index] == contrast)
		return;

	/* set the contrast */
	palette->entry_contrast[index] = contrast;

	/* update across all groups */
	for (groupnum = 0; groupnum < palette->numgroups; groupnum++)
		update_adjusted_color(palette, groupnum, index);
}


/*-------------------------------------------------
    palette_entry_get_contrast - return the
    contrast adjustment for a single palette index
-------------------------------------------------*/

float palette_entry_get_contrast(palette_t *palette, UINT32 index)
{
	return (index < palette->numcolors) ? palette->entry_contrast[index] : 1.0f;
}


/*-------------------------------------------------
    palette_group_set_brightness - configure
    overall brightness for a palette group
-------------------------------------------------*/

void palette_group_set_brightness(palette_t *palette, UINT32 group, float brightness)
{
	int index;

	/* convert incoming value to normalized result */
	brightness = (brightness - 1.0f) * 256.0f;

	/* if out of range, or unchanged, ignore */
	if (group >= palette->numgroups || palette->group_bright[group] == brightness)
		return;

	/* set the contrast */
	palette->group_bright[group] = brightness;

	/* update across all colors */
	for (index = 0; index < palette->numcolors; index++)
		update_adjusted_color(palette, group, index);
}


/*-------------------------------------------------
    palette_group_set_contrast - configure
    overall contrast for a palette group
-------------------------------------------------*/

void palette_group_set_contrast(palette_t *palette, UINT32 group, float contrast)
{
	int index;

	/* if out of range, or unchanged, ignore */
	if (group >= palette->numgroups || palette->group_contrast[group] == contrast)
		return;

	/* set the contrast */
	palette->group_contrast[group] = contrast;

	/* update across all colors */
	for (index = 0; index < palette->numcolors; index++)
		update_adjusted_color(palette, group, index);
}



/***************************************************************************
    PALETTE UTILITIES
***************************************************************************/

/*-------------------------------------------------
    palette_normalize_range - normalize a range
    of palette entries
-------------------------------------------------*/

void palette_normalize_range(palette_t *palette, UINT32 start, UINT32 end, int lum_min, int lum_max)
{
	UINT32 ymin = 1000 * 255, ymax = 0;
	UINT32 tmin, tmax;
	UINT32 index;

	/* clamp within range */
	start = MAX(start, 0);
	end = MIN(end, palette->numcolors - 1);

	/* find the minimum and maximum brightness of all the colors in the range */
	for (index = start; index <= end; index++)
	{
		rgb_t rgb = palette->entry_color[index];
		UINT32 y = 299 * RGB_RED(rgb) + 587 * RGB_GREEN(rgb) + 114 * RGB_BLUE(rgb);
		ymin = MIN(ymin, y);
		ymax = MAX(ymax, y);
	}

	/* determine target minimum/maximum */
	tmin = (lum_min < 0) ? ((ymin + 500) / 1000) : lum_min;
	tmax = (lum_max < 0) ? ((ymax + 500) / 1000) : lum_max;

	/* now normalize the palette */
	for (index = start; index <= end; index++)
	{
		rgb_t rgb = palette->entry_color[index];
		UINT32 y = 299 * RGB_RED(rgb) + 587 * RGB_GREEN(rgb) + 114 * RGB_BLUE(rgb);
		UINT32 target = tmin + ((y - ymin) * (tmax - tmin + 1)) / (ymax - ymin);
		UINT8 r = (y == 0) ? 0 : rgb_clamp(RGB_RED(rgb) * 1000 * target / y);
		UINT8 g = (y == 0) ? 0 : rgb_clamp(RGB_GREEN(rgb) * 1000 * target / y);
		UINT8 b = (y == 0) ? 0 : rgb_clamp(RGB_BLUE(rgb) * 1000 * target / y);
		palette_entry_set_color(palette, index, MAKE_RGB(r, g, b));
	}
}



/***************************************************************************
    INTERNAL ROUTINES
***************************************************************************/

/*-------------------------------------------------
    internal_palette_free - free all allocations
    from a palette
-------------------------------------------------*/

static void internal_palette_free(palette_t *palette)
{
	/* free per-color data */
	if (palette->entry_color != NULL)
		free(palette->entry_color);
	if (palette->entry_contrast != NULL)
		free(palette->entry_contrast);

	/* free per-group data */
	if (palette->group_bright != NULL)
		free(palette->group_bright);
	if (palette->group_contrast != NULL)
		free(palette->group_contrast);

	/* free adjusted data */
	if (palette->adjusted_color != NULL)
		free(palette->adjusted_color);
	if (palette->adjusted_rgb15 != NULL)
		free(palette->adjusted_rgb15);

	/* and the palette itself */
	free(palette);
}


/*-------------------------------------------------
    update_adjusted_color - update a color index
    by group and index pair
-------------------------------------------------*/

static void update_adjusted_color(palette_t *palette, UINT32 group, UINT32 index)
{
	UINT32 finalindex = group * palette->numcolors + index;
	palette_client *client;
	rgb_t adjusted;

	/* compute the adjusted value */
	adjusted = adjust_palette_entry(palette->entry_color[index],
				palette->group_bright[group] + palette->brightness,
				palette->group_contrast[group] * palette->entry_contrast[index] * palette->contrast,
				palette->gamma_map);

	/* if not different, ignore */
	if (palette->adjusted_color[finalindex] == adjusted)
		return;

	/* otherwise, modify the adjusted color array */
	palette->adjusted_color[finalindex] = adjusted;
	palette->adjusted_rgb15[finalindex] = rgb_to_rgb15(adjusted);

	/* mark dirty in all clients */
	for (client = palette->client_list; client != NULL; client = client->next)
	{
		client->live.dirty[finalindex / 32] |= 1 << (finalindex % 32);
		client->live.mindirty = MIN(client->live.mindirty, finalindex);
		client->live.maxdirty = MAX(client->live.maxdirty, finalindex);
	}
}