rasops.c revision 1.99
1/*	 $NetBSD: rasops.c,v 1.99 2019/07/30 14:33:04 rin Exp $	*/
2
3/*-
4 * Copyright (c) 1999 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Andrew Doran.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
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 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: rasops.c,v 1.99 2019/07/30 14:33:04 rin Exp $");
34
35#include "opt_rasops.h"
36#include "rasops_glue.h"
37#include "opt_wsmsgattrs.h"
38
39#include <sys/param.h>
40#include <sys/bswap.h>
41#include <sys/kmem.h>
42#include <sys/systm.h>
43#include <sys/time.h>
44
45#include <machine/endian.h>
46
47#include <dev/wscons/wsdisplayvar.h>
48#include <dev/wscons/wsconsio.h>
49#include <dev/wsfont/wsfont.h>
50#include <dev/rasops/rasops.h>
51
52#ifndef _KERNEL
53#include <errno.h>
54#endif
55
56#ifdef RASOPS_DEBUG
57#define DPRINTF(...) aprint_error(...)
58#else
59#define DPRINTF(...) __nothing
60#endif
61
62struct rasops_matchdata {
63	struct rasops_info *ri;
64	int wantcols, wantrows;
65	int bestscore;
66	struct wsdisplay_font *pick;
67	int ident;
68};
69
70/* ANSI colormap (R,G,B). Upper 8 are high-intensity */
71const uint8_t rasops_cmap[256 * 3] = {
72	0x00, 0x00, 0x00, /* black */
73	0x7f, 0x00, 0x00, /* red */
74	0x00, 0x7f, 0x00, /* green */
75	0x7f, 0x7f, 0x00, /* brown */
76	0x00, 0x00, 0x7f, /* blue */
77	0x7f, 0x00, 0x7f, /* magenta */
78	0x00, 0x7f, 0x7f, /* cyan */
79	0xc7, 0xc7, 0xc7, /* white - XXX too dim? */
80
81	0x7f, 0x7f, 0x7f, /* black */
82	0xff, 0x00, 0x00, /* red */
83	0x00, 0xff, 0x00, /* green */
84	0xff, 0xff, 0x00, /* brown */
85	0x00, 0x00, 0xff, /* blue */
86	0xff, 0x00, 0xff, /* magenta */
87	0x00, 0xff, 0xff, /* cyan */
88	0xff, 0xff, 0xff, /* white */
89
90	/*
91	 * For the cursor, we need at least the last (255th)
92	 * color to be white. Fill up white completely for
93	 * simplicity.
94	 */
95#define _CMWHITE 0xff, 0xff, 0xff,
96#define _CMWHITE16	_CMWHITE _CMWHITE _CMWHITE _CMWHITE \
97			_CMWHITE _CMWHITE _CMWHITE _CMWHITE \
98			_CMWHITE _CMWHITE _CMWHITE _CMWHITE \
99			_CMWHITE _CMWHITE _CMWHITE _CMWHITE
100	_CMWHITE16 _CMWHITE16 _CMWHITE16 _CMWHITE16 _CMWHITE16
101	_CMWHITE16 _CMWHITE16 _CMWHITE16 _CMWHITE16 _CMWHITE16
102	_CMWHITE16 _CMWHITE16 _CMWHITE16 _CMWHITE16 /* but not the last one */
103#undef _CMWHITE16
104#undef _CMWHITE
105
106	/*
107	 * For the cursor the fg/bg indices are bit inverted, so
108	 * provide complimentary colors in the upper 16 entries.
109	 */
110	0x7f, 0x7f, 0x7f, /* black */
111	0xff, 0x00, 0x00, /* red */
112	0x00, 0xff, 0x00, /* green */
113	0xff, 0xff, 0x00, /* brown */
114	0x00, 0x00, 0xff, /* blue */
115	0xff, 0x00, 0xff, /* magenta */
116	0x00, 0xff, 0xff, /* cyan */
117	0xff, 0xff, 0xff, /* white */
118
119	0x00, 0x00, 0x00, /* black */
120	0x7f, 0x00, 0x00, /* red */
121	0x00, 0x7f, 0x00, /* green */
122	0x7f, 0x7f, 0x00, /* brown */
123	0x00, 0x00, 0x7f, /* blue */
124	0x7f, 0x00, 0x7f, /* magenta */
125	0x00, 0x7f, 0x7f, /* cyan */
126	0xc7, 0xc7, 0xc7, /* white - XXX too dim? */
127};
128
129/* True if color is gray */
130const uint8_t rasops_isgray[16] = {
131	1, 0, 0, 0,
132	0, 0, 0, 1,
133	1, 0, 0, 0,
134	0, 0, 0, 1,
135};
136
137#ifdef RASOPS_APPLE_PALETTE
138/*
139 * Approximate ANSI colormap for legacy Apple color palettes
140 */
141static const uint8_t apple8_devcmap[16] = {
142	0xff,	/* black	0x00, 0x00, 0x00 */
143	0x6b,	/* red		0x99, 0x00, 0x00 */
144	0xc5,	/* green	0x00, 0x99, 0x00 */
145	0x59,	/* yellow	0x99, 0x99, 0x00 */
146	0xd4,	/* blue		0x00, 0x00, 0x99 */
147	0x68,	/* magenta	0x99, 0x00, 0x99 */
148	0xc2,	/* cyan		0x00, 0x99, 0x99 */
149	0x2b,	/* white	0xcc, 0xcc, 0xcc */
150
151	0x56,	/* black	0x99, 0x99, 0x99 */
152	0x23,	/* red		0xff, 0x00, 0x00 */
153	0xb9,	/* green	0x00, 0xff, 0x00 */
154	0x05,	/* yellow	0xff, 0xff, 0x00 */
155	0xd2,	/* blue		0x00, 0x00, 0xff */
156	0x1e,	/* magenta	0xff, 0x00, 0xff */
157	0xb4,	/* cyan		0x00, 0xff, 0xff */
158	0x00,	/* white	0xff, 0xff, 0xff */
159};
160
161static const uint8_t apple4_devcmap[16] = {
162	15,	/* black	*/
163	 3,	/* red		*/
164	 8,	/* green	*/
165	 1,	/* yellow	*/
166	 6,	/* blue		*/
167	 4,	/* magenta	*/
168	 7,	/* cyan		*/
169	12,	/* light grey	*/
170
171	13,	/* medium grey	*/
172	 3,	/* red		*/
173	 8,	/* green	*/
174	 1,	/* yellow	*/
175	 6,	/* blue		*/
176	 4,	/* magenta	*/
177	 7,	/* cyan		*/
178	 0,	/* white	*/
179};
180#endif
181
182/* Generic functions */
183static void	rasops_copyrows(void *, int, int, int);
184static int	rasops_mapchar(void *, int, u_int *);
185static void	rasops_cursor(void *, int, int, int);
186static int	rasops_allocattr_color(void *, int, int, int, long *);
187static int	rasops_allocattr_mono(void *, int, int, int, long *);
188static void	rasops_do_cursor(struct rasops_info *);
189static void	rasops_init_devcmap(struct rasops_info *);
190
191#if NRASOPS_ROTATION > 0
192static void	rasops_rotate_font(int *, int);
193static void	rasops_copychar(void *, int, int, int, int);
194
195/* rotate clockwise */
196static void	rasops_copycols_rotated_cw(void *, int, int, int, int);
197static void	rasops_copyrows_rotated_cw(void *, int, int, int);
198static void	rasops_erasecols_rotated_cw(void *, int, int, int, long);
199static void	rasops_eraserows_rotated_cw(void *, int, int, long);
200static void	rasops_putchar_rotated_cw(void *, int, int, u_int, long);
201
202/* rotate counter-clockwise */
203static void	rasops_copychar_ccw(void *, int, int, int, int);
204static void	rasops_copycols_rotated_ccw(void *, int, int, int, int);
205static void	rasops_copyrows_rotated_ccw(void *, int, int, int);
206#define rasops_erasecols_rotated_ccw rasops_erasecols_rotated_cw
207#define rasops_eraserows_rotated_ccw rasops_eraserows_rotated_cw
208static void	rasops_putchar_rotated_ccw(void *, int, int, u_int, long);
209
210/*
211 * List of all rotated fonts
212 */
213SLIST_HEAD(, rotatedfont) rotatedfonts = SLIST_HEAD_INITIALIZER(rotatedfonts);
214struct rotatedfont {
215	SLIST_ENTRY(rotatedfont) rf_next;
216	int rf_cookie;
217	int rf_rotated;
218};
219#endif	/* NRASOPS_ROTATION > 0 */
220
221void	rasops_make_box_chars_8(struct rasops_info *);
222void	rasops_make_box_chars_16(struct rasops_info *);
223void	rasops_make_box_chars_32(struct rasops_info *);
224void	rasops_make_box_chars_alpha(struct rasops_info *);
225
226extern int cold;
227
228/*
229 * Initialize a 'rasops_info' descriptor.
230 */
231int
232rasops_init(struct rasops_info *ri, int wantrows, int wantcols)
233{
234
235	memset(&ri->ri_optfont, 0, sizeof(ri->ri_optfont));
236#ifdef _KERNEL
237	/* Select a font if the caller doesn't care */
238	if (ri->ri_font == NULL) {
239		int cookie = -1;
240		int flags;
241
242		wsfont_init();
243
244		/*
245		 * first, try to find something that's as close as possible
246		 * to the caller's requested terminal size
247		 */
248		if (wantrows == 0)
249			wantrows = RASOPS_DEFAULT_HEIGHT;
250		if (wantcols == 0)
251			wantcols = RASOPS_DEFAULT_WIDTH;
252
253		flags = WSFONT_FIND_BESTWIDTH | WSFONT_FIND_BITMAP;
254		if ((ri->ri_flg & RI_ENABLE_ALPHA) != 0)
255			flags |= WSFONT_FIND_ALPHA;
256		if ((ri->ri_flg & RI_PREFER_ALPHA) != 0)
257			flags |= WSFONT_PREFER_ALPHA;
258		cookie = wsfont_find(NULL,
259			ri->ri_width / wantcols,
260			0,
261			0,
262			WSDISPLAY_FONTORDER_L2R,
263			WSDISPLAY_FONTORDER_L2R,
264			flags);
265
266		/*
267		 * this means there is no supported font in the list
268		 */
269		if (cookie <= 0) {
270			aprint_error("%s: font table is empty\n", __func__);
271			return -1;
272		}
273
274#if NRASOPS_ROTATION > 0
275		/*
276		 * Pick the rotated version of this font. This will create it
277		 * if necessary.
278		 */
279		if (ri->ri_flg & RI_ROTATE_MASK) {
280			if (ri->ri_flg & RI_ROTATE_CW)
281				rasops_rotate_font(&cookie, WSFONT_ROTATE_CW);
282			else if (ri->ri_flg & RI_ROTATE_CCW)
283				rasops_rotate_font(&cookie, WSFONT_ROTATE_CCW);
284		}
285#endif
286
287		if (wsfont_lock(cookie, &ri->ri_font)) {
288			aprint_error("%s: couldn't lock font\n", __func__);
289			return -1;
290		}
291
292		ri->ri_wsfcookie = cookie;
293	}
294#endif
295
296	/* This should never happen in reality... */
297	if ((uintptr_t)ri->ri_bits & 3) {
298		aprint_error("%s: bits not aligned on 32-bit boundary\n",
299		    __func__);
300		return -1;
301	}
302
303	if (ri->ri_stride & 3) {
304		aprint_error("%s: stride not aligned on 32-bit boundary\n",
305		    __func__);
306		return -1;
307	}
308
309	if (rasops_reconfig(ri, wantrows, wantcols))
310		return -1;
311
312	rasops_init_devcmap(ri);
313	return 0;
314}
315
316/*
317 * Reconfigure (because parameters have changed in some way).
318 */
319int
320rasops_reconfig(struct rasops_info *ri, int wantrows, int wantcols)
321{
322	int bpp, s;
323	size_t len;
324
325	s = splhigh();
326
327	if (wantrows == 0)
328		wantrows = RASOPS_DEFAULT_HEIGHT;
329	if (wantcols == 0)
330		wantcols = RASOPS_DEFAULT_WIDTH;
331
332	/* throw away old line drawing character bitmaps, if we have any */
333	if (ri->ri_optfont.data != NULL) {
334		kmem_free(ri->ri_optfont.data, ri->ri_optfont.stride *
335		    ri->ri_optfont.fontheight * ri->ri_optfont.numchars);
336		ri->ri_optfont.data = NULL;
337	}
338
339	/* autogenerate box drawing characters */
340	ri->ri_optfont.firstchar = WSFONT_FLAG_OPT;
341	ri->ri_optfont.numchars = 16;
342	ri->ri_optfont.fontwidth = ri->ri_font->fontwidth;
343	ri->ri_optfont.fontheight = ri->ri_font->fontheight;
344	ri->ri_optfont.stride = ri->ri_font->stride;
345	len = ri->ri_optfont.fontheight * ri->ri_optfont.stride *
346	    ri->ri_optfont.numchars;
347
348	if (ri->ri_font->fontwidth > 32 || ri->ri_font->fontwidth < 4) {
349		aprint_error("%s: fontwidth assumptions botched", __func__);
350		splx(s);
351		return -1;
352	}
353
354	if ((ri->ri_flg & RI_NO_AUTO) == 0) {
355		ri->ri_optfont.data = kmem_zalloc(len, KM_SLEEP);
356		if (FONT_IS_ALPHA(&ri->ri_optfont))
357			rasops_make_box_chars_alpha(ri);
358		else {
359			switch (ri->ri_optfont.stride) {
360			case 1:
361				rasops_make_box_chars_8(ri);
362				break;
363			case 2:
364				rasops_make_box_chars_16(ri);
365				break;
366			case 4:
367				rasops_make_box_chars_32(ri);
368				break;
369			default:
370				aprint_error(
371				    "%s: font stride assumptions botched",
372				    __func__);
373				splx(s);
374				return -1;
375			}
376		}
377	} else
378		memset(&ri->ri_optfont, 0, sizeof(ri->ri_optfont));
379
380	/* Need this to frob the setup below */
381	bpp = (ri->ri_depth == 15 ? 16 : ri->ri_depth);
382
383	if ((ri->ri_flg & RI_CFGDONE) != 0) {
384		ri->ri_bits = ri->ri_origbits;
385		ri->ri_hwbits = ri->ri_hworigbits;
386	}
387
388	/* Don't care if the caller wants a hideously small console */
389	if (wantrows < 10)
390		wantrows = 10;
391
392	if (wantcols < 20)
393		wantcols = 20;
394
395	/* Now constrain what they get */
396	ri->ri_emuwidth = ri->ri_font->fontwidth * wantcols;
397	ri->ri_emuheight = ri->ri_font->fontheight * wantrows;
398
399	if (ri->ri_emuwidth > ri->ri_width)
400		ri->ri_emuwidth = ri->ri_width;
401
402	if (ri->ri_emuheight > ri->ri_height)
403		ri->ri_emuheight = ri->ri_height;
404
405	/* Reduce width until aligned on a 32-bit boundary */
406	while ((ri->ri_emuwidth * bpp & 31) != 0)
407		ri->ri_emuwidth--;
408
409#if NRASOPS_ROTATION > 0
410	if (ri->ri_flg & (RI_ROTATE_CW|RI_ROTATE_CCW)) {
411		ri->ri_rows = ri->ri_emuwidth / ri->ri_font->fontwidth;
412		ri->ri_cols = ri->ri_emuheight / ri->ri_font->fontheight;
413	} else
414#endif
415	{
416
417		ri->ri_cols = ri->ri_emuwidth / ri->ri_font->fontwidth;
418		ri->ri_rows = ri->ri_emuheight / ri->ri_font->fontheight;
419	}
420	ri->ri_emustride = ri->ri_emuwidth * bpp >> 3;
421	ri->ri_delta = ri->ri_stride - ri->ri_emustride;
422	ri->ri_ccol = 0;
423	ri->ri_crow = 0;
424	ri->ri_pelbytes = bpp >> 3;
425
426	ri->ri_xscale = (ri->ri_font->fontwidth * bpp) >> 3;
427	ri->ri_yscale = ri->ri_font->fontheight * ri->ri_stride;
428	ri->ri_fontscale = ri->ri_font->fontheight * ri->ri_font->stride;
429
430	if ((ri->ri_delta & 3) != 0) {
431		aprint_error(
432		    "%s: ri_delta not aligned on 32-bit boundary", __func__);
433		splx(s);
434		return -1;
435	}
436	ri->ri_origbits = ri->ri_bits;
437	ri->ri_hworigbits = ri->ri_hwbits;
438
439	/* Clear the entire display */
440	if ((ri->ri_flg & RI_CLEAR) != 0)
441		memset(ri->ri_bits, 0, ri->ri_stride * ri->ri_height);
442
443	/* Now centre our window if needs be */
444	if ((ri->ri_flg & RI_CENTER) != 0) {
445		ri->ri_bits += (((ri->ri_width * bpp >> 3) -
446		    ri->ri_emustride) >> 1) & ~3;
447		ri->ri_bits += ((ri->ri_height - ri->ri_emuheight) >> 1) *
448		    ri->ri_stride;
449		if (ri->ri_hwbits != NULL) {
450			ri->ri_hwbits += (((ri->ri_width * bpp >> 3) -
451			    ri->ri_emustride) >> 1) & ~3;
452			ri->ri_hwbits +=
453			    ((ri->ri_height - ri->ri_emuheight) >> 1) *
454			    ri->ri_stride;
455		}
456		ri->ri_yorigin = (int)(ri->ri_bits - ri->ri_origbits) /
457		    ri->ri_stride;
458		ri->ri_xorigin = (((int)(ri->ri_bits - ri->ri_origbits) %
459		    ri->ri_stride) * 8 / bpp);
460	} else
461		ri->ri_xorigin = ri->ri_yorigin = 0;
462
463	/*
464	 * Fill in defaults for operations set.  XXX this nukes private
465	 * routines used by accelerated fb drivers.
466	 */
467	ri->ri_ops.mapchar = rasops_mapchar;
468	ri->ri_ops.copyrows = rasops_copyrows;
469	ri->ri_ops.copycols = rasops_copycols;
470	ri->ri_ops.erasecols = rasops_erasecols;
471	ri->ri_ops.eraserows = rasops_eraserows;
472	ri->ri_ops.cursor = rasops_cursor;
473	ri->ri_do_cursor = rasops_do_cursor;
474
475	ri->ri_caps &= ~(WSSCREEN_UNDERLINE | WSSCREEN_HILIT |
476		    WSSCREEN_WSCOLORS | WSSCREEN_REVERSE);
477
478	if ((ri->ri_flg & RI_FORCEMONO) != 0 ||
479#ifdef RASOPS_APPLE_PALETTE
480	    ri->ri_depth == 1
481#else
482	    ri->ri_depth < 8
483#endif
484	) {
485		ri->ri_ops.allocattr = rasops_allocattr_mono;
486		ri->ri_caps |= WSSCREEN_UNDERLINE | WSSCREEN_REVERSE;
487	} else {
488		ri->ri_ops.allocattr = rasops_allocattr_color;
489		ri->ri_caps |= WSSCREEN_UNDERLINE | WSSCREEN_HILIT |
490		    WSSCREEN_WSCOLORS | WSSCREEN_REVERSE;
491	}
492
493	switch (ri->ri_depth) {
494#if NRASOPS1 > 0
495	case 1:
496		rasops1_init(ri);
497		break;
498#endif
499#if NRASOPS2 > 0
500	case 2:
501		rasops2_init(ri);
502		break;
503#endif
504#if NRASOPS4 > 0
505	case 4:
506		rasops4_init(ri);
507		break;
508#endif
509#if NRASOPS8 > 0
510	case 8:
511		rasops8_init(ri);
512		break;
513#endif
514#if (NRASOPS15 + NRASOPS16) > 0
515	case 15:
516	case 16:
517		rasops15_init(ri);
518		break;
519#endif
520#if NRASOPS24 > 0
521	case 24:
522		rasops24_init(ri);
523		break;
524#endif
525#if NRASOPS32 > 0
526	case 32:
527		rasops32_init(ri);
528		break;
529#endif
530	default:
531		ri->ri_flg &= ~RI_CFGDONE;
532		aprint_error("%s: depth not supported\n", __func__);
533		splx(s);
534		return -1;
535	}
536
537#if NRASOPS_ROTATION > 0
538	if (ri->ri_flg & RI_ROTATE_MASK) {
539		if (ri->ri_flg & RI_ROTATE_CW) {
540			ri->ri_real_ops = ri->ri_ops;
541			ri->ri_ops.copycols = rasops_copycols_rotated_cw;
542			ri->ri_ops.copyrows = rasops_copyrows_rotated_cw;
543			ri->ri_ops.erasecols = rasops_erasecols_rotated_cw;
544			ri->ri_ops.eraserows = rasops_eraserows_rotated_cw;
545			ri->ri_ops.putchar = rasops_putchar_rotated_cw;
546		} else if (ri->ri_flg & RI_ROTATE_CCW) {
547			ri->ri_real_ops = ri->ri_ops;
548			ri->ri_ops.copycols = rasops_copycols_rotated_ccw;
549			ri->ri_ops.copyrows = rasops_copyrows_rotated_ccw;
550			ri->ri_ops.erasecols = rasops_erasecols_rotated_ccw;
551			ri->ri_ops.eraserows = rasops_eraserows_rotated_ccw;
552			ri->ri_ops.putchar = rasops_putchar_rotated_ccw;
553		}
554	}
555#endif
556
557	ri->ri_flg |= RI_CFGDONE;
558	splx(s);
559	return 0;
560}
561
562/*
563 * Map a character.
564 */
565static int
566rasops_mapchar(void *cookie, int c, u_int *cp)
567{
568	struct rasops_info *ri = (struct rasops_info *)cookie;
569
570	KASSERT(ri->ri_font != NULL);
571
572	if ((c = wsfont_map_unichar(ri->ri_font, c)) < 0 ||
573	    c < ri->ri_font->firstchar) {
574		*cp = ' ';
575		return 0;
576	}
577
578#if 0 /* XXXRO */
579	if (CHAR_IN_FONT(c, ri->ri_font)) {
580		*cp = ' ';
581		return 0;
582	}
583#endif
584
585	*cp = c;
586	return 5;
587}
588
589/*
590 * Allocate a color attribute.
591 */
592static int
593rasops_allocattr_color(void *cookie, int fg0, int bg0, int flg, long *attr)
594{
595	uint32_t fg = fg0, bg = bg0;
596
597	if (__predict_false(fg >= sizeof(rasops_isgray) ||
598	    bg >= sizeof(rasops_isgray)))
599		return EINVAL;
600
601#ifdef RASOPS_CLIPPING
602	fg &= 7;
603	bg &= 7;
604#endif
605	if ((flg & WSATTR_BLINK) != 0)
606		return EINVAL;
607
608	if ((flg & WSATTR_WSCOLORS) == 0) {
609#ifdef WS_DEFAULT_FG
610		fg = WS_DEFAULT_FG;
611#else
612		fg = WSCOL_WHITE;
613#endif
614#ifdef WS_DEFAULT_BG
615		bg = WS_DEFAULT_BG;
616#else
617		bg = WSCOL_BLACK;
618#endif
619	}
620
621	if ((flg & WSATTR_HILIT) != 0)
622		fg += 8;
623
624	if ((flg & WSATTR_REVERSE) != 0) {
625		uint32_t swap = fg;
626		fg = bg;
627		bg = swap;
628	}
629
630	flg &= WSATTR_USERMASK;
631
632	if (rasops_isgray[fg])
633		flg |= WSATTR_PRIVATE1;
634
635	if (rasops_isgray[bg])
636		flg |= WSATTR_PRIVATE2;
637
638	*attr = (bg << 16) | (fg << 24) | flg;
639	return 0;
640}
641
642/*
643 * Allocate a mono attribute.
644 */
645static int
646rasops_allocattr_mono(void *cookie, int fg0, int bg0, int flg, long *attr)
647{
648	uint32_t fg = fg0, bg = bg0;
649
650	if ((flg & (WSATTR_BLINK | WSATTR_HILIT | WSATTR_WSCOLORS)) != 0)
651		return EINVAL;
652
653	fg = 1;
654	bg = 0;
655
656	if ((flg & WSATTR_REVERSE) != 0) {
657		uint32_t swap = fg;
658		fg = bg;
659		bg = swap;
660	}
661
662	*attr = (bg << 16) | (fg << 24) | flg;
663	return 0;
664}
665
666/*
667 * Copy rows.
668 */
669static void
670rasops_copyrows(void *cookie, int src, int dst, int num)
671{
672	struct rasops_info *ri = (struct rasops_info *)cookie;
673	uint8_t *sp, *dp, *hp;
674	int n, stride;
675
676	hp = NULL;	/* XXX GCC */
677
678#ifdef RASOPS_CLIPPING
679	if (dst == src)
680		return;
681
682	if (src < 0) {
683		num += src;
684		src = 0;
685	}
686
687	if (src + num > ri->ri_rows)
688		num = ri->ri_rows - src;
689
690	if (dst < 0) {
691		num += dst;
692		dst = 0;
693	}
694
695	if (dst + num > ri->ri_rows)
696		num = ri->ri_rows - dst;
697
698	if (num <= 0)
699		return;
700#endif
701
702	num *= ri->ri_font->fontheight;
703	n = ri->ri_emustride;
704	stride = ri->ri_stride;
705
706	sp = ri->ri_bits + src * ri->ri_yscale;
707	dp = ri->ri_bits + dst * ri->ri_yscale;
708	if (ri->ri_hwbits)
709		hp = ri->ri_hwbits + dst * ri->ri_yscale;
710
711	while (num--) {
712		memmove(dp, sp, n);
713		dp += stride;
714		if (ri->ri_hwbits) {
715			memcpy(hp, sp, n);
716			hp += stride;
717		}
718		sp += stride;
719	}
720}
721
722/*
723 * Copy columns. This is slow, and hard to optimize due to alignment,
724 * and the fact that we have to copy both left->right and right->left.
725 * We simply cop-out here and use memmove(), since it handles all of
726 * these cases anyway.
727 */
728void
729rasops_copycols(void *cookie, int row, int src, int dst, int num)
730{
731	struct rasops_info *ri = (struct rasops_info *)cookie;
732	uint8_t *sp, *dp, *hp;
733	int height;
734
735	hp = NULL;	/* XXX GCC */
736
737#ifdef RASOPS_CLIPPING
738	if (dst == src)
739		return;
740
741	/* Catches < 0 case too */
742	if ((unsigned)row >= (unsigned)ri->ri_rows)
743		return;
744
745	if (src < 0) {
746		num += src;
747		src = 0;
748	}
749
750	if (src + num > ri->ri_cols)
751		num = ri->ri_cols - src;
752
753	if (dst < 0) {
754		num += dst;
755		dst = 0;
756	}
757
758	if (dst + num > ri->ri_cols)
759		num = ri->ri_cols - dst;
760
761	if (num <= 0)
762		return;
763#endif
764
765	num *= ri->ri_xscale;
766	row *= ri->ri_yscale;
767	height = ri->ri_font->fontheight;
768
769	sp = ri->ri_bits + row + src * ri->ri_xscale;
770	dp = ri->ri_bits + row + dst * ri->ri_xscale;
771	if (ri->ri_hwbits)
772		hp = ri->ri_hwbits + row + dst * ri->ri_xscale;
773
774	while (height--) {
775		memmove(dp, sp, num);
776		dp += ri->ri_stride;
777		if (ri->ri_hwbits) {
778			memcpy(hp, sp, num);
779			hp += ri->ri_stride;
780		}
781		sp += ri->ri_stride;
782	}
783}
784
785/*
786 * Turn cursor off/on.
787 */
788static void
789rasops_cursor(void *cookie, int on, int row, int col)
790{
791	struct rasops_info *ri = (struct rasops_info *)cookie;
792
793	/* Turn old cursor off */
794	if ((ri->ri_flg & RI_CURSOR) != 0)
795#ifdef RASOPS_CLIPPING
796		if ((ri->ri_flg & RI_CURSORCLIP) == 0)
797#endif
798			ri->ri_do_cursor(ri);
799
800	/* Select new cursor */
801#ifdef RASOPS_CLIPPING
802	ri->ri_flg &= ~RI_CURSORCLIP;
803
804	if (row < 0 || row >= ri->ri_rows)
805		ri->ri_flg |= RI_CURSORCLIP;
806	else if (col < 0 || col >= ri->ri_cols)
807		ri->ri_flg |= RI_CURSORCLIP;
808#endif
809	ri->ri_crow = row;
810	ri->ri_ccol = col;
811
812	if (on) {
813		ri->ri_flg |= RI_CURSOR;
814#ifdef RASOPS_CLIPPING
815		if ((ri->ri_flg & RI_CURSORCLIP) == 0)
816#endif
817			ri->ri_do_cursor(ri);
818	} else
819		ri->ri_flg &= ~RI_CURSOR;
820}
821
822/*
823 * Make the device colormap
824 */
825static void
826rasops_init_devcmap(struct rasops_info *ri)
827{
828	int i;
829	uint32_t c;
830	const uint8_t *p;
831
832	switch (ri->ri_depth) {
833	case 1:
834		ri->ri_devcmap[0] = 0;
835		for (i = 1; i < 16; i++)
836			ri->ri_devcmap[i] = -1;
837		return;
838
839	case 2:
840		for (i = 1; i < 15; i++)
841			ri->ri_devcmap[i] = 0xaaaaaaaa;
842
843		ri->ri_devcmap[0] = 0;
844		ri->ri_devcmap[8] = 0x55555555;
845		ri->ri_devcmap[15] = -1;
846		return;
847
848#ifdef RASOPS_APPLE_PALETTE
849	case 4:
850		for (i = 0; i < 16; i++) {
851			c = apple4_devcmap[i];
852			ri->ri_devcmap[i] =
853			    (c <<  0) | (c <<  4) | (c <<  8) | (c << 12) |
854			    (c << 16) | (c << 20) | (c << 24) | (c << 28);
855		}
856		return;
857#else
858	/* XXXRO What should we do here? */
859#endif
860
861	case 8:
862		if ((ri->ri_flg & RI_8BIT_IS_RGB) == 0) {
863			for (i = 0; i < 16; i++) {
864#ifdef RASOPS_APPLE_PALETTE
865				c = apple8_devcmap[i];
866#else
867				c = i;
868#endif
869				ri->ri_devcmap[i] =
870				    c | (c << 8) | (c << 16) | (c << 24);
871			}
872			return;
873		}
874	}
875
876	p = rasops_cmap;
877
878	for (i = 0; i < 16; i++) {
879		if (ri->ri_rnum <= 8)
880			c = (uint32_t)(*p >> (8 - ri->ri_rnum)) << ri->ri_rpos;
881		else
882			c = (uint32_t)(*p << (ri->ri_rnum - 8)) << ri->ri_rpos;
883		p++;
884
885		if (ri->ri_gnum <= 8)
886			c |= (uint32_t)(*p >> (8 - ri->ri_gnum)) << ri->ri_gpos;
887		else
888			c |= (uint32_t)(*p << (ri->ri_gnum - 8)) << ri->ri_gpos;
889		p++;
890
891		if (ri->ri_bnum <= 8)
892			c |= (uint32_t)(*p >> (8 - ri->ri_bnum)) << ri->ri_bpos;
893		else
894			c |= (uint32_t)(*p << (ri->ri_bnum - 8)) << ri->ri_bpos;
895		p++;
896
897		/* Fill the word for generic routines, which want this */
898		if (ri->ri_depth == 8) {
899			c |= c << 8;
900			c |= c << 16;
901		} else if (ri->ri_depth == 15 || ri->ri_depth == 16)
902			c |= c << 16;
903		else if (ri->ri_depth == 24) {
904#if BYTE_ORDER == LITTLE_ENDIAN
905#  ifndef RASOPS_SMALL
906			if (ri->ri_font->fontwidth != 12)
907#  endif
908				c = (c & 0x0000ff) << 16 | (c & 0x00ff00) |
909				    (c & 0xff0000) >> 16;
910#  ifndef RASOPS_SMALL
911			else
912				c = (c & 0x0000ff) | (c & 0x00ff00) << 8 |
913				    (c & 0xff0000) >> 8;
914#  endif
915#else
916			/* XXXRO What should we do here? */
917#endif
918			c |= (c & 0xff) << 24;
919		}
920
921		/* 24bpp does bswap on the fly. {32,16,15}bpp do it here. */
922		if ((ri->ri_flg & RI_BSWAP) == 0)
923			ri->ri_devcmap[i] = c;
924		else if (ri->ri_depth == 15 || ri->ri_depth == 16)
925			ri->ri_devcmap[i] = bswap16(c);
926		else if (ri->ri_depth == 32)
927			ri->ri_devcmap[i] = bswap32(c);
928		else /* 8, 24 */
929			ri->ri_devcmap[i] = c;
930	}
931}
932
933/*
934 * Unpack a rasops attribute
935 */
936void
937rasops_unpack_attr(long attr, int *fg, int *bg, int *underline)
938{
939
940	*fg = ((uint32_t)attr >> 24) & 0xf;
941	*bg = ((uint32_t)attr >> 16) & 0xf;
942	if (underline != NULL)
943		*underline = (uint32_t)attr & WSATTR_UNDERLINE;
944}
945
946/*
947 * Erase rows. This isn't static, since 24-bpp uses it in special cases.
948 */
949void
950rasops_eraserows(void *cookie, int row, int num, long attr)
951{
952	struct rasops_info *ri = (struct rasops_info *)cookie;
953	uint32_t *dp, *hp, clr;
954	int n, cnt, delta;
955
956	hp = NULL;	/* XXX GCC */
957
958#ifdef RASOPS_CLIPPING
959	if (row < 0) {
960		num += row;
961		row = 0;
962	}
963
964	if (row + num > ri->ri_rows)
965		num = ri->ri_rows - row;
966
967	if (num <= 0)
968		return;
969#endif
970
971	clr = ri->ri_devcmap[((uint32_t)attr >> 16) & 0xf];
972
973	/*
974	 * XXX The wsdisplay_emulops interface seems a little deficient in
975	 * that there is no way to clear the *entire* screen. We provide a
976	 * workaround here: if the entire console area is being cleared, and
977	 * the RI_FULLCLEAR flag is set, clear the entire display.
978	 */
979	if (num == ri->ri_rows && (ri->ri_flg & RI_FULLCLEAR) != 0) {
980		n = ri->ri_stride >> 2;
981		num = ri->ri_height;
982		dp = (uint32_t *)ri->ri_origbits;
983		if (ri->ri_hwbits)
984			hp = (uint32_t *)ri->ri_hworigbits;
985		delta = 0;
986	} else {
987		n = ri->ri_emustride >> 2;
988		num *= ri->ri_font->fontheight;
989		dp = (uint32_t *)(ri->ri_bits + row * ri->ri_yscale);
990		if (ri->ri_hwbits)
991			hp = (uint32_t *)(ri->ri_hwbits + row *
992			    ri->ri_yscale);
993		delta = ri->ri_delta;
994	}
995
996	while (num--) {
997		for (cnt = n; cnt; cnt--) {
998			*dp++ = clr;
999			if (ri->ri_hwbits)
1000				*hp++ = clr;
1001		}
1002		DELTA(dp, delta, uint32_t *);
1003		if (ri->ri_hwbits)
1004			DELTA(hp, delta, uint32_t *);
1005	}
1006}
1007
1008/*
1009 * Actually turn the cursor on or off. This does the dirty work for
1010 * rasops_cursor().
1011 */
1012static void
1013rasops_do_cursor(struct rasops_info *ri)
1014{
1015	int full, height, cnt, slop1, slop2, row, col;
1016	uint32_t tmp32, msk1, msk2;
1017	uint8_t tmp8;
1018	uint8_t *dp, *rp, *hrp, *hp;
1019
1020	hrp = hp = NULL;	/* XXX GCC */
1021
1022#if NRASOPS_ROTATION > 0
1023	if (ri->ri_flg & RI_ROTATE_MASK) {
1024		if (ri->ri_flg & RI_ROTATE_CW) {
1025			/* Rotate rows/columns */
1026			row = ri->ri_ccol;
1027			col = ri->ri_rows - ri->ri_crow - 1;
1028		} else if (ri->ri_flg & RI_ROTATE_CCW) {
1029			/* Rotate rows/columns */
1030			row = ri->ri_cols - ri->ri_ccol - 1;
1031			col = ri->ri_crow;
1032		} else {	/* upside-down */
1033			row = ri->ri_crow;
1034			col = ri->ri_ccol;
1035		}
1036	} else
1037#endif
1038	{
1039		row = ri->ri_crow;
1040		col = ri->ri_ccol;
1041	}
1042
1043	rp = ri->ri_bits + row * ri->ri_yscale + col * ri->ri_xscale;
1044	if (ri->ri_hwbits)
1045		hrp = ri->ri_hwbits + row * ri->ri_yscale + col
1046		    * ri->ri_xscale;
1047	height = ri->ri_font->fontheight;
1048
1049	/*
1050	 * For ri_xscale = 1:
1051	 *
1052	 * Logic below does not work for ri_xscale = 1, e.g.,
1053	 * fontwidth = 8 and bpp = 1. So we take care of it.
1054	 */
1055	if (ri->ri_xscale == 1) {
1056		while (height--) {
1057			tmp8 = ~*rp;
1058
1059			*rp = tmp8;
1060			rp += ri->ri_stride;
1061
1062			if (ri->ri_hwbits) {
1063				*hrp = tmp8;
1064				hrp += ri->ri_stride;
1065			}
1066		}
1067		return;
1068	}
1069
1070	/*
1071	 * For ri_xscale = 2, 3, 4, ...:
1072	 *
1073	 * Note that siop1 <= ri_xscale even for ri_xscale = 2,
1074	 * since rp % 3 = 0 or 2 (ri_stride % 4 = 0).
1075	 */
1076	slop1 = (4 - ((uintptr_t)rp & 3)) & 3;
1077	slop2 = (ri->ri_xscale - slop1) & 3;
1078	full = (ri->ri_xscale - slop1 /* - slop2 */) >> 2;
1079
1080	rp = (uint8_t *)((uintptr_t)rp & ~3);
1081	hrp = (uint8_t *)((uintptr_t)hrp & ~3);
1082
1083	msk1 = !slop1 ? 0 : be32toh(0xffffffffU >> (32 - (8 * slop1)));
1084	msk2 = !slop2 ? 0 : be32toh(0xffffffffU << (32 - (8 * slop2)));
1085
1086	while (height--) {
1087		dp = rp;
1088		rp += ri->ri_stride;
1089		if (ri->ri_hwbits) {
1090			hp = hrp;
1091			hrp += ri->ri_stride;
1092		}
1093
1094		if (slop1) {
1095			tmp32 = *(uint32_t *)dp ^ msk1;
1096			*(uint32_t *)dp = tmp32;
1097			dp += 4;
1098			if (ri->ri_hwbits) {
1099				*(uint32_t *)hp = tmp32;
1100				hp += 4;
1101			}
1102		}
1103
1104		for (cnt = full; cnt; cnt--) {
1105			tmp32 = ~*(uint32_t *)dp;
1106			*(uint32_t *)dp = tmp32;
1107			dp += 4;
1108			if (ri->ri_hwbits) {
1109				*(uint32_t *)hp = tmp32;
1110				hp += 4;
1111			}
1112		}
1113
1114		if (slop2) {
1115			tmp32 = *(uint32_t *)dp ^ msk2;
1116			*(uint32_t *)dp = tmp32;
1117			if (ri->ri_hwbits)
1118				*(uint32_t *)hp = tmp32;
1119		}
1120	}
1121}
1122
1123/*
1124 * Erase columns.
1125 */
1126void
1127rasops_erasecols(void *cookie, int row, int col, int num, long attr)
1128{
1129	struct rasops_info *ri = (struct rasops_info *)cookie;
1130	int height, cnt, slop1, slop2, clr;
1131	uint32_t *rp, *dp, *hrp, *hp;
1132
1133	hrp = hp = NULL;	/* XXX GCC */
1134
1135#ifdef RASOPS_CLIPPING
1136	if ((unsigned)row >= (unsigned)ri->ri_rows)
1137		return;
1138
1139	if (col < 0) {
1140		num += col;
1141		col = 0;
1142	}
1143
1144	if ((col + num) > ri->ri_cols)
1145		num = ri->ri_cols - col;
1146
1147	if (num <= 0)
1148		return;
1149#endif
1150
1151	num *= ri->ri_xscale;
1152	rp = (uint32_t *)(ri->ri_bits + row*ri->ri_yscale + col*ri->ri_xscale);
1153	if (ri->ri_hwbits)
1154		hrp = (uint32_t *)(ri->ri_hwbits + row*ri->ri_yscale +
1155		    col*ri->ri_xscale);
1156	height = ri->ri_font->fontheight;
1157	clr = ri->ri_devcmap[((uint32_t)attr >> 16) & 0xf];
1158
1159	/* Don't bother using the full loop for <= 32 pels */
1160	if (num <= 32) {
1161		if (((num | ri->ri_xscale) & 3) == 0) {
1162			/* Word aligned blt */
1163			num >>= 2;
1164
1165			while (height--) {
1166				dp = rp;
1167				DELTA(rp, ri->ri_stride, uint32_t *);
1168				if (ri->ri_hwbits) {
1169					hp = hrp;
1170					DELTA(hrp, ri->ri_stride, uint32_t *);
1171				}
1172
1173				for (cnt = num; cnt; cnt--) {
1174					*dp++ = clr;
1175					if (ri->ri_hwbits)
1176						*hp++ = clr;
1177				}
1178			}
1179		} else if (((num | ri->ri_xscale) & 1) == 0) {
1180			/*
1181			 * Halfword aligned blt. This is needed so the
1182			 * 15/16 bit ops can use this function.
1183			 */
1184			num >>= 1;
1185
1186			while (height--) {
1187				dp = rp;
1188				DELTA(rp, ri->ri_stride, uint32_t *);
1189				if (ri->ri_hwbits) {
1190					hp = hrp;
1191					DELTA(hrp, ri->ri_stride, uint32_t *);
1192				}
1193
1194				for (cnt = num; cnt; cnt--) {
1195					*(uint16_t *)dp = clr;
1196					DELTA(dp, 2, uint32_t *);
1197					if (ri->ri_hwbits) {
1198						*(uint16_t *)hp = clr;
1199						DELTA(hp, 2, uint32_t *);
1200					}
1201				}
1202			}
1203		} else {
1204			while (height--) {
1205				dp = rp;
1206				DELTA(rp, ri->ri_stride, uint32_t *);
1207				if (ri->ri_hwbits) {
1208					hp = hrp;
1209					DELTA(hrp, ri->ri_stride, uint32_t *);
1210				}
1211
1212				for (cnt = num; cnt; cnt--) {
1213					*(uint8_t *)dp = clr;
1214					DELTA(dp, 1, uint32_t *);
1215					if (ri->ri_hwbits) {
1216						*(uint8_t *)hp = clr;
1217						DELTA(hp, 1, uint32_t *);
1218					}
1219				}
1220			}
1221		}
1222
1223		return;
1224	}
1225
1226	slop1 = (4 - ((uintptr_t)rp & 3)) & 3;
1227	slop2 = (num - slop1) & 3;
1228	num = (num - slop1 /* - slop2 */) >> 2;
1229
1230	while (height--) {
1231		dp = rp;
1232		DELTA(rp, ri->ri_stride, uint32_t *);
1233		if (ri->ri_hwbits) {
1234			hp = hrp;
1235			DELTA(hrp, ri->ri_stride, uint32_t *);
1236		}
1237
1238		/* Align span to 4 bytes */
1239		if (slop1 & 1) {
1240			*(uint8_t *)dp = clr;
1241			DELTA(dp, 1, uint32_t *);
1242			if (ri->ri_hwbits) {
1243				*(uint8_t *)hp = clr;
1244				DELTA(hp, 1, uint32_t *);
1245			}
1246		}
1247
1248		if (slop1 & 2) {
1249			*(uint16_t *)dp = clr;
1250			DELTA(dp, 2, uint32_t *);
1251			if (ri->ri_hwbits) {
1252				*(uint16_t *)hp = clr;
1253				DELTA(hp, 2, uint32_t *);
1254			}
1255		}
1256
1257		/* Write 4 bytes per loop */
1258		for (cnt = num; cnt; cnt--) {
1259			*dp++ = clr;
1260			if (ri->ri_hwbits)
1261				*hp++ = clr;
1262		}
1263
1264		/* Write unaligned trailing slop */
1265		if (slop2 & 1) {
1266			*(uint8_t *)dp = clr;
1267			DELTA(dp, 1, uint32_t *);
1268			if (ri->ri_hwbits) {
1269				*(uint8_t *)hp = clr;
1270				DELTA(hp, 1, uint32_t *);
1271			}
1272		}
1273
1274		if (slop2 & 2) {
1275			*(uint16_t *)dp = clr;
1276			if (ri->ri_hwbits)
1277				*(uint16_t *)hp = clr;
1278		}
1279	}
1280}
1281
1282#if NRASOPS_ROTATION > 0
1283/*
1284 * Quarter clockwise rotation routines (originally intended for the
1285 * built-in Zaurus C3x00 display in 16bpp).
1286 */
1287
1288static void
1289rasops_rotate_font(int *cookie, int rotate)
1290{
1291	struct rotatedfont *f;
1292	int ncookie;
1293
1294	SLIST_FOREACH(f, &rotatedfonts, rf_next) {
1295		if (f->rf_cookie == *cookie) {
1296			*cookie = f->rf_rotated;
1297			return;
1298		}
1299	}
1300
1301	/*
1302	 * We did not find a rotated version of this font. Ask the wsfont
1303	 * code to compute one for us.
1304	 */
1305
1306	f = kmem_alloc(sizeof(*f), KM_SLEEP);
1307
1308	if ((ncookie = wsfont_rotate(*cookie, rotate)) == -1)
1309		goto fail;
1310
1311	f->rf_cookie = *cookie;
1312	f->rf_rotated = ncookie;
1313	SLIST_INSERT_HEAD(&rotatedfonts, f, rf_next);
1314
1315	*cookie = ncookie;
1316	return;
1317
1318fail:	kmem_free(f, sizeof(*f));
1319	return;
1320}
1321
1322static void
1323rasops_copychar(void *cookie, int srcrow, int dstrow, int srccol, int dstcol)
1324{
1325	struct rasops_info *ri = (struct rasops_info *)cookie;
1326	int height;
1327	int r_srcrow, r_dstrow, r_srccol, r_dstcol;
1328	uint8_t *sp, *dp;
1329
1330	r_srcrow = srccol;
1331	r_dstrow = dstcol;
1332	r_srccol = ri->ri_rows - srcrow - 1;
1333	r_dstcol = ri->ri_rows - dstrow - 1;
1334
1335	r_srcrow *= ri->ri_yscale;
1336	r_dstrow *= ri->ri_yscale;
1337	height = ri->ri_font->fontheight;
1338
1339	sp = ri->ri_bits + r_srcrow + r_srccol * ri->ri_xscale;
1340	dp = ri->ri_bits + r_dstrow + r_dstcol * ri->ri_xscale;
1341
1342	while (height--) {
1343		memmove(dp, sp, ri->ri_xscale);
1344		dp += ri->ri_stride;
1345		sp += ri->ri_stride;
1346	}
1347}
1348
1349static void
1350rasops_putchar_rotated_cw(void *cookie, int row, int col, u_int uc, long attr)
1351{
1352	struct rasops_info *ri = (struct rasops_info *)cookie;
1353	int height;
1354	uint8_t *rp;
1355
1356	if (__predict_false((unsigned int)row > ri->ri_rows ||
1357	    (unsigned int)col > ri->ri_cols))
1358		return;
1359
1360	/* Avoid underflow */
1361	if (ri->ri_rows - row - 1 < 0)
1362		return;
1363
1364	/* Do rotated char sans (side)underline */
1365	ri->ri_real_ops.putchar(cookie, col, ri->ri_rows - row - 1, uc,
1366	    attr & ~WSATTR_UNDERLINE);
1367
1368	/* Do rotated underline */
1369	rp = ri->ri_bits + col * ri->ri_yscale + (ri->ri_rows - row - 1) *
1370	    ri->ri_xscale;
1371	height = ri->ri_font->fontheight;
1372
1373	/* XXX this assumes 16-bit color depth */
1374	if ((attr & WSATTR_UNDERLINE) != 0) {
1375		uint16_t c =
1376		    (uint16_t)ri->ri_devcmap[((uint32_t)attr >> 24) & 0xf];
1377
1378		while (height--) {
1379			*(uint16_t *)rp = c;
1380			rp += ri->ri_stride;
1381		}
1382	}
1383}
1384
1385static void
1386rasops_erasecols_rotated_cw(void *cookie, int row, int col, int num, long attr)
1387{
1388	struct rasops_info *ri = (struct rasops_info *)cookie;
1389	int i;
1390
1391	for (i = col; i < col + num; i++)
1392		ri->ri_ops.putchar(cookie, row, i, ' ', attr);
1393}
1394
1395/* XXX: these could likely be optimised somewhat. */
1396static void
1397rasops_copyrows_rotated_cw(void *cookie, int src, int dst, int num)
1398{
1399	struct rasops_info *ri = (struct rasops_info *)cookie;
1400	int col, roff;
1401
1402	if (src > dst)
1403		for (roff = 0; roff < num; roff++)
1404			for (col = 0; col < ri->ri_cols; col++)
1405				rasops_copychar(cookie, src + roff, dst + roff,
1406				    col, col);
1407	else
1408		for (roff = num - 1; roff >= 0; roff--)
1409			for (col = 0; col < ri->ri_cols; col++)
1410				rasops_copychar(cookie, src + roff, dst + roff,
1411				    col, col);
1412}
1413
1414static void
1415rasops_copycols_rotated_cw(void *cookie, int row, int src, int dst, int num)
1416{
1417	int coff;
1418
1419	if (src > dst)
1420		for (coff = 0; coff < num; coff++)
1421			rasops_copychar(cookie, row, row, src + coff,
1422			    dst + coff);
1423	else
1424		for (coff = num - 1; coff >= 0; coff--)
1425			rasops_copychar(cookie, row, row, src + coff,
1426			    dst + coff);
1427}
1428
1429static void
1430rasops_eraserows_rotated_cw(void *cookie, int row, int num, long attr)
1431{
1432	struct rasops_info *ri = (struct rasops_info *)cookie;
1433	int col, rn;
1434
1435	for (rn = row; rn < row + num; rn++)
1436		for (col = 0; col < ri->ri_cols; col++)
1437			ri->ri_ops.putchar(cookie, rn, col, ' ', attr);
1438}
1439
1440/*
1441 * Quarter counter-clockwise rotation routines (originally intended for the
1442 * built-in Sharp W-ZERO3 display in 16bpp).
1443 */
1444static void
1445rasops_copychar_ccw(void *cookie, int srcrow, int dstrow, int srccol,
1446    int dstcol)
1447{
1448	struct rasops_info *ri = (struct rasops_info *)cookie;
1449	int height, r_srcrow, r_dstrow, r_srccol, r_dstcol;
1450	uint8_t *sp, *dp;
1451
1452	r_srcrow = ri->ri_cols - srccol - 1;
1453	r_dstrow = ri->ri_cols - dstcol - 1;
1454	r_srccol = srcrow;
1455	r_dstcol = dstrow;
1456
1457	r_srcrow *= ri->ri_yscale;
1458	r_dstrow *= ri->ri_yscale;
1459	height = ri->ri_font->fontheight;
1460
1461	sp = ri->ri_bits + r_srcrow + r_srccol * ri->ri_xscale;
1462	dp = ri->ri_bits + r_dstrow + r_dstcol * ri->ri_xscale;
1463
1464	while (height--) {
1465		memmove(dp, sp, ri->ri_xscale);
1466		dp += ri->ri_stride;
1467		sp += ri->ri_stride;
1468	}
1469}
1470
1471static void
1472rasops_putchar_rotated_ccw(void *cookie, int row, int col, u_int uc, long attr)
1473{
1474	struct rasops_info *ri = (struct rasops_info *)cookie;
1475	int height;
1476	uint8_t *rp;
1477
1478	if (__predict_false((unsigned int)row > ri->ri_rows ||
1479	    (unsigned int)col > ri->ri_cols))
1480		return;
1481
1482	/* Avoid underflow */
1483	if (ri->ri_cols - col - 1 < 0)
1484		return;
1485
1486	/* Do rotated char sans (side)underline */
1487	ri->ri_real_ops.putchar(cookie, ri->ri_cols - col - 1, row, uc,
1488	    attr & ~WSATTR_UNDERLINE);
1489
1490	/* Do rotated underline */
1491	rp = ri->ri_bits + (ri->ri_cols - col - 1) * ri->ri_yscale +
1492	    row * ri->ri_xscale +
1493	    (ri->ri_font->fontwidth - 1) * ri->ri_pelbytes;
1494	height = ri->ri_font->fontheight;
1495
1496	/* XXX this assumes 16-bit color depth */
1497	if ((attr & WSATTR_UNDERLINE) != 0) {
1498		uint16_t c =
1499		    (uint16_t)ri->ri_devcmap[((uint32_t)attr >> 24) & 0xf];
1500
1501		while (height--) {
1502			*(uint16_t *)rp = c;
1503			rp += ri->ri_stride;
1504		}
1505	}
1506}
1507
1508/* XXX: these could likely be optimised somewhat. */
1509static void
1510rasops_copyrows_rotated_ccw(void *cookie, int src, int dst, int num)
1511{
1512	struct rasops_info *ri = (struct rasops_info *)cookie;
1513	int col, roff;
1514
1515	if (src > dst)
1516		for (roff = 0; roff < num; roff++)
1517			for (col = 0; col < ri->ri_cols; col++)
1518				rasops_copychar_ccw(cookie,
1519				    src + roff, dst + roff, col, col);
1520	else
1521		for (roff = num - 1; roff >= 0; roff--)
1522			for (col = 0; col < ri->ri_cols; col++)
1523				rasops_copychar_ccw(cookie,
1524				    src + roff, dst + roff, col, col);
1525}
1526
1527static void
1528rasops_copycols_rotated_ccw(void *cookie, int row, int src, int dst, int num)
1529{
1530	int coff;
1531
1532	if (src > dst)
1533		for (coff = 0; coff < num; coff++)
1534			rasops_copychar_ccw(cookie, row, row,
1535			    src + coff, dst + coff);
1536	else
1537		for (coff = num - 1; coff >= 0; coff--)
1538			rasops_copychar_ccw(cookie, row, row,
1539			    src + coff, dst + coff);
1540}
1541#endif	/* NRASOPS_ROTATION */
1542
1543void
1544rasops_make_box_chars_16(struct rasops_info *ri)
1545{
1546	int c, i, mid;
1547	uint16_t vert_mask, hmask_left, hmask_right;
1548	uint16_t *data = (uint16_t *)ri->ri_optfont.data;
1549
1550	vert_mask = 0xc000U >> ((ri->ri_font->fontwidth >> 1) - 1);
1551	hmask_left = 0xff00U << (8 - (ri->ri_font->fontwidth >> 1));
1552	hmask_right = hmask_left >> ((ri->ri_font->fontwidth + 1) >> 1);
1553	mid = (ri->ri_font->fontheight + 1) >> 1;
1554
1555	/* 0x00 would be empty anyway so don't bother */
1556	for (c = 1; c < 16; c++) {
1557		data += ri->ri_font->fontheight;
1558		if (c & 1) {
1559			/* upper segment */
1560			for (i = 0; i < mid; i++)
1561				data[i] = vert_mask;
1562		}
1563		if (c & 4) {
1564			/* lower segment */
1565			for (i = mid; i < ri->ri_font->fontheight; i++)
1566				data[i] = vert_mask;
1567		}
1568		if (c & 2) {
1569			/* right segment */
1570			i = ri->ri_font->fontheight >> 1;
1571			data[mid - 1] |= hmask_right;
1572			data[mid] |= hmask_right;
1573		}
1574		if (c & 8) {
1575			/* left segment */
1576			data[mid - 1] |= hmask_left;
1577			data[mid] |= hmask_left;
1578		}
1579	}
1580}
1581
1582void
1583rasops_make_box_chars_8(struct rasops_info *ri)
1584{
1585	int c, i, mid;
1586	uint8_t vert_mask, hmask_left, hmask_right;
1587	uint8_t *data = (uint8_t *)ri->ri_optfont.data;
1588
1589	vert_mask = 0xc0U >> ((ri->ri_font->fontwidth >> 1) - 1);
1590	hmask_left = 0xf0U << (4 - (ri->ri_font->fontwidth >> 1));
1591	hmask_right = hmask_left >> ((ri->ri_font->fontwidth + 1) >> 1);
1592	mid = (ri->ri_font->fontheight + 1) >> 1;
1593
1594	/* 0x00 would be empty anyway so don't bother */
1595	for (c = 1; c < 16; c++) {
1596		data += ri->ri_font->fontheight;
1597		if (c & 1) {
1598			/* upper segment */
1599			for (i = 0; i < mid; i++)
1600				data[i] = vert_mask;
1601		}
1602		if (c & 4) {
1603			/* lower segment */
1604			for (i = mid; i < ri->ri_font->fontheight; i++)
1605				data[i] = vert_mask;
1606		}
1607		if (c & 2) {
1608			/* right segment */
1609			i = ri->ri_font->fontheight >> 1;
1610			data[mid - 1] |= hmask_right;
1611			data[mid] |= hmask_right;
1612		}
1613		if (c & 8) {
1614			/* left segment */
1615			data[mid - 1] |= hmask_left;
1616			data[mid] |= hmask_left;
1617		}
1618	}
1619}
1620
1621void
1622rasops_make_box_chars_32(struct rasops_info *ri)
1623{
1624	int c, i, mid;
1625	uint32_t vert_mask, hmask_left, hmask_right;
1626	uint32_t *data = (uint32_t *)ri->ri_optfont.data;
1627
1628	vert_mask = 0xc0000000U >> ((ri->ri_font->fontwidth >> 1) - 1);
1629	hmask_left = 0xffff0000U << (16 - (ri->ri_font->fontwidth >> 1));
1630	hmask_right = hmask_left >> ((ri->ri_font->fontwidth + 1) >> 1);
1631	mid = (ri->ri_font->fontheight + 1) >> 1;
1632
1633	/* 0x00 would be empty anyway so don't bother */
1634	for (c = 1; c < 16; c++) {
1635		data += ri->ri_font->fontheight;
1636		if (c & 1) {
1637			/* upper segment */
1638			for (i = 0; i < mid; i++)
1639				data[i] = vert_mask;
1640		}
1641		if (c & 4) {
1642			/* lower segment */
1643			for (i = mid; i < ri->ri_font->fontheight; i++)
1644				data[i] = vert_mask;
1645		}
1646		if (c & 2) {
1647			/* right segment */
1648			i = ri->ri_font->fontheight >> 1;
1649			data[mid - 1] |= hmask_right;
1650			data[mid] |= hmask_right;
1651		}
1652		if (c & 8) {
1653			/* left segment */
1654			data[mid - 1] |= hmask_left;
1655			data[mid] |= hmask_left;
1656		}
1657	}
1658}
1659
1660void
1661rasops_make_box_chars_alpha(struct rasops_info *ri)
1662{
1663	int c, i, hmid, vmid, wi, he;
1664	uint8_t *data = (uint8_t *)ri->ri_optfont.data;
1665	uint8_t *ddata;
1666
1667	he = ri->ri_font->fontheight;
1668	wi = ri->ri_font->fontwidth;
1669
1670	vmid = (he + 1) >> 1;
1671	hmid = (wi + 1) >> 1;
1672
1673	/* 0x00 would be empty anyway so don't bother */
1674	for (c = 1; c < 16; c++) {
1675		data += ri->ri_fontscale;
1676		if (c & 1) {
1677			/* upper segment */
1678			ddata = data + hmid;
1679			for (i = 0; i <= vmid; i++) {
1680				*ddata = 0xff;
1681				ddata += wi;
1682			}
1683		}
1684		if (c & 4) {
1685			/* lower segment */
1686			ddata = data + wi * vmid + hmid;
1687			for (i = vmid; i < he; i++) {
1688				*ddata = 0xff;
1689				ddata += wi;
1690			}
1691		}
1692		if (c & 2) {
1693			/* right segment */
1694			ddata = data + wi * vmid + hmid;
1695			for (i = hmid; i < wi; i++) {
1696				*ddata = 0xff;
1697				ddata++;
1698			}
1699		}
1700		if (c & 8) {
1701			/* left segment */
1702			ddata = data + wi * vmid;
1703			for (i = 0; i <= hmid; i++) {
1704				*ddata = 0xff;
1705				ddata++;
1706			}
1707		}
1708	}
1709}
1710
1711/*
1712 * Return a colour map appropriate for the given struct rasops_info in the
1713 * same form used by rasops_cmap[]
1714 * For now this is either a copy of rasops_cmap[] or an R3G3B2 map, it should
1715 * probably be a linear ( or gamma corrected? ) ramp for higher depths.
1716 */
1717
1718int
1719rasops_get_cmap(struct rasops_info *ri, uint8_t *palette, size_t bytes)
1720{
1721
1722	if ((ri->ri_depth == 8) && ((ri->ri_flg & RI_8BIT_IS_RGB) != 0)) {
1723		/* generate an R3G3B2 palette */
1724		int i, idx = 0;
1725		uint8_t tmp;
1726
1727		if (bytes < 768)
1728			return EINVAL;
1729		for (i = 0; i < 256; i++) {
1730			tmp = i & 0xe0;
1731			/*
1732			 * replicate bits so 0xe0 maps to a red value of 0xff
1733			 * in order to make white look actually white
1734			 */
1735			tmp |= (tmp >> 3) | (tmp >> 6);
1736			palette[idx] = tmp;
1737			idx++;
1738
1739			tmp = (i & 0x1c) << 3;
1740			tmp |= (tmp >> 3) | (tmp >> 6);
1741			palette[idx] = tmp;
1742			idx++;
1743
1744			tmp = (i & 0x03) << 6;
1745			tmp |= tmp >> 2;
1746			tmp |= tmp >> 4;
1747			palette[idx] = tmp;
1748			idx++;
1749		}
1750	} else
1751		memcpy(palette, rasops_cmap, uimin(bytes, sizeof(rasops_cmap)));
1752	return 0;
1753}
1754