1/* $NetBSD: splash.c,v 1.10 2011/02/07 02:57:49 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2006 Jared D. McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *        This product includes software developed by the NetBSD
18 *        Foundation, Inc. and its contributors.
19 * 4. Neither the name of The NetBSD Foundation nor the names of its
20 *    contributors may be used to endorse or promote products derived
21 *    from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36#include <sys/cdefs.h>
37__KERNEL_RCSID(0, "$NetBSD: splash.c,v 1.10 2011/02/07 02:57:49 jmcneill Exp $");
38
39#include "opt_splash.h"
40
41/* XXX */
42#define NSPLASH8  1
43#define	NSPLASH16 1
44#define	NSPLASH32 1
45
46#include <sys/param.h>
47#include <sys/device.h>
48#include <sys/systm.h>
49#include <sys/types.h>
50#include <sys/kernel.h>
51#include <sys/kthread.h>
52
53#include <dev/splash/splash.h>
54#include <dev/stbi/stbi.h>
55
56#ifdef SPLASHSCREEN
57
58static struct {
59	const u_char	*data;
60	size_t		datalen;
61} splash_image = { NULL, 0 };
62
63#define SPLASH_INDEX(r, g, b)						\
64	((((r) >> 6) << 4) | (((g) >> 6) << 2) | (((b) >> 6) << 0))
65
66static uint8_t splash_palette[SPLASH_CMAP_SIZE][3] = {
67	{ 0x00, 0x00, 0x00 },
68	{ 0x00, 0x00, 0x55 },
69	{ 0x00, 0x00, 0xaa },
70	{ 0x00, 0x00, 0xff },
71	{ 0x00, 0x55, 0x00 },
72	{ 0x00, 0x55, 0x55 },
73	{ 0x00, 0x55, 0xaa },
74	{ 0x00, 0x55, 0xff },
75	{ 0x00, 0xaa, 0x00 },
76	{ 0x00, 0xaa, 0x55 },
77	{ 0x00, 0xaa, 0xaa },
78	{ 0x00, 0xaa, 0xff },
79	{ 0x00, 0xff, 0x00 },
80	{ 0x00, 0xff, 0x55 },
81	{ 0x00, 0xff, 0xaa },
82	{ 0x00, 0xff, 0xff },
83	{ 0x55, 0x00, 0x00 },
84	{ 0x55, 0x00, 0x55 },
85	{ 0x55, 0x00, 0xaa },
86	{ 0x55, 0x00, 0xff },
87	{ 0x55, 0x55, 0x00 },
88	{ 0x55, 0x55, 0x55 },
89	{ 0x55, 0x55, 0xaa },
90	{ 0x55, 0x55, 0xff },
91	{ 0x55, 0xaa, 0x00 },
92	{ 0x55, 0xaa, 0x55 },
93	{ 0x55, 0xaa, 0xaa },
94	{ 0x55, 0xaa, 0xff },
95	{ 0x55, 0xff, 0x00 },
96	{ 0x55, 0xff, 0x55 },
97	{ 0x55, 0xff, 0xaa },
98	{ 0x55, 0xff, 0xff },
99	{ 0xaa, 0x00, 0x00 },
100	{ 0xaa, 0x00, 0x55 },
101	{ 0xaa, 0x00, 0xaa },
102	{ 0xaa, 0x00, 0xff },
103	{ 0xaa, 0x55, 0x00 },
104	{ 0xaa, 0x55, 0x55 },
105	{ 0xaa, 0x55, 0xaa },
106	{ 0xaa, 0x55, 0xff },
107	{ 0xaa, 0xaa, 0x00 },
108	{ 0xaa, 0xaa, 0x55 },
109	{ 0xaa, 0xaa, 0xaa },
110	{ 0xaa, 0xaa, 0xff },
111	{ 0xaa, 0xff, 0x00 },
112	{ 0xaa, 0xff, 0x55 },
113	{ 0xaa, 0xff, 0xaa },
114	{ 0xaa, 0xff, 0xff },
115	{ 0xff, 0x00, 0x00 },
116	{ 0xff, 0x00, 0x55 },
117	{ 0xff, 0x00, 0xaa },
118	{ 0xff, 0x00, 0xff },
119	{ 0xff, 0x55, 0x00 },
120	{ 0xff, 0x55, 0x55 },
121	{ 0xff, 0x55, 0xaa },
122	{ 0xff, 0x55, 0xff },
123	{ 0xff, 0xaa, 0x00 },
124	{ 0xff, 0xaa, 0x55 },
125	{ 0xff, 0xaa, 0xaa },
126	{ 0xff, 0xaa, 0xff },
127	{ 0xff, 0xff, 0x00 },
128	{ 0xff, 0xff, 0x55 },
129	{ 0xff, 0xff, 0xaa },
130	{ 0xff, 0xff, 0xff },
131};
132
133#if NSPLASH8 > 0
134static void	splash_render8(struct splash_info *, const char *, int,
135			       int, int, int, int);
136#endif
137#if NSPLASH16 > 0
138static void	splash_render16(struct splash_info *, const char *, int,
139				int, int, int, int);
140#endif
141#if NSPLASH32 > 0
142static void	splash_render32(struct splash_info *, const char *, int,
143				int, int, int, int);
144#endif
145
146int
147splash_setimage(const void *imgdata, size_t imgdatalen)
148{
149	if (splash_image.data != NULL) {
150		aprint_debug("WARNING: %s: already initialized\n", __func__);
151		return EBUSY;
152	}
153
154	aprint_verbose("%s: splash image @ %p, %zu bytes\n",
155	    __func__, imgdata, imgdatalen);
156	splash_image.data = imgdata;
157	splash_image.datalen = imgdatalen;
158
159	return 0;
160}
161
162int
163splash_get_cmap(int index, uint8_t *r, uint8_t *g, uint8_t *b)
164{
165	if (index < SPLASH_CMAP_OFFSET ||
166	    index >= SPLASH_CMAP_OFFSET + SPLASH_CMAP_SIZE)
167		return ERANGE;
168
169	*r = splash_palette[index - SPLASH_CMAP_OFFSET][0];
170	*g = splash_palette[index - SPLASH_CMAP_OFFSET][1];
171	*b = splash_palette[index - SPLASH_CMAP_OFFSET][2];
172
173	return 0;
174}
175
176int
177splash_render(struct splash_info *si, int flg)
178{
179	char *data = NULL;
180	int xoff, yoff, width, height, comp;
181	int error = 0;
182
183	if (splash_image.data == NULL) {
184		aprint_error("WARNING: %s: not initialized\n", __func__);
185		return ENXIO;
186	}
187
188	data = stbi_load_from_memory(splash_image.data,
189	    splash_image.datalen, &width, &height, &comp, STBI_rgb);
190	if (data == NULL) {
191		aprint_error("WARNING: couldn't load splash image: %s\n",
192		    stbi_failure_reason());
193		return EINVAL;
194	}
195	aprint_debug("%s: splash loaded, width %d height %d comp %d\n",
196	    __func__, width, height, comp);
197
198	/* XXX */
199	if (flg & SPLASH_F_CENTER) {
200		xoff = (si->si_width - width) / 2;
201		yoff = (si->si_height - height) / 2;
202	} else
203		xoff = yoff = 0;
204
205	switch (si->si_depth) {
206#if NSPLASH8 > 0
207	case 8:
208		splash_render8(si, data, xoff, yoff, width, height, flg);
209		break;
210#endif
211#if NSPLASH16 > 0
212	case 16:
213		splash_render16(si, data, xoff, yoff, width, height, flg);
214		break;
215#endif
216#if NSPLASH32 > 0
217	case 32:
218		splash_render32(si, data, xoff, yoff, width, height, flg);
219		break;
220#endif
221	default:
222		aprint_error("WARNING: Splash not supported at %dbpp\n",
223		    si->si_depth);
224		error = EINVAL;
225	}
226
227	if (data)
228		stbi_image_free(data);
229
230	return error;
231}
232
233#if NSPLASH8 > 0
234
235static void
236splash_render8(struct splash_info *si, const char *data, int xoff, int yoff,
237	       int swidth, int sheight, int flg)
238{
239	const char *d;
240	u_char *fb, *p;
241	u_char pix[3];
242	int x, y, i;
243	int filled;
244
245	fb = si->si_bits;
246
247	if (flg & SPLASH_F_FILL)
248		filled = 0;
249	else
250		filled = 1;
251
252	d = data;
253	fb += xoff + yoff * si->si_stride;
254
255	for (y = 0; y < sheight; y++) {
256		for (x = 0; x < swidth; x++) {
257			pix[0] = *d++;
258			pix[1] = *d++;
259			pix[2] = *d++;
260			if (filled == 0) {
261				p = si->si_bits;
262				i = 0;
263				while (i < si->si_height*si->si_stride) {
264					p[i] = SPLASH_INDEX(
265					    pix[0], pix[1], pix[2]) +
266					    SPLASH_CMAP_OFFSET;
267					i++;
268				}
269				filled = 1;
270			}
271			fb[x] = SPLASH_INDEX(pix[0], pix[1], pix[2]) +
272				    SPLASH_CMAP_OFFSET;
273		}
274		fb += si->si_width;
275	}
276
277	/* If we've just written to the shadow fb, copy it to the display */
278	if (si->si_hwbits) {
279		if (flg & SPLASH_F_FILL) {
280			memcpy(si->si_hwbits, si->si_bits,
281			    si->si_height*si->si_width);
282		} else {
283			u_char *rp, *hrp;
284
285			rp = si->si_bits + xoff + (yoff * si->si_width);
286			hrp = si->si_hwbits + xoff + (yoff * si->si_width);
287
288			for (y = 0; y < sheight; y++) {
289				memcpy(hrp, rp, swidth);
290				rp += si->si_stride;
291				hrp += si->si_stride;
292			}
293		}
294	}
295
296	return;
297}
298#endif /* !NSPLASH8 > 0 */
299
300#if NSPLASH16 > 0
301#define RGBTO16(b, o, x, c)					\
302	do {							\
303		uint16_t *_ptr = (uint16_t *)(&(b)[(o)]);	\
304		*_ptr = (((c)[(x)*3+0] / 8) << 11) |		\
305			(((c)[(x)*3+1] / 4) << 5) |		\
306			(((c)[(x)*3+2] / 8) << 0);		\
307	} while (0)
308
309static void
310splash_render16(struct splash_info *si, const char *data, int xoff, int yoff,
311		int swidth, int sheight, int flg)
312{
313	const char *d;
314	u_char *fb, *p;
315	u_char pix[3];
316	int x, y, i;
317	int filled;
318
319	fb = si->si_bits;
320
321	if (flg & SPLASH_F_FILL)
322		filled = 0;
323	else
324		filled = 1;
325
326	d = data;
327	fb += xoff * 2 + yoff * si->si_stride;
328
329	for (y = 0; y < sheight; y++) {
330		for (x = 0; x < swidth; x++) {
331			pix[0] = *d++;
332			pix[1] = *d++;
333			pix[2] = *d++;
334			if (filled == 0) {
335				p = si->si_bits;
336				i = 0;
337				while (i < si->si_height*si->si_stride) {
338					RGBTO16(p, i, 0, pix);
339					i += 2;
340				}
341				filled = 1;
342			}
343			RGBTO16(fb, x*2, 0, pix);
344		}
345		fb += si->si_stride;
346	}
347
348	/* If we've just written to the shadow fb, copy it to the display */
349	if (si->si_hwbits) {
350		if (flg & SPLASH_F_FILL) {
351			memcpy(si->si_hwbits, si->si_bits,
352			    si->si_height*si->si_stride);
353		} else {
354			u_char *rp, *hrp;
355
356			rp = si->si_bits + (xoff * 2) + (yoff * si->si_stride);
357			hrp = si->si_hwbits + (xoff * 2) +
358			    (yoff * si->si_stride);
359
360			for (y = 0; y < sheight; y++) {
361				memcpy(hrp, rp, swidth * 2);
362				rp += si->si_stride;
363				hrp += si->si_stride;
364			}
365		}
366	}
367
368	return;
369}
370#undef RGBTO16
371#endif /* !NSPLASH16 > 0 */
372
373#if NSPLASH32 > 0
374static void
375splash_render32(struct splash_info *si, const char *data, int xoff, int yoff,
376		int swidth, int sheight, int flg)
377{
378	const char *d;
379	u_char *fb, *p;
380	u_char pix[3];
381	int x, y, i;
382	int filled;
383
384	fb = si->si_bits;
385
386	if (flg & SPLASH_F_FILL)
387		filled = 0;
388	else
389		filled = 1;
390
391	d = data;
392	fb += xoff * 4 + yoff * si->si_stride;
393
394	for (y = 0; y < sheight; y++) {
395		for (x = 0; x < swidth; x++) {
396			pix[0] = *d++;
397			pix[1] = *d++;
398			pix[2] = *d++;
399			if (filled == 0) {
400				p = si->si_bits;
401				i = 0;
402				while (i < si->si_height*si->si_stride) {
403					p[i++] = pix[2];
404					p[i++] = pix[1];
405					p[i++] = pix[0];
406					p[i++] = 0;
407				}
408				filled = 1;
409			}
410			fb[x*4+0] = pix[2];
411			fb[x*4+1] = pix[1];
412			fb[x*4+2] = pix[0];
413			fb[x*4+3] = 0;
414		}
415		fb += si->si_stride;
416	}
417
418	/* If we've just written to the shadow fb, copy it to the display */
419	if (si->si_hwbits) {
420		if (flg & SPLASH_F_FILL) {
421			memcpy(si->si_hwbits, si->si_bits,
422			    si->si_height*si->si_stride);
423		} else {
424			u_char *rp, *hrp;
425
426			rp = si->si_bits + (xoff * 4) + (yoff * si->si_stride);
427			hrp = si->si_hwbits + (xoff * 4) +
428			    (yoff * si->si_stride);
429
430			for (y = 0; y < sheight; y++) {
431				memcpy(hrp, rp, swidth * 4);
432				rp += si->si_stride;
433				hrp += si->si_stride;
434			}
435		}
436	}
437
438	return;
439}
440#endif /* !NSPLASH32 > 0 */
441
442#endif /* !SPLASHSCREEN */
443