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