xref: /freebsd/sys/dev/fb/splash_pcx.c (revision 2b8331622f0b212cf3bb4fc4914a501e5321d506)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
5  * Copyright (c) 1999 Kazutaka YOKOTA <yokota@freebsd.org>
6  * Copyright (c) 1999 Dag-Erling Coïdan Smørgrav
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer
14  *    in this position and unchanged.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * $FreeBSD$
33  */
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/linker.h>
39 #include <sys/module.h>
40 #include <sys/fbio.h>
41 
42 #include <dev/fb/fbreg.h>
43 #include <dev/fb/splashreg.h>
44 
45 static int splash_mode = -1;
46 static int splash_on = FALSE;
47 
48 static int pcx_start(video_adapter_t *adp);
49 static int pcx_end(video_adapter_t *adp);
50 static int pcx_splash(video_adapter_t *adp, int on);
51 static int pcx_init(void *data, int sdepth);
52 static int pcx_draw(video_adapter_t *adp);
53 
54 static splash_decoder_t pcx_decoder = {
55 	.name = "splash_pcx",
56 	.init = pcx_start,
57 	.term = pcx_end,
58 	.splash = pcx_splash,
59 	.data_type = SPLASH_IMAGE,
60 };
61 
62 SPLASH_DECODER(splash_pcx, pcx_decoder);
63 
64 static struct {
65 	int		 width;
66 	int		 height;
67 	int		 bpsl;
68 	int		 bpp;
69 	int		 planes;
70 	int		 zlen;
71 	const uint8_t	*zdata;
72 	uint8_t		*palette;
73 } pcx_info;
74 
75 static int
76 pcx_start(video_adapter_t *adp)
77 {
78 	static int modes[] = {
79 		M_VGA_CG320,
80 		M_VESA_CG640x480,
81 		M_VESA_CG800x600,
82 		M_VESA_CG1024x768,
83 		-1,
84 	};
85 	video_info_t info;
86 	int i;
87 
88 	if (pcx_decoder.data == NULL ||
89 	    pcx_decoder.data_size <= 0 ||
90 	    pcx_init(pcx_decoder.data, pcx_decoder.data_size))
91 		return (ENODEV);
92 
93 	if (bootverbose)
94 		printf("splash_pcx: image good:\n"
95 		    "  width = %d\n"
96 		    "  height = %d\n"
97 		    "  depth = %d\n"
98 		    "  planes = %d\n",
99 		    pcx_info.width, pcx_info.height,
100 		    pcx_info.bpp, pcx_info.planes);
101 
102 	for (i = 0; modes[i] >= 0; ++i) {
103 		if (vidd_get_info(adp, modes[i], &info) != 0)
104 			continue;
105 		if (bootverbose)
106 			printf("splash_pcx: considering mode %d:\n"
107 			    "  vi_width = %d\n"
108 			    "  vi_height = %d\n"
109 			    "  vi_depth = %d\n"
110 			    "  vi_planes = %d\n",
111 			    modes[i],
112 			    info.vi_width, info.vi_height,
113 			    info.vi_depth, info.vi_planes);
114 		if (info.vi_width >= pcx_info.width
115 		    && info.vi_height >= pcx_info.height
116 		    && info.vi_depth == pcx_info.bpp
117 		    && info.vi_planes == pcx_info.planes)
118 			break;
119 	}
120 
121 	splash_mode = modes[i];
122 	if (splash_mode == -1)
123 		return (ENODEV);
124 	if (bootverbose)
125 		printf("splash_pcx: selecting mode %d\n", splash_mode);
126 	return (0);
127 }
128 
129 static int
130 pcx_end(video_adapter_t *adp)
131 {
132 	/* nothing to do */
133 	return (0);
134 }
135 
136 static int
137 pcx_splash(video_adapter_t *adp, int on)
138 {
139 	if (on) {
140 		if (!splash_on) {
141 			if (vidd_set_mode(adp, splash_mode) || pcx_draw(adp))
142 				return 1;
143 			splash_on = TRUE;
144 		}
145 		return (0);
146 	} else {
147 		splash_on = FALSE;
148 		return (0);
149 	}
150 }
151 
152 struct pcx_header {
153 	uint8_t		 manufactor;
154 	uint8_t		 version;
155 	uint8_t		 encoding;
156 	uint8_t		 bpp;
157 	uint16_t	 xmin;
158 	uint16_t	 ymin;
159 	uint16_t	 xmax;
160 	uint16_t	 ymax;
161 	uint16_t	 hres;
162 	uint16_t	 vres;
163 	uint8_t		 colormap[48];
164 	uint8_t		 rsvd;
165 	uint8_t		 nplanes;
166 	uint16_t	 bpsl;
167 	uint16_t	 palinfo;
168 	uint16_t	 hsize;
169 	uint16_t	 vsize;
170 };
171 
172 #define MAXSCANLINE 1024
173 
174 static int
175 pcx_init(void *data, int size)
176 {
177 	const struct pcx_header *hdr = data;
178 
179 	if (size < 128 + 1 + 1 + 768 ||
180 	    hdr->manufactor != 10 ||
181 	    hdr->version != 5 ||
182 	    hdr->encoding != 1 ||
183 	    hdr->nplanes != 1 ||
184 	    hdr->bpp != 8 ||
185 	    hdr->bpsl > MAXSCANLINE ||
186 	    ((uint8_t *)data)[size - 769] != 12) {
187 		printf("splash_pcx: invalid PCX image\n");
188 		return (1);
189 	}
190 	pcx_info.width = hdr->xmax - hdr->xmin + 1;
191 	pcx_info.height = hdr->ymax - hdr->ymin + 1;
192 	pcx_info.bpsl = hdr->bpsl;
193 	pcx_info.bpp = hdr->bpp;
194 	pcx_info.planes = hdr->nplanes;
195 	pcx_info.zlen = size - (128 + 1 + 768);
196 	pcx_info.zdata = (uint8_t *)data + 128;
197 	pcx_info.palette = (uint8_t *)data + size - 768;
198 	return (0);
199 }
200 
201 static int
202 pcx_draw(video_adapter_t *adp)
203 {
204 	uint8_t *vidmem;
205 	int swidth, sheight, sbpsl;
206 	int banksize, origin;
207 	int c, i, j, pos, scan, x, y;
208 	uint8_t line[MAXSCANLINE];
209 
210 	if (pcx_info.zlen < 1)
211 		return (1);
212 
213 	vidd_load_palette(adp, pcx_info.palette);
214 
215 	vidmem = (uint8_t *)adp->va_window;
216 	swidth = adp->va_info.vi_width;
217 	sheight = adp->va_info.vi_height;
218 	sbpsl = adp->va_line_width;
219 	banksize = adp->va_window_size;
220 
221 	for (origin = 0; origin < sheight*sbpsl; origin += banksize) {
222 		vidd_set_win_org(adp, origin);
223 		bzero(vidmem, banksize);
224 	}
225 
226 	x = (swidth - pcx_info.width) / 2;
227 	y = (sheight - pcx_info.height) / 2;
228 	origin = 0;
229 	pos = y * sbpsl + x;
230 	while (pos > banksize) {
231 		pos -= banksize;
232 		origin += banksize;
233 	}
234 	vidd_set_win_org(adp, origin);
235 
236 	for (scan = i = 0; scan < pcx_info.height; ++scan, ++y, pos += sbpsl) {
237 		for (j = 0; j < pcx_info.bpsl && i < pcx_info.zlen; ++i) {
238 			if ((pcx_info.zdata[i] & 0xc0) == 0xc0) {
239 				c = pcx_info.zdata[i++] & 0x3f;
240 				if (i >= pcx_info.zlen)
241 					return (1);
242 			} else {
243 				c = 1;
244 			}
245 			if (j + c > pcx_info.bpsl)
246 				return (1);
247 			while (c--)
248 				line[j++] = pcx_info.zdata[i];
249 		}
250 
251 		if (pos > banksize) {
252 			origin += banksize;
253 			pos -= banksize;
254 			vidd_set_win_org(adp, origin);
255 		}
256 
257 		if (pos + pcx_info.width > banksize) {
258 			/* scanline crosses bank boundary */
259 			j = banksize - pos;
260 			bcopy(line, vidmem + pos, j);
261 			origin += banksize;
262 			pos -= banksize;
263 			vidd_set_win_org(adp, origin);
264 			bcopy(line + j, vidmem, pcx_info.width - j);
265 		} else {
266 			bcopy(line, vidmem + pos, pcx_info.width);
267 		}
268 	}
269 
270 	return (0);
271 }
272