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