1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4
5 dipalette.cpp
6
7 Device palette interface.
8
9 ***************************************************************************/
10
11 #include "emu.h"
12 #include "screen.h"
13
14 #define VERBOSE 0
15
16
17 //**************************************************************************
18 // DEVICE INTERFACE MANAGEMENT
19 //**************************************************************************
20
21 //-------------------------------------------------
22 // device_palette_interface - constructor
23 //-------------------------------------------------
24
device_palette_interface(const machine_config & mconfig,device_t & device)25 device_palette_interface::device_palette_interface(const machine_config &mconfig, device_t &device)
26 : device_interface(device, "palette"),
27 m_palette(nullptr),
28 m_pens(nullptr),
29 m_format(BITMAP_FORMAT_RGB32),
30 m_shadow_table(nullptr),
31 m_shadow_group(0),
32 m_hilight_group(0),
33 m_white_pen(0),
34 m_black_pen(0)
35 {
36 }
37
38
39 //-------------------------------------------------
40 // interface_validity_check - validation for a
41 // device after the configuration has been
42 // constructed
43 //-------------------------------------------------
44
interface_validity_check(validity_checker & valid) const45 void device_palette_interface::interface_validity_check(validity_checker &valid) const
46 {
47 // this info must be available before the device has started
48 if (palette_entries() == 0)
49 osd_printf_error("Palette has no entries specified\n");
50 }
51
52
53 //-------------------------------------------------
54 // interface_pre_start - work to be done prior to
55 // actually starting a device
56 //-------------------------------------------------
57
interface_pre_start()58 void device_palette_interface::interface_pre_start()
59 {
60 // allocate the palette
61 u32 numentries = palette_entries();
62 allocate_palette(numentries);
63 allocate_color_tables();
64 allocate_shadow_tables();
65
66 // allocate indirection tables
67 int indirect_colors = palette_indirect_entries();
68 if (indirect_colors > 0)
69 {
70 m_indirect_colors.resize(indirect_colors);
71 for (int color = 0; color < indirect_colors; color++)
72 {
73 // alpha = 0 ensures change is detected the first time set_indirect_color() is called
74 m_indirect_colors[color] = rgb_t::transparent();
75 }
76
77 m_indirect_pens.resize(numentries);
78 for (int pen = 0; pen < numentries; pen++)
79 m_indirect_pens[pen] = pen % indirect_colors;
80 }
81 }
82
83
84 //-------------------------------------------------
85 // interface_post_start - work to be done after
86 // actually starting a device
87 //-------------------------------------------------
88
interface_post_start()89 void device_palette_interface::interface_post_start()
90 {
91 // set up save/restore of the palette
92 m_save_pen.resize(m_palette->num_colors());
93 m_save_contrast.resize(m_palette->num_colors());
94 device().save_item(NAME(m_save_pen));
95 device().save_item(NAME(m_save_contrast));
96
97 // save indirection tables if we have them
98 if (m_indirect_colors.size() > 0)
99 {
100 device().save_item(NAME(m_indirect_colors));
101 device().save_item(NAME(m_indirect_pens));
102 }
103 }
104
105
106 //-------------------------------------------------
107 // interface_pre_save - prepare the save arrays
108 // for saving
109 //-------------------------------------------------
110
interface_pre_save()111 void device_palette_interface::interface_pre_save()
112 {
113 // fill the save arrays with updated pen and brightness information
114 int numcolors = m_palette->num_colors();
115 for (int index = 0; index < numcolors; index++)
116 {
117 m_save_pen[index] = pen_color(index);
118 m_save_contrast[index] = pen_contrast(index);
119 }
120 }
121
122
123 //-------------------------------------------------
124 // interface_post_load - called after restore to
125 // actually update the palette
126 //-------------------------------------------------
127
interface_post_load()128 void device_palette_interface::interface_post_load()
129 {
130 // reset the pen and brightness for each entry
131 int numcolors = m_palette->num_colors();
132 for (int index = 0; index < numcolors; index++)
133 {
134 set_pen_color(index, m_save_pen[index]);
135 set_pen_contrast(index, m_save_contrast[index]);
136 }
137 }
138
139
140 //-------------------------------------------------
141 // interface_post_stop - final cleanup
142 //-------------------------------------------------
143
interface_post_stop()144 void device_palette_interface::interface_post_stop()
145 {
146 // dereference the palette
147 if (m_palette != nullptr)
148 m_palette->deref();
149 }
150
151
152 //**************************************************************************
153 // INDIRECTION (AKA COLORTABLES)
154 //**************************************************************************
155
156 //-------------------------------------------------
157 // set_indirect_color - set an indirect color
158 //-------------------------------------------------
159
set_indirect_color(int index,rgb_t rgb)160 void device_palette_interface::set_indirect_color(int index, rgb_t rgb)
161 {
162 // make sure we are in range
163 assert(index < m_indirect_colors.size());
164
165 // alpha doesn't matter
166 rgb.set_a(255);
167
168 // update if it has changed
169 if (m_indirect_colors[index] != rgb)
170 {
171 m_indirect_colors[index] = rgb;
172
173 // update the palette for any colortable entries that reference it
174 for (u32 pen = 0; pen < m_indirect_pens.size(); pen++)
175 if (m_indirect_pens[pen] == index)
176 m_palette->entry_set_color(pen, rgb);
177 }
178 }
179
180
181 //-------------------------------------------------
182 // set_pen_indirect - set an indirect pen index
183 //-------------------------------------------------
184
set_pen_indirect(pen_t pen,indirect_pen_t index)185 void device_palette_interface::set_pen_indirect(pen_t pen, indirect_pen_t index)
186 {
187 // make sure we are in range
188 assert(pen < entries() && index < indirect_entries());
189
190 m_indirect_pens[pen] = index;
191
192 m_palette->entry_set_color(pen, m_indirect_colors[index]);
193 }
194
195
196 //-------------------------------------------------
197 // transpen_mask - return a mask of pens that
198 // whose indirect values match the given
199 // transcolor
200 //-------------------------------------------------
201
transpen_mask(gfx_element & gfx,u32 color,indirect_pen_t transcolor) const202 u32 device_palette_interface::transpen_mask(gfx_element &gfx, u32 color, indirect_pen_t transcolor) const
203 {
204 u32 entry = gfx.colorbase() + (color % gfx.colors()) * gfx.granularity();
205
206 // make sure we are in range
207 assert(entry < m_indirect_pens.size());
208 assert(gfx.depth() <= 32);
209
210 // either gfx->color_depth entries or as many as we can get up until the end
211 int count = std::min(size_t(gfx.depth()), m_indirect_pens.size() - entry);
212
213 // set a bit anywhere the transcolor matches
214 u32 mask = 0;
215 for (int bit = 0; bit < count; bit++)
216 if (m_indirect_pens[entry++] == transcolor)
217 mask |= 1 << bit;
218
219 // return the final mask
220 return mask;
221 }
222
223
224
225 //**************************************************************************
226 // SHADOW TABLE CONFIGURATION
227 //**************************************************************************
228
229 //-------------------------------------------------
230 // palette_set_shadow_mode(mode)
231 //
232 // mode: 0 = use preset 0 (default shadow)
233 // 1 = use preset 1 (default highlight)
234 // 2 = use preset 2 *
235 // 3 = use preset 3 *
236 //
237 // * Preset 2 & 3 work independently under 32bpp,
238 // supporting up to four different types of
239 // shadows at one time. They mirror preset 1 & 2
240 // in lower depth settings to maintain
241 // compatibility.
242 //
243 //
244 // set_shadow_dRGB32(mode, dr, dg, db, noclip)
245 //
246 // mode: 0 to 3 (which preset to configure)
247 //
248 // dr: -255 to 255 ( red displacement )
249 // dg: -255 to 255 ( green displacement )
250 // db: -255 to 255 ( blue displacement )
251 //
252 // noclip: 0 = resultant RGB clipped at 0x00/0xff
253 // 1 = resultant RGB wraparound 0x00/0xff
254 //
255 //
256 // * Color shadows only work under 32bpp.
257 // This function has no effect in lower color
258 // depths where
259 //
260 // set_shadow_factor() or
261 // set_highlight_factor()
262 //
263 // should be used instead.
264 //
265 // * 32-bit shadows are lossy. Even with zero RGB
266 // displacements the affected area will still look
267 // slightly darkened.
268 //
269 // Drivers should ensure all shadow pens in
270 // gfx_drawmode_table[] are set to DRAWMODE_NONE
271 // when RGB displacements are zero to avoid the
272 // darkening effect.
273 //-------------------------------------------------
274
275 //-------------------------------------------------
276 // set_shadow_dRGB32 - configure delta RGB values
277 // for 1 of 4 shadow tables
278 //-------------------------------------------------
279
set_shadow_dRGB32(int mode,int dr,int dg,int db,bool noclip)280 void device_palette_interface::set_shadow_dRGB32(int mode, int dr, int dg, int db, bool noclip)
281 {
282 shadow_table_data &stable = m_shadow_tables[mode];
283
284 // only applies to RGB direct modes
285 assert(m_format != BITMAP_FORMAT_IND16);
286 assert(stable.base != nullptr);
287
288 // clamp the deltas (why?)
289 if (dr < -0xff) dr = -0xff; else if (dr > 0xff) dr = 0xff;
290 if (dg < -0xff) dg = -0xff; else if (dg > 0xff) dg = 0xff;
291 if (db < -0xff) db = -0xff; else if (db > 0xff) db = 0xff;
292
293 // early exit if nothing changed
294 if (dr == stable.dr && dg == stable.dg && db == stable.db && noclip == stable.noclip)
295 return;
296 stable.dr = dr;
297 stable.dg = dg;
298 stable.db = db;
299 stable.noclip = noclip;
300
301 if (VERBOSE)
302 device().popmessage("shadow %d recalc %d %d %d %02x", mode, dr, dg, db, noclip);
303
304 // regenerate the table
305 for (int i = 0; i < 32768; i++)
306 {
307 int r = pal5bit(i >> 10) + dr;
308 int g = pal5bit(i >> 5) + dg;
309 int b = pal5bit(i >> 0) + db;
310
311 // apply clipping
312 if (!noclip)
313 {
314 r = rgb_t::clamp(r);
315 g = rgb_t::clamp(g);
316 b = rgb_t::clamp(b);
317 }
318 rgb_t final = rgb_t(r, g, b);
319
320 // store either 16 or 32 bit
321 if (m_format == BITMAP_FORMAT_RGB32)
322 stable.base[i] = final;
323 else
324 stable.base[i] = final.as_rgb15();
325 }
326 }
327
328
329 //**************************************************************************
330 // INTERNAL FUNCTIONS
331 //**************************************************************************
332
333 //-------------------------------------------------
334 // allocate_palette - allocate and configure the
335 // palette object itself
336 //-------------------------------------------------
337
allocate_palette(u32 numentries)338 void device_palette_interface::allocate_palette(u32 numentries)
339 {
340 assert(numentries > 0);
341
342 // determine the number of groups we need
343 int numgroups = 1;
344 if (palette_shadows_enabled())
345 m_shadow_group = numgroups++;
346 if (palette_hilights_enabled())
347 m_hilight_group = numgroups++;
348 if (numentries * numgroups > 65536)
349 throw emu_fatalerror("%s(%s): Palette has more than 65536 colors.", device().shortname(), device().tag());
350
351 // allocate a palette object containing all the colors and groups
352 m_palette = palette_t::alloc(numentries, numgroups);
353
354 // configure the groups
355 if (m_shadow_group != 0)
356 set_shadow_factor(PALETTE_DEFAULT_SHADOW_FACTOR);
357 if (m_hilight_group != 0)
358 set_highlight_factor(PALETTE_DEFAULT_HIGHLIGHT_FACTOR);
359
360 // set the initial colors to a standard rainbow
361 for (int index = 0; index < numentries; index++)
362 set_pen_color(index, rgbexpand<1,1,1>(index, 0, 1, 2));
363
364 // switch off the color mode
365 switch (m_format)
366 {
367 // 16-bit paletteized case
368 case BITMAP_FORMAT_IND16:
369 m_black_pen = m_palette->black_entry();
370 m_white_pen = m_palette->white_entry();
371 if (m_black_pen >= 65536)
372 m_black_pen = 0;
373 if (m_white_pen >= 65536)
374 m_white_pen = 65535;
375 break;
376
377 // 32-bit direct case
378 case BITMAP_FORMAT_RGB32:
379 m_black_pen = rgb_t::black();
380 m_white_pen = rgb_t::white();
381 break;
382
383 // screenless case
384 case BITMAP_FORMAT_INVALID:
385 default:
386 break;
387 }
388 }
389
390
391 //-------------------------------------------------
392 // allocate_color_tables - allocate memory for
393 // pen and color tables
394 //-------------------------------------------------
395
allocate_color_tables()396 void device_palette_interface::allocate_color_tables()
397 {
398 int total_colors = m_palette->num_colors() * m_palette->num_groups();
399
400 // allocate memory for the pen table
401 switch (m_format)
402 {
403 case BITMAP_FORMAT_IND16:
404 // create a dummy 1:1 mapping
405 {
406 m_pen_array.resize(total_colors + 2);
407 pen_t *pentable = &m_pen_array[0];
408 m_pens = &m_pen_array[0];
409 for (int i = 0; i < total_colors + 2; i++)
410 pentable[i] = i;
411 }
412 break;
413
414 case BITMAP_FORMAT_RGB32:
415 m_pens = reinterpret_cast<const pen_t *>(m_palette->entry_list_adjusted());
416 break;
417
418 default:
419 m_pens = nullptr;
420 break;
421 }
422 }
423
424
425 //-------------------------------------------------
426 // allocate_shadow_tables - allocate memory for
427 // shadow tables
428 //-------------------------------------------------
429
allocate_shadow_tables()430 void device_palette_interface::allocate_shadow_tables()
431 {
432 int numentries = m_palette->num_colors();
433
434 // if we have shadows, allocate shadow tables
435 if (m_shadow_group != 0)
436 {
437 m_shadow_array.resize(65536);
438
439 // palettized mode gets a single 64k table in slots 0 and 2
440 if (m_format == BITMAP_FORMAT_IND16)
441 {
442 m_shadow_tables[0].base = m_shadow_tables[2].base = &m_shadow_array[0];
443 for (int i = 0; i < 65536; i++)
444 m_shadow_array[i] = (i < numentries) ? (i + numentries) : i;
445 }
446
447 // RGB mode gets two 32k tables in slots 0 and 2
448 else
449 {
450 m_shadow_tables[0].base = &m_shadow_array[0];
451 m_shadow_tables[2].base = &m_shadow_array[32768];
452 configure_rgb_shadows(0, PALETTE_DEFAULT_SHADOW_FACTOR);
453 }
454 }
455
456 // if we have hilights, allocate shadow tables
457 if (m_hilight_group != 0)
458 {
459 m_hilight_array.resize(65536);
460
461 // palettized mode gets a single 64k table in slots 1 and 3
462 if (m_format == BITMAP_FORMAT_IND16)
463 {
464 m_shadow_tables[1].base = m_shadow_tables[3].base = &m_hilight_array[0];
465 for (int i = 0; i < 65536; i++)
466 m_hilight_array[i] = (i < numentries) ? (i + 2 * numentries) : i;
467 }
468
469 // RGB mode gets two 32k tables in slots 1 and 3
470 else
471 {
472 m_shadow_tables[1].base = &m_hilight_array[0];
473 m_shadow_tables[3].base = &m_hilight_array[32768];
474 configure_rgb_shadows(1, PALETTE_DEFAULT_HIGHLIGHT_FACTOR);
475 }
476 }
477
478 // set the default table
479 m_shadow_table = m_shadow_tables[0].base;
480 }
481
482
483 //-------------------------------------------------
484 // configure_rgb_shadows - configure shadows
485 // for the RGB tables
486 //-------------------------------------------------
487
configure_rgb_shadows(int mode,float factor)488 void device_palette_interface::configure_rgb_shadows(int mode, float factor)
489 {
490 // only applies to RGB direct modes
491 assert(m_format != BITMAP_FORMAT_IND16);
492
493 // verify the shadow table
494 assert(mode >= 0 && mode < ARRAY_LENGTH(m_shadow_tables));
495 shadow_table_data &stable = m_shadow_tables[mode];
496 assert(stable.base != nullptr);
497
498 // regenerate the table
499 int ifactor = int(factor * 256.0f);
500 for (int rgb555 = 0; rgb555 < 32768; rgb555++)
501 {
502 u8 const r = rgb_t::clamp((pal5bit(rgb555 >> 10) * ifactor) >> 8);
503 u8 const g = rgb_t::clamp((pal5bit(rgb555 >> 5) * ifactor) >> 8);
504 u8 const b = rgb_t::clamp((pal5bit(rgb555 >> 0) * ifactor) >> 8);
505
506 // store either 16 or 32 bit
507 rgb_t final = rgb_t(r, g, b);
508 if (m_format == BITMAP_FORMAT_RGB32)
509 stable.base[rgb555] = final;
510 else
511 stable.base[rgb555] = final.as_rgb15();
512 }
513 }
514