From: <fb...@co...> - 2006-02-27 21:32:58
|
Hi all, This is a patch submission I'm making for my framebuffer Enhanced functionality module, whose purpose is to expand the range of graphics capabilities available within the kernel, and later available to userspace via my FBUI project (fbui.org). I also plan to use Enhanced to support an anti-aliased console font feature sometime in the near future. The generic graphics routines that I've written are taken from FBUI and are therefore written for use with vesafb; but they should work with other drivers. I've done this as a "diff -Naur" patch to kernel 2.6.15.4. If you need something else let me know. I've also placed the patch here: http://home.comcast.net/~fbui/Enhanced_0.txt Zack T. Smith diff -Naur linux-2.6.15.4-original/drivers/video/Kconfig linux-2.6.15.4/drivers/video/Kconfig --- linux-2.6.15.4-original/drivers/video/Kconfig 2006-02-10 07:22:48.000000000 +0000 +++ linux-2.6.15.4/drivers/video/Kconfig 2006-02-26 12:15:19.000000000 +0000 @@ -38,6 +38,64 @@ (e.g. an accelerated X server) and that are not frame buffer device-aware may cause unexpected results. If unsure, say N. +config FB_ENHANCED + tristate "Enhanced Functionality for a generic framebuffer" + depends on FB + default y + ---help--- + Enhanced Functionality is a compact and robust set of routines + derived from the FramebufferUI project (see http://fbui.org). + + They implement: + + drawing routines that are clippable and + which support pixel-level transparency (alpha blending) + - line draw + - rectangle fill + - triangle fill + + robust and optimized clippable image blit from userspace + and kernel memory, supporting images of these types: + - opaque 16, 24, 32 bpp + - 1 bpp bitmap + - 8 bit alpha with foreground color + + robust copy area, also clippable, supporting + up, down, left and right directions, currently + supporting 16/24/32 bpp. + + In future, this code will be used to permit anti-aliased + console fonts and will be necessary for the next FramebufferUI. + + Enhanced Functionality provides the paradigm for future + hardware-based accelerated versions of the functionality + it provides. + +config FB_ENHANCED_32BPP + bool "Support 32 bits per pixel drawing" + depends on FB_ENHANCED + default n + help + 32bpp (8 red, 8 green, 8 blue). This offers + good color realism however it is often slower + than 16bpp. + +config FB_ENHANCED_24BPP + bool "Support 24 bits per pixel drawing" + depends on FB_ENHANCED + default n + help + 24bpp drawing. It's memory-efficient but + in VESA it will probably always be slower + than either 16bpp or 32bpp. + +config FB_ENHANCED_16BPP + bool "Support 16 bits per pixel drawing" + depends on FB_ENHANCED + default y + help + 16bpp mode uses 5 bits for red, 6 for green, + and 5 for blue. In VESA mode, this depth + is sometimes twice as fast as 32bpp, other + times slower. + config FB_CFB_FILLRECT tristate depends on FB diff -Naur linux-2.6.15.4-original/drivers/video/Makefile linux-2.6.15.4/drivers/video/Makefile --- linux-2.6.15.4-original/drivers/video/Makefile 2006-02-10 07:22:48.000000000 +0000 +++ linux-2.6.15.4/drivers/video/Makefile 2006-02-24 12:59:50.000000000 +0000 @@ -13,6 +13,8 @@ modedb.o fbcvt.o fb-objs := $(fb-y) +obj-$(CONFIG_FB_ENHANCED) += fb_enhanced.o + obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o diff -Naur linux-2.6.15.4-original/drivers/video/fb_enhanced.c linux-2.6.15.4/drivers/video/fb_enhanced.c --- linux-2.6.15.4-original/drivers/video/fb_enhanced.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.15.4/drivers/video/fb_enhanced.c 2006-02-24 12:56:40.000000000 +0000 @@ -0,0 +1,3086 @@ +/* Framebuffer Enhanced Functionality + * + * Copyright (C) 2004-2006 Zack T Smith, fb...@co.... + * + * This code is derived from my FBUI project; see fbui.org. + * This code is covered by the GNU Public License and is provided AS-IS + * with no warranty or guarantee of any kind in any respect. + * + * It provides much, in a small space. Specifically: + * + * (1) a paradigm for more robust use of graphics hardware, involving + * + clippability + * + alpha blending + * + image blit for several useful image types + * + line draw + * + triangle fill + * + robust copyarea + * + * (2) generic routines to implement these features for + * 16/24/32bpp framebuffers, such as VESA. + * + * Still to do list: + * - offscreen drawing + * - offscreen to onscreen copy + * - overlay support + * - reimplementation of my former alpha+foreground drawing + * - alpha image blit + * - video RAM allocation, deallocation, mapping, unmapping + * - PCI-burst fillrect + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/fb.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/ctype.h> +#include <asm/types.h> +#include <asm/uaccess.h> + + + +/*----------------------------------------------------------------------------- + * Change log: + * 05 Sep 2005, fb...@co...: removed 8bpp code. + * 05 Sep 2005, fb...@co...: transparent hline speedup. + * 10 Sep 2005, fb...@co...: merged point/hline/vline/fillrect. + * 12 Sep 2005, fb...@co...: optimized right/leftward copyarea. + * 12 Sep 2005, fb...@co...: optimized 32bpp image draw RGB4. + * 12 Sep 2005, fb...@co...: optimized 32bpp image draw RGB3. + * 12 Sep 2005, fb...@co...: optimized 32bpp image draw RGB2. + * 12 Sep 2005, fb...@co...: optimized 32bpp image draw GREY. + * 12 Sep 2005, fb...@co...: optimized 32bpp opaque fillrect. + * 12 Sep 2005, fb...@co...: optimized 24bpp opaque fillrect. + * 13 Sep 2005, fb...@co...: unrolled loops for further speed-up. + * 17 Sep 2005, fb...@co...: putimage optimizations using memcpy_toio. + * 18 Sep 2005, fb...@co...: copyarea optimizations using memcpy_to/fromio. + * 20 Sep 2005, fb...@co...: added 1bpp monochrome putimage. + * 22 Sep 2005, fb...@co...: reimplemented fb_enh_line for speedup. + * 22 Sep 2005, fb...@co...: speed up of mono putimage by 52%. + * 02 Oct 2005, fb...@co...: 24bpp improvements & speedups. + * 31 Dec 2005, fb...@co...: created Advanced Functionality from fbui code. + *----------------------------------------------------------------------------- + */ + +#ifdef CONFIG_FB_ENHANCED + +u32 fb_pixel_from_rgb (struct fb_info *info, u32 value) +{ + u32 r,g,b; + unsigned long tmp; + unsigned long pixel; + + value &= 0xffffff; +#ifdef CONFIG_FB_UI + if (info->mode24) + return value; + if (info->mode565) { + return ((value >> 8) & 0xf800) | + ((value >> 5) & 0x7e0) | + ((value >> 3) & 31); + } + if (info->mode555) { + return ((value >> 9) & 0x7c00) | + ((value >> 6) & 0x3e0) | + ((value >> 3) & 31); + } +#endif + + tmp = 0xff; + b = tmp & value; + value >>= 8; + g = tmp & value; + value >>= 8; + r = tmp & value; + tmp = 8; + r >>= (tmp - info->var.red.length); + g >>= (tmp - info->var.green.length); + b >>= (tmp - info->var.blue.length); + r <<= info->var.red.offset; + g <<= info->var.green.offset; + b <<= info->var.blue.offset; + pixel = r | g | b; + return pixel; +} + + +u32 fb_pixel_to_rgb (struct fb_info *info, u32 value) +{ + u32 r,g,b; + unsigned char tmp = 8; + +#ifdef CONFIG_FB_UI + if (info->mode24) + return value & 0xffffff; + if (info->mode565) { + return ((value << 8) & 0xf80000) | + ((value << 5) & 0xfc00) | + ((value << 3) & 0xf8); + } + if (info->mode555) { + return ((value << 9) & 0xf80000) | + ((value << 6) & 0xf800) | + ((value << 3) & 0xf8); + } +#endif + + r = value >> info->var.red.offset; + g = value >> info->var.green.offset; + b = value >> info->var.blue.offset; + r &= (1 << info->var.red.length) - 1; + b &= (1 << info->var.blue.length) - 1; + g &= (1 << info->var.green.length) - 1; + r <<= (tmp - info->var.red.length); + g <<= (tmp - info->var.green.length); + b <<= (tmp - info->var.blue.length); + r <<= 16; + g <<= 8; + return r | g | b; +} + + +/* This routine combines two pixels based on an 8-bit alpha. + */ +inline u32 fb_combine_rgb_pixels (u32 orig_value, u32 value, u32 transp) +{ + u32 r,g,b; + u32 t; + register u32 m = 255; + transp &= 255; + t = m - transp; + r = transp * (m & (orig_value>>16)) + t * (m & (value>>16)); + g = transp * (m & (orig_value>>8)) + t * (m & (value>>8)); + b = transp * (m & orig_value) + t * (m & value); + r >>= 8; + g >>= 8; + b >>= 8; + value = (r<<16) | (g<<8) | b; + return value; +} + + +#ifdef CONFIG_FB_ENHANCED_32BPP +/* Optimized function to put opaque image data to a 32bpp display. + */ +static void fb_enh_putimage_opaque32 (struct fb_info *info, struct fb_put *p) +{ + int i, j; + u32 offset; + short xres, yres; + short width, height; + short x0, y0; + u8 src_bytes_per_pixel; + u32 *dest, *dest_save; + u8 *src0; + short stride; + short x1, y1; + short xstart,xend,ystart,yend; + + if (!info || !p) + return; + + xstart = p->xstart; + xend = p->xend; + if (xstart > xend) { + short tmp = xstart; + xstart = xend; + xend = tmp; + } + ystart = p->ystart; + yend = p->yend; + if (ystart > yend) { + short tmp = ystart; + ystart = yend; + yend = tmp; + } + if (xend < 0 || yend < 0 || xstart >= p->width || ystart >= p->height) + return; + if (xstart < 0) + xstart = 0; + if (ystart < 0) + ystart = 0; + if (xend >= p->width) + xend = p->width - 1; + if (yend >= p->height) + yend = p->height - 1; + + src0 = p->pixels; + xres = info->var.xres; + yres = info->var.yres; + width = p->xend - p->xstart + 1; + height = p->yend - p->ystart + 1; + x0 = p->x0 + xstart; + y0 = p->y0 + ystart; + x1 = p->x0 + xend; + y1 = p->y0 + yend; + stride = p->width; + + switch (p->type) { + case FB_IMAGETYPE_ALPHA: + src_bytes_per_pixel = 1; + break; + case FB_IMAGETYPE_GREY: + src_bytes_per_pixel = 1; + break; + case FB_IMAGETYPE_RGB2: + src_bytes_per_pixel = 2; + break; + case FB_IMAGETYPE_RGB3: + src_bytes_per_pixel = 3; + break; + case FB_IMAGETYPE_RGB4: + src_bytes_per_pixel = 4; + break; + default: + return; + } + + if (!src0 || width <= 0 || height <= 0 || + x1 < 0 || y1 < 0 || x0 >= xres || y0 >= yres || + stride <= 0) + return; + src0 += src_bytes_per_pixel * (stride * ystart + xstart); + if (x0 < 0) { + x0 = -x0; + width -= x0; + src0 += x0 * src_bytes_per_pixel; + x0 = 0; + } + if (x1 >= xres) { + short diff = x1 - xres + 1; + width -= diff; + x1 = xres - 1; + } + if (y0 < 0) { + y0 = -y0; + height -= y0; + src0 += y0 * stride * src_bytes_per_pixel; + y0 = 0; + } + if (y1 >= yres) { + short diff = y1 - yres + 1; + height -= diff; + y1 = yres - 1; + } + + if (p->clip_valid) { + if (y0 > p->clip_y1 || x0 > p->clip_x1 || + y1 < p->clip_y0 || x1 < p->clip_x0) + return; + if (x0 < p->clip_x0) { + short diff = p->clip_x0 - x0; + width -= diff; + src0 += diff * src_bytes_per_pixel; + x0 = p->clip_x0; + } + if (x1 > p->clip_x1) { + short diff = x1 - p->clip_x1; + width -= diff; + } + if (y0 < p->clip_y0) { + short diff = p->clip_y0 - y0; + height -= diff; + src0 += diff * stride * src_bytes_per_pixel; + y0 = p->clip_y0; + } + if (y1 > p->clip_y1) { + short diff = y1 - p->clip_y1; + height -= diff; + } + } + /*----------*/ + + dest = (u32*) info->screen_base; + offset = y0 * (info->fix.line_length>>2) + x0; + dest += offset; + dest_save = dest; + + switch (p->type) { + case FB_IMAGETYPE_RGB4: { + u32 *src, *src_save; + u32 *buffer; +#define BUFSIZE32 4096 + + /* What is needed is a function to transfer from memory chunks + * directly from userspace to memory mapped I/O. Without that, + * gotta kmalloc. + */ + buffer = kmalloc(BUFSIZE32, GFP_KERNEL); + if (!buffer) { + printk(KERN_INFO "fb_enh_putimage_opaque32: no memory\n"); + return; + } + + src = (u32*) src0; + src_save = src; + + j = height; + while (j-- > 0) { + i = width; + while (i > 0) { + u32 pixels = i > (BUFSIZE32/4) ? (BUFSIZE32/4) : i; + u32 bytes = pixels << 2; + + /* Data -> L2 cache */ + if (copy_from_user((u8*)buffer,(u8*)src, bytes)) + goto dealloc; + src += pixels; + + /* L2 cache -> VRAM */ + memcpy_toio ((u8*)dest, (u8*)buffer, bytes); + dest += pixels; + i -= pixels; + } + src_save += stride; + src = src_save; + dest_save += (info->fix.line_length>>2); + dest = dest_save; + } + +dealloc: + if (buffer) + kfree (buffer); + } + break; + + case FB_IMAGETYPE_RGB3: { + u8 *src = (u8*) src0, *src_save; + src_save = src; + + j = height; + while (j-- > 0) { + u32 value; + i = width; + while (i > 0) { +#if 0 + if (!(3 & (u32)src)) { + if (get_user(value, ((u32*)src))) + return; + value &= 0xffffff; + } else { +#endif + u8 tmp; + src += 2; + if (get_user(tmp, src)) /* red << 16 */ + return; + value = tmp; + value <<= 8; + src--; + if (get_user(tmp, src)) /* green << 8 */ + return; + value |= tmp; + value <<= 8; + src--; + if (get_user(tmp, src)) /* blue << 0 */ + return; + value |= tmp; + //} + src += 3; + + fb_writel (value, dest); + dest++; + i--; + } + src_save += stride * 3; + src = src_save; + dest_save += (info->fix.line_length>>2); + dest = dest_save; + } + } + break; + + case FB_IMAGETYPE_RGB2: { + u16 *src, *src_save; + src = (u16*) src0; + src_save = src; + + j = height; + while (j-- > 0) { + u32 value,value2; + i = width; + while (i > 0) { + u32 tmp; + if (i >= 2) { + if (get_user(tmp, ((u32*)src))) + return; + value = ((tmp << 8) & 0xf80000) | + ((tmp << 5) & 0xfc00) | + ((tmp << 3) & 0xf8); + value2 = ((tmp >> 8) & 0xf80000) | + ((tmp >> 11) & 0xfc00) | + ((tmp >> 13) & 0xf8); + src += 2; + fb_writel (value, dest); dest++; + fb_writel (value2, dest); dest++; + i -= 2; + } else { + if (get_user(tmp, src)) + return; + value = ((tmp << 8) & 0xf80000) | + ((tmp << 5) & 0xfc00) | + ((tmp << 3) & 0xf8); + src++; + fb_writel (value, dest); + dest++; + i--; + } + } + src_save += stride; + src = src_save; + dest_save += (info->fix.line_length>>2); + dest = dest_save; + } + } + break; + + case FB_IMAGETYPE_GREY: { + u8 *src, *src_save; + src = src0; + src_save = src; + + j = height; + while (j-- > 0) { + i = width; + while (i > 0) { + if (!(3 & (u32)src) && i >= 4) { + u32 *s = (u32*) src; + u32 value, value2; + if (get_user(value, s)) + return; + src += 4; + i -= 4; + + value2 = value & 0xff; + value2 |= (value2<<8) | (value2<<16); + value >>= 8; + fb_writel (value2, dest); + dest++; + + value2 = value & 0xff; + value2 |= (value2<<8) | (value2<<16); + value >>= 8; + fb_writel (value2, dest); + dest++; + + value2 = value & 0xff; + value2 |= (value2<<8) | (value2<<16); + value >>= 8; + fb_writel (value2, dest); + dest++; + + value2 = value & 0xff; + value2 |= (value2<<8) | (value2<<16); + fb_writel (value2, dest); + dest++; + } else { + u32 value; + if (get_user(value, src)) + return; + value &= 0xff; + value |= (value<<8) | (value<<16); + src++; + fb_writel (value, dest); + dest++; + i--; + } + } + src_save += stride; + src = src_save; + dest_save += (info->fix.line_length>>2); + dest = dest_save; + } + } + break; + } + +} +#endif + + + +/* Optimized function to put monochrome image. 1's put color, 0's put nothing. + */ +static void fb_enh_putimage_mono (struct fb_info *info, struct fb_put *p) +{ + int i,j; + u32 offset; + short xres, yres; + short width, height; + short x0, y0; + u8 *src0; + u8 *src, *src_save; + short stride; + short x1, y1; + short xstart, xend, ystart, yend; + u8 ix0; + + if (!info || !p) + return; + if (p->type != FB_IMAGETYPE_MONO) + return; + xstart = p->xstart; + xend = p->xend; + if (xstart > xend) { + short tmp = xstart; + xstart = xend; + xend = tmp; + } + ystart = p->ystart; + yend = p->yend; + if (ystart > yend) { + short tmp = ystart; + ystart = yend; + yend = tmp; + } + if (xend < 0 || yend < 0 || xstart >= p->width || ystart >= p->height) + return; + if (xstart < 0) + xstart = 0; + if (ystart < 0) + ystart = 0; + if (xend >= p->width) + xend = p->width - 1; + if (yend >= p->height) + yend = p->height - 1; + xres = info->var.xres; + yres = info->var.yres; + width = xend - xstart + 1; + height = yend - ystart + 1; + x0 = p->x0 + xstart; + y0 = p->y0 + ystart; + x1 = p->x0 + xend; + y1 = p->y0 + yend; + stride = p->width; + src0 = p->pixels; + + if (!src0 || width <= 0 || height <= 0 || + x1 < 0 || y1 < 0 || x0 >= xres || y0 >= yres || + stride <= 0) + return; + if (x0 < 0) { + x0 = -x0; + width -= x0; + xstart += x0; + x0 = 0; + } + if (x1 >= xres) { + short diff = x1 - xres + 1; + width -= diff; + x1 = xres - 1; + xend -= diff; + } + if (y0 < 0) { + y0 = -y0; + height -= y0; + ystart += y0; + y0 = 0; + } + if (y1 >= yres) { + short diff = y1 - yres + 1; + height -= diff; + y1 = yres - 1; + yend -= diff; + } + + if (p->clip_valid) { + if (y0 > p->clip_y1 || x0 > p->clip_x1 || + y1 < p->clip_y0 || x1 < p->clip_x0) + return; + if (x0 < p->clip_x0) { + short diff = p->clip_x0 - x0; + width -= diff; + x0 = p->clip_x0; + xstart += diff; + } + if (x1 > p->clip_x1) { + short diff = x1 - p->clip_x1; + width -= diff; + xend -= diff; + } + if (y0 < p->clip_y0) { + short diff = p->clip_y0 - y0; + height -= diff; + y0 = p->clip_y0; + ystart += diff; + } + if (y1 > p->clip_y1) { + short diff = y1 - p->clip_y1; + height -= diff; + yend -= diff; + } + } + /*----------*/ + + switch ((info->var.bits_per_pixel + 7) >> 3) { +#ifdef CONFIG_FB_ENHANCED_32BPP + case 4: { + u32 *dest, *dest_save; + dest = (u32*) info->screen_base; + offset = y0 * (info->fix.line_length>>2) + x0; + dest += offset; + dest_save = dest; + + src0 += ((stride+7)>>3) * ystart + (xstart>>3); + ix0 = xstart & 7; + + src = src0; + src_save = src; + + j = height; + while (j-- > 0) { + u8 k = 1 << ix0; + i = width; + + while (i > 0) { + if (i >= 32 && !(3 & (u32)src)) { + /* 52% faster using this code. + */ + register u32 k2, value, *s=(u32*)src; + register u32 *d = dest; + register u32 c = p->color; + if (get_user(value, s)) + return; + src += 4; + + k2 = k; + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + if (k2 & value) fb_writel (c, d); + k2 <<= 1; d++; + + dest = d; + k = 1; + i-=32; + } else { + register u8 value; + register u32 c = p->color, *s=(u32*)src; + if (get_user(value, s)) + return; + src++; + + while (i > 0 && k) { + if (k & value) + fb_writel (c, dest); + dest++; + k <<= 1; + i--; + } + + k = 1; + } + } + src_save += (stride+7)>>3; + src = src_save; + dest_save += (info->fix.line_length>>2); + dest = dest_save; + } + break; + } +#endif + +#ifdef CONFIG_FB_ENHANCED_24BPP + case 3: { + u8 *dest, *dest_save; + dest = info->screen_base; + offset = y0 * info->fix.line_length + x0*3; + dest += offset; + dest_save = dest; + + src0 += ((stride+7)>>3) * ystart + (xstart>>3); + ix0 = xstart & 7; + + src = src0; + src_save = src; + + j = height; + while (j-- > 0) { + u8 k = 1 << ix0; + u32 c = p->color; + + i = width; + while (i > 0) { + if (i >= 32 && k == 1) { + register u32 k2 = k; + register u32 value, *s; + register u8 *d2 = dest; + register u8 r, g; + g = c>>8; + r = c>>16; + s = (u32*) src; + + if (get_user(value, s)) + return; + src += 4; + + while (k2) { + if (k2 & value) { + fb_writeb (c, d2); + fb_writeb (g, (1+d2)); + fb_writeb (r, (2+d2)); + } + d2 += 3; + k2 <<= 1; + } + + dest += 96; + i -= 32; + } else { + u8 value; + u32 c = p->color; + register u8 r, g; + g = c>>8; + r = c>>16; + + if (get_user(value, src)) + return; + src++; + + while (i > 0 && k) { + if (k & value) { + fb_writeb (c, dest); + fb_writeb (g, (1+dest)); + fb_writeb (r, (2+dest)); + } + dest += 3; + k <<= 1; + i--; + } + } + k = 1; + } + + src_save += (stride+7)>>3; + src = src_save; + dest_save += info->fix.line_length; + dest = dest_save; + } + break; + } +#endif + +#ifdef CONFIG_FB_ENHANCED_16BPP + case 2: { + u16 *dest, *dest_save; + u16 pixel; + dest = (u16*) info->screen_base; + offset = y0 * (info->fix.line_length>>1) + x0; + dest += offset; + dest_save = dest; + + src0 += ((stride+7)>>3) * ystart + (xstart>>3); + ix0 = xstart & 7; + + pixel = fb_pixel_from_rgb (info, p->color); + + src = src0; + src_save = src; + + j = height; + while (j-- > 0) { + u8 k = 1 << ix0; + i = width; + + while (i > 0) { + if (i >= 16 && !(3 & (u32)src)) { + register u16 k2, value, *s=(u16*)src; + register u16 *d = dest; + register u16 c = pixel; + if (get_user(value, s)) + return; + src += 2; + k2 = k; + + if (k2 & value) fb_writew (c, d); + k2 <<= 1; + + if (k2 & value) fb_writew (c, (d+1)); + k2 <<= 1; + + if (k2 & value) fb_writew (c, (d+2)); + k2 <<= 1; + + if (k2 & value) fb_writew (c, (d+3)); + k2 <<= 1; + + if (k2 & value) fb_writew (c, (d+4)); + k2 <<= 1; + + if (k2 & value) fb_writew (c, (d+5)); + k2 <<= 1; + + if (k2 & value) fb_writew (c, (d+6)); + k2 <<= 1; + + if (k2 & value) fb_writew (c, (d+7)); + k2 <<= 1; + + if (k2 & value) fb_writew (c, (d+8)); + k2 <<= 1; + + if (k2 & value) fb_writew (c, (d+9)); + k2 <<= 1; + + if (k2 & value) fb_writew (c, (d+10)); + k2 <<= 1; + + if (k2 & value) fb_writew (c, (d+11)); + k2 <<= 1; + + if (k2 & value) fb_writew (c, (d+12)); + k2 <<= 1; + + if (k2 & value) fb_writew (c, (d+13)); + k2 <<= 1; + + if (k2 & value) fb_writew (c, (d+14)); + k2 <<= 1; + + if (k2 & value) fb_writew (c, (d+15)); + + dest += 16; + k = 1; + i -= 16; + } else { + u8 value; + if (get_user(value, src)) + return; + src++; + + while (i > 0 && k) { + if (k & value) + fb_writew (pixel, dest); + + dest++; + k <<= 1; + i--; + } + + k = 1; + } + } + src_save += (stride+7)>>3; + src = src_save; + dest_save += (info->fix.line_length>>1); + dest = dest_save; + } + } +#endif + } +} + + +#ifdef CONFIG_FB_ENHANCED_16BPP +/* Optimized function to put opaque image data to a 16bpp display. + */ +static void fb_enh_putimage_opaque16 (struct fb_info *info, struct fb_put *p) +{ + int i, j; + u32 offset; + short xres, yres; + short width, height; + short x0, y0; + u8 *src0; + u8 src_bytes_per_pixel; + u16 *dest, *dest_save; + short stride; + short x1, y1; + short xstart, xend, ystart, yend; + + if (!info || !p) + return; + + xstart = p->xstart; + xend = p->xend; + if (xstart > xend) { + short tmp = xstart; + xstart = xend; + xend = tmp; + } + ystart = p->ystart; + yend = p->yend; + if (ystart > yend) { + short tmp = ystart; + ystart = yend; + yend = tmp; + } + if (xend < 0 || yend < 0 || xstart >= p->width || ystart >= p->height) + return; + if (xstart < 0) + xstart = 0; + if (ystart < 0) + ystart = 0; + if (xend >= p->width) + xend = p->width - 1; + if (yend >= p->height) + yend = p->height - 1; + + src0 = p->pixels; + xres = info->var.xres; + yres = info->var.yres; + width = p->xend - p->xstart + 1; + height = p->yend - p->ystart + 1; + x0 = p->x0 + xstart; + y0 = p->y0 + ystart; + x1 = p->x0 + xend; + y1 = p->y0 + yend; + stride = p->width; + + switch (p->type) { + case FB_IMAGETYPE_ALPHA: + src_bytes_per_pixel = 1; + break; + case FB_IMAGETYPE_GREY: + src_bytes_per_pixel = 1; + break; + case FB_IMAGETYPE_RGB2: + src_bytes_per_pixel = 2; + break; + case FB_IMAGETYPE_RGB3: + src_bytes_per_pixel = 3; + break; + case FB_IMAGETYPE_RGB4: + src_bytes_per_pixel = 4; + break; + default: + return; + } + + if (!src0 || width <= 0 || height <= 0 || + x1 < 0 || y1 < 0 || x0 >= xres || y0 >= yres || + stride <= 0) + return; + src0 += src_bytes_per_pixel * (stride * ystart + xstart); + if (x0 < 0) { + x0 = -x0; + width -= x0; + src0 += x0 * src_bytes_per_pixel; + x0 = 0; + } + if (x1 >= xres) { + short diff = x1 - xres + 1; + width -= diff; + x1 = xres - 1; + } + if (y0 < 0) { + y0 = -y0; + height -= y0; + src0 += y0 * stride * src_bytes_per_pixel; + y0 = 0; + } + if (y1 >= yres) { + short diff = y1 - yres + 1; + height -= diff; + y1 = yres - 1; + } + + if (p->clip_valid) { + if (y0 > p->clip_y1 || x0 > p->clip_x1 || + y1 < p->clip_y0 || x1 < p->clip_x0) + return; + if (x0 < p->clip_x0) { + short diff = p->clip_x0 - x0; + width -= diff; + src0 += diff * src_bytes_per_pixel; + x0 = p->clip_x0; + } + if (x1 > p->clip_x1) { + short diff = x1 - p->clip_x1; + width -= diff; + /* x1 = p->clip_x1; */ + } + if (y0 < p->clip_y0) { + short diff = p->clip_y0 - y0; + height -= diff; + src0 += diff * stride * src_bytes_per_pixel; + y0 = p->clip_y0; + } + if (y1 > p->clip_y1) { + short diff = y1 - p->clip_y1; + height -= diff; + /* y1 = p->clip_y1; */ + } + } + /*----------*/ + + dest = (u16*) info->screen_base; + offset = y0 * (info->fix.line_length >> 1) + x0; + dest += offset; + dest_save = dest; + + switch (p->type) { + case FB_IMAGETYPE_RGB4: { + u32 *src, *src_save; + src = (u32*) src0; + src_save = src; + + j = height; + while (j-- > 0) { + u32 value; + i = width; + while (i-- > 0) { + if (get_user(value, src)) + return; + src++; + value = ((value >> 8) & 0xf800) | + ((value >> 5) & 0x7e0) | + ((value >> 3) & 31); + fb_writew (value, dest); + dest++; + } + src_save += stride; + src = src_save; + dest_save += (info->fix.line_length>>1); + dest = dest_save; + } + } + break; + + case FB_IMAGETYPE_RGB3: { + u8 *src = (u8*) src0, *src_save; + src_save = src; + + j = height; + while (j-- > 0) { + u32 value; + i = width; + while (i-- > 0) { + if (!(3 & (u32)src)) { + if (get_user(value, ((u32*)src))) + return; + value &= 0xffffff; + } else { + u8 tmp; + src += 2; + if (get_user(tmp, src)) + return; + value = tmp; + value <<= 8; + src--; + if (get_user(tmp, src)) + return; + value |= tmp; + value <<= 8; + src--; + if (get_user(tmp, src)) + return; + value |= tmp; + } + src += 3; + + value = ((value >> 8) & 0xf800) | + ((value >> 5) & 0x7e0) | + ((value >> 3) & 31); + fb_writew (value, dest); + dest++; + } + src_save += stride * 3; + src = src_save; + dest_save += (info->fix.line_length>>1); + dest = dest_save; + } + } + break; + + case FB_IMAGETYPE_RGB2: { + u16 *src, *src_save; + u8* buffer; + src = (u16*) src0; + src_save = src; + + /* What is needed is a function to transfer from memory chunks + * directly from userspace to memory mapped I/O. Without that, + * gotta kmalloc. + */ +#define BUFSIZE16 2040 + buffer = kmalloc(BUFSIZE16, GFP_KERNEL); + if (!buffer) { + printk(KERN_INFO "fb_enh_putimage_opaque16: no memory\n"); + return; + } + + j = height; + while (j-- > 0) { + i = width; + while (i > 0) { + u32 pixels = i > (BUFSIZE16/2) ? (BUFSIZE16/2) : i; + u32 bytes = pixels << 1; + + /* Userspace -> L2 cache */ + if (copy_from_user((u8*)buffer,(u8*)src, bytes)) + goto dealloc2; + src += pixels; + + /* L2 cache -> VRAM */ + memcpy_toio ((u8*)dest, (u8*)buffer, bytes); + dest += pixels; + i -= pixels; +#if 0 + + /* This runs at one half the speed of the + * above buffer-based code. + */ + if (!(3 & (u32)src) && i >= 4) { + u32 a, b, *s=(u32*)src; + if (get_user(a, s)) + return; + s++; + if (get_user(b, s)) + return; + src+=4; + + fb_writew (a, dest); + dest++; + fb_writew (a>>16, dest); + dest++; + fb_writew (b, dest); + dest++; + fb_writew (b>>16, dest); + dest++; + i-=4; + } else { + u32 tmp; + if (get_user(tmp, src)) + return; + tmp &= 0xffff; + src++; + + fb_writew (tmp, dest); + dest++; + i--; + } +#endif + } + src_save += stride; + src = src_save; + dest_save += (info->fix.line_length>>1); + dest = dest_save; + } + +dealloc2: + if (buffer) + kfree (buffer); + } + break; + + case FB_IMAGETYPE_GREY: { + u8 *src, *src_save; + src = src0; + src_save = src; + + j = height; + while (j-- > 0) { + i = width; + while (i > 0) { + if (!(3 & (u32)src) && i >= 4) { + u32 value, value2, *s=(u32*)src; + if (get_user(value, s)) + return; + src += 4; + i -= 4; + + value2 = value & 0xff; + value >>= 8; + value2 = ((value2 << 8) & 0xf800) | + ((value2 << 3) & 0x7e0) | + ((value2 >> 3) & 31); + fb_writew (value2, dest); + dest++; + + value2 = value & 0xff; + value >>= 8; + value2 = ((value2 << 8) & 0xf800) | + ((value2 << 3) & 0x7e0) | + ((value2 >> 3) & 31); + fb_writew (value2, dest); + dest++; + + value2 = value & 0xff; + value >>= 8; + value2 = ((value2 << 8) & 0xf800) | + ((value2 << 3) & 0x7e0) | + ((value2 >> 3) & 31); + fb_writew (value2, dest); + dest++; + + value2 = value & 0xff; + value >>= 8; + value2 = ((value2 << 8) & 0xf800) | + ((value2 << 3) & 0x7e0) | + ((value2 >> 3) & 31); + fb_writew (value2, dest); + dest++; + } else { + u32 value; + if (get_user(value, src)) + return; + value &= 0xff; + value = ((value << 8) & 0xf800) | + ((value << 3) & 0x7e0) | + ((value >> 3) & 31); + src++; + + fb_writew (value, dest); + dest++; + i--; + } + } + src_save += stride; + src = src_save; + dest_save += (info->fix.line_length>>1); + dest = dest_save; + } + } + break; + } +} +#endif + + +#ifdef CONFIG_FB_ENHANCED_24BPP +/* Optimized function to put opaque image data to a 24bpp display. + */ +static void fb_enh_putimage_opaque24 (struct fb_info *info, struct fb_put *p) +{ + int i, j; + u32 offset; + short xres, yres; + short width, height; + short x0, y0; + u8 *src0; + u8 src_bytes_per_pixel; + u8 *dest, *dest_save; + short stride; + short x1, y1; + short xstart, xend, ystart, yend; + + if (!info || !p) + return; + + xstart = p->xstart; + xend = p->xend; + if (xstart > xend) { + short tmp = xstart; + xstart = xend; + xend = tmp; + } + ystart = p->ystart; + yend = p->yend; + if (ystart > yend) { + short tmp = ystart; + ystart = yend; + yend = tmp; + } + if (xend < 0 || yend < 0 || xstart >= p->width || ystart >= p->height) + return; + if (xstart < 0) + xstart = 0; + if (ystart < 0) + ystart = 0; + if (xend >= p->width) + xend = p->width - 1; + if (yend >= p->height) + yend = p->height - 1; + + src0 = p->pixels; + xres = info->var.xres; + yres = info->var.yres; + width = p->xend - p->xstart + 1; + height = p->yend - p->ystart + 1; + x0 = p->x0 + xstart; + y0 = p->y0 + ystart; + x1 = p->x0 + xend; + y1 = p->y0 + yend; + stride = p->width; + + switch (p->type) { + case FB_IMAGETYPE_ALPHA: + src_bytes_per_pixel = 1; + break; + case FB_IMAGETYPE_GREY: + src_bytes_per_pixel = 1; + break; + case FB_IMAGETYPE_RGB2: + src_bytes_per_pixel = 2; + break; + case FB_IMAGETYPE_RGB3: + src_bytes_per_pixel = 3; + break; + case FB_IMAGETYPE_RGB4: + src_bytes_per_pixel = 4; + break; + default: + return; + } + + if (!src0 || width <= 0 || height <= 0 || + x1 < 0 || y1 < 0 || x0 >= xres || y0 >= yres || + stride <= 0) + return; + src0 += src_bytes_per_pixel * (stride * ystart + xstart); + if (x0 < 0) { + x0 = -x0; + width -= x0; + src0 += x0 * src_bytes_per_pixel; + x0 = 0; + } + if (x1 >= xres) { + short diff = x1 - xres + 1; + width -= diff; + x1 = xres - 1; + } + if (y0 < 0) { + y0 = -y0; + height -= y0; + src0 += y0 * stride * src_bytes_per_pixel; + y0 = 0; + } + if (y1 >= yres) { + short diff = y1 - yres + 1; + height -= diff; + y1 = yres - 1; + } + + if (p->clip_valid) { + if (y0 > p->clip_y1 || x0 > p->clip_x1 || + y1 < p->clip_y0 || x1 < p->clip_x0) + return; + if (x0 < p->clip_x0) { + short diff = p->clip_x0 - x0; + width -= diff; + src0 += diff * src_bytes_per_pixel; + x0 = p->clip_x0; + } + if (x1 > p->clip_x1) { + short diff = x1 - p->clip_x1; + width -= diff; + /* x1 = p->clip_x1; */ + } + if (y0 < p->clip_y0) { + short diff = p->clip_y0 - y0; + height -= diff; + src0 += diff * stride * src_bytes_per_pixel; + y0 = p->clip_y0; + } + if (y1 > p->clip_y1) { + short diff = y1 - p->clip_y1; + height -= diff; + /* y1 = p->clip_y1; */ + } + } + /*----------*/ + + dest = (u8*) info->screen_base; + offset = y0 * info->fix.line_length + 3 * x0; + dest += offset; + dest_save = dest; + + switch (p->type) { + case FB_IMAGETYPE_RGB4: { + u32 *src, *src_save; + src = (u32*) src0; + src_save = src; + + j = height; + while (j-- > 0) { + u32 value; + i = width; + while (i-- > 0) { + if (get_user(value, src)) + return; + src++; + + fb_writeb (value, dest); dest++; + fb_writeb (value>>8, dest); dest++; + fb_writeb (value>>16, dest); dest++; + } + src_save += stride; + src = src_save; + dest_save += info->fix.line_length; + dest = dest_save; + } + } + break; + + case FB_IMAGETYPE_RGB3: { + u8 *src = (u8*) src0, *src_save; + u8 *buffer; + + /* What is needed is a function to transfer from memory chunks + * directly from userspace to memory mapped I/O. Without that, + * gotta kmalloc. + */ +#define BUFSIZE24 3072 + buffer = kmalloc(BUFSIZE24, GFP_KERNEL); + if (!buffer) { + printk(KERN_INFO "fb_enh_putimage_opaque16: no memory\n"); + return; + } + + src_save = src; + + j = height; + while (j-- > 0) { + i = width; + while (i > 0) { + u32 pixels = i > (BUFSIZE24/3) ? (BUFSIZE24/3) : i; + u32 bytes = pixels * 3; + + /* Userspace -> L2 cache */ + if (copy_from_user((u8*)buffer,(u8*)src, bytes)) + goto dealloc3; + src += pixels; + + /* L2 cache -> VRAM */ + memcpy_toio ((u8*)dest, (u8*)buffer, bytes); + dest += pixels; + i -= pixels; + +#if 0 + j = height; + while (j-- > 0) { + u32 value; + i = width; + while (i-- > 0) { + u8 a,b,c; + + if (get_user(a, src)) + return; + src++; + if (get_user(b, src)) + return; + src++; + if (get_user(c, src)) + return; + src++; + + fb_writeb (a, dest); dest++; + fb_writeb (b, dest); dest++; + fb_writeb (c, dest); dest++; +#endif + } + src_save += stride * 3; + src = src_save; + dest_save += info->fix.line_length; + dest = dest_save; + } +dealloc3: + if (buffer) + kfree (buffer); + } + break; + + case FB_IMAGETYPE_RGB2: { + u16 *src, *src_save; + src = (u16*) src0; + src_save = src; + + j = height; + while (j-- > 0) { + i = width; + while (i > 0) { + u32 value; + u32 tmp; + + if (get_user(tmp, src)) + return; + src++; + + value = ((tmp << 8) & 0xf80000) | + ((tmp << 5) & 0xfc00) | + ((tmp << 3) & 0xf8); + fb_writeb (value, dest); dest++; value >>= 8; + fb_writeb (value, dest); dest++; value >>= 8; + fb_writeb (value, dest); dest++; + i--; + } + src_save += stride; + src = src_save; + dest_save += info->fix.line_length; + dest = dest_save; + } + } + break; + + case FB_IMAGETYPE_GREY: { + u8 *src, *src_save; + src = src0; + src_save = src; + + j = height; + while (j-- > 0) { + i = width; + while (i > 0) { + u8 value; + if (get_user(value, src)) + return; + src++; + + fb_writeb (value, dest); dest++; + fb_writeb (value, dest); dest++; + fb_writeb (value, dest); dest++; + i--; + } + src_save += stride; + src = src_save; + dest_save += info->fix.line_length; + dest = dest_save; + } + } + break; + } +} +#endif + + +/* generic_putimage + * + * Supports source images of: + * . native depth + * . 24 bits per pixel + * . 32 bits per pixel with transparency + * . 8 bits grey + * + * Re transparency: + * 0 is opaque, 255 is 100% transparent. + */ + +static void generic_putimage (struct fb_info *info, struct fb_put *p) +{ + int i, j; + int offset; + u16 bytes_per_pixel; + unsigned char *dest; + unsigned char *dest_save; + unsigned char *src, *src_save; + short xres, yres; + short width, height; + short x0, y0; + short x1, y1; + u32 src_bytes_per_pixel; + short stride; + + if (!info || !p) + return; + if (p->type == FB_IMAGETYPE_MONO) + return; + if (p->xend < 0 || p->yend < 0 || p->xstart >= p->width || p->ystart >= p->height) + return; + if (p->xstart > p->xend) { + short tmp = p->xstart; + p->xstart = p->xend; + p->xend = tmp; + } + if (p->ystart > p->yend) { + short tmp = p->ystart; + p->ystart = p->yend; + p->yend = tmp; + } + if (p->xstart < 0) + p->xstart = 0; + if (p->ystart < 0) + p->ystart = 0; + if (p->xend >= p->width) + p->xend = p->width - 1; + if (p->yend >= p->height) + p->yend = p->height - 1; + src = p->pixels; + xres = info->var.xres; + yres = info->var.yres; + width = p->xend - p->xstart + 1; + height = p->yend - p->ystart + 1; + x0 = p->x0; + y0 = p->y0; + x1 = p->x1; + y1 = p->y1; + stride = p->width; + + switch (p->type) { + case FB_IMAGETYPE_GREY: + src_bytes_per_pixel = 1; + break; + case FB_IMAGETYPE_RGB2: + src_bytes_per_pixel = 2; + break; + case FB_IMAGETYPE_RGB3: + src_bytes_per_pixel = 3; + break; + case FB_IMAGETYPE_RGB4: + case FB_IMAGETYPE_RGBA: + src_bytes_per_pixel = 4; + break; + default: + return; + } + + if (!src || width <= 0 || height <= 0 || + x1 < 0 || y1 < 0 || x0 >= xres || y0 >= yres || + stride <= 0) + return; + src += src_bytes_per_pixel * (p->width * p->ystart + p->xstart); + if (x0 < 0) { + x0 = -x0; + width -= x0; + src += x0 * src_bytes_per_pixel; + x0 = 0; + } + if (x1 >= xres) { + short diff = x1 - xres + 1; + width -= diff; + x1 = xres - 1; + } + if (y0 < 0) { + y0 = -y0; + height -= y0; + src += y0 * stride * src_bytes_per_pixel; + y0 = 0; + } + if (y1 >= yres) { + short diff = y1 - yres + 1; + height -= diff; + y1 = yres - 1; + } + + if (p->clip_valid) { + if (y0 > p->clip_y1 || x0 > p->clip_x1 || + y1 < p->clip_y0 || x1 < p->clip_x0) + return; + if (x0 < p->clip_x0) { + short diff = p->clip_x0 - x0; + width -= diff; + src += diff * src_bytes_per_pixel; + x0 = p->clip_x0; + } + if (x1 > p->clip_x1) { + short diff = x1 - p->clip_x1; + width -= diff; + } + if (y0 < p->clip_y0) { + short diff = p->clip_y0 - y0; + height -= diff; + src += diff * stride * src_bytes_per_pixel; + y0 = p->clip_y0; + } + if (y1 > p->clip_y1) { + short diff = y1 - p->clip_y1; + height -= diff; + } + } + /*----------*/ + + bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3; + dest = info->screen_base; + offset = (y0 + p->ystart) * info->fix.line_length + + (x0 + p->xstart) * bytes_per_pixel; + dest += offset; + dest_save = dest; + src_save = src; + + j = height; + while (j-- > 0) { + i = width; + while (i-- > 0) { + u32 value=0; + u32 transp=0; + u32 orig_value; + + switch (p->location) { + case FB_LOCATION_KERNEL: + switch (src_bytes_per_pixel) { + case 1: + value = *src++; + break; + case 2: + value = *(unsigned short*) src; + src += 2; + break; + case 3: { + value = src[2]; + value <<= 8; + value |= src[1]; + value <<= 8; + value |= *src++; + src += 2; + break; + } + case 4: + value = *(unsigned long*) src; + src += 4; + } + break; + + case FB_LOCATION_USER: + switch (src_bytes_per_pixel) { + case 1: { + u8 tmp=0; + if (get_user(tmp, src)) + return; + src++; + value = tmp; + break; + } + case 2: { + u16 tmp=0, *s=(u16*)src; + if (get_user(tmp, s)) + return; + src+=2; + value = tmp; + break; + } + case 3: { + u8 tmp; + src += 2; + if (get_user(tmp, src)) + return; + value = tmp; + value <<= 8; + src--; + if (get_user(tmp, src)) + return; + value |= tmp; + value <<= 8; + src--; + if (get_user(tmp, src)) + return; + value |= tmp; + src += 3; + break; + } + case 4: { + u32 *s=(u32*)src; + if (get_user(value, s)) + return; + src+=4; + break; + } + } + break; + + case FB_LOCATION_VRAM: + value = 0; + break; + + default: + return; + } + + switch (p->type) { + case FB_IMAGETYPE_RGBA: + transp = value >> 24; + break; + case FB_IMAGETYPE_RGB2: { + u32 r,g,b; + r = (value >> 11) & 31; + g = (value >> 5) & 63; + b = value & 31; + r <<= 19; + g <<= 10; + b <<= 3; + value = r | g | b; + } + break; + case FB_IMAGETYPE_GREY: + value |= (value<<8) | (value<<16); + } + + if (transp > 0 && transp < 255) { + orig_value = 0; + switch (bytes_per_pixel) { +#ifdef CONFIG_FB_ENHANCED_32BPP + case 4: + orig_value = fb_readl (dest); + break; +#endif + +#ifdef CONFIG_FB_ENHANCED_24BPP + case 3: { + u32 tmp; + orig_value = 0xff & fb_readb (dest); + dest++; + tmp = 0xff & fb_readb (dest); + dest++; + tmp <<= 8; + orig_value |= tmp; + tmp = 0xff & fb_readb (dest); + tmp <<= 16; + orig_value |= tmp; + dest -= 2; + break; + } +#endif + +#ifdef CONFIG_FB_ENHANCED_16BPP + case 2: + orig_value = fb_readw (dest); + break; +#endif + + } + + orig_value = fb_pixel_to_rgb (info, orig_value); + value = fb_combine_rgb_pixels (orig_value, value, transp); + } + + if (transp == 255) + dest += bytes_per_pixel; + else { + value = fb_pixel_from_rgb (info, value); + + switch (bytes_per_pixel) + { +#ifdef CONFIG_FB_ENHANCED_32BPP + case 4: + fb_writel (value, dest); + dest += 4; + break; +#endif + +#ifdef CONFIG_FB_ENHANCED_24BPP + case 3: + fb_writeb (value, dest); dest++; value >>= 8; + fb_writeb (value, dest); dest++; value >>= 8; + fb_writeb (value, dest); dest++; + break; +#endif + +#ifdef CONFIG_FB_ENHANCED_16BPP + case 2: + fb_writew (value, dest); + dest += 2; + break; +#endif + + default: + break; + } + } + } + src_save += stride * src_bytes_per_pixel; + src = src_save; + dest_save += info->fix.line_length; + dest = dest_save; + } +} + +static void fb_enh_putimage (struct fb_info *info, struct fb_put *p) +{ + u8 bytes_per_pixel; + + if (!info || !p) + return; + /*----------*/ + + bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3; + + if (p->type == FB_IMAGETYPE_MONO) { + fb_enh_putimage_mono (info, p); + return; + } + else + if (p->type != FB_IMAGETYPE_RGBA) { + if (bytes_per_pixel==2 && + info->var.red.length == 5 && + info->var.green.length == 6 && + info->var.blue.length == 5) { +#ifdef CONFIG_FB_ENHANCED_16BPP + fb_enh_putimage_opaque16 (info, p); + return; +#endif + } else + if (info->var.red.length == 8 && + info->var.green.length == 8 && + info->var.blue.length == 8) { + if (bytes_per_pixel==4) { +#ifdef CONFIG_FB_ENHANCED_32BPP + fb_enh_putimage_opaque32 (info, p); +#endif + } else { +#ifdef CONFIG_FB_ENHANCED_24BPP + fb_enh_putimage_opaque24 (info, p); +#endif + } + return; + } + } + + generic_putimage (info, p); +} + + +static void generic_fillrect (struct fb_info *info, struct fb_draw *p) +{ + u32 pixel, bytes_per_pixel, offset; + short xres, yres; + unsigned char *ptr, *ptr_save; + u32 transp; + short x0,x1,y0,y1; + int i, j; + + if (!info || !p) + return; + x0 = p->x0; + y0 = p->y0; + x1 = p->x1; + y1 = p->y1; + transp = p->color >> 24; + if (transp == 255) + return; + if (x0 > x1) { + short tmp = x0; + x0 = x1; + x1 = tmp; + } + if (y0 > y1) { + short tmp = y0; + y0 = y1; + y1 = tmp; + } + if (y1 < 0 || x1 < 0) + return; + xres = info->var.xres; + yres = info->var.yres; + if (x0 >= xres || y0 >= yres) + return; + if (x0 < 0) + x0 = 0; + if (x1 >= xres) + x1 = xres-1; + if (y0 < 0) + y0 = 0; + if (y1 >= yres) + y1 = yres-1; + if (p->clip_valid) { + if (y1 < p->clip_y0 || y0 > p->clip_y1 || + x1 < p->clip_x0 || x0 > p->clip_x1) + return; + + if (x0 < p->clip_x0) + x0 = p->clip_x0; + if (x1 > p->clip_x1) + x1 = p->clip_x1; + if (y0 < p->clip_y0) + y0 = p->clip_y0; + if (y1 > p->clip_y1) + y1 = p->clip_y1; + } + /*----------*/ + + bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3; + offset = y0 * info->fix.line_length + x0 * bytes_per_pixel; + ptr = ((unsigned char*)info->screen_base); + ptr += offset; + ptr_save = ptr; + pixel = fb_pixel_from_rgb (info, p->color); + + j = y1 - y0 + 1; + while (j--) { + ptr = ptr_save; + + i = x1 - x0 + 1; + + while (i--) { + if (transp) { + u32 orig_value = 0; + u32 value = p->color; + + switch (bytes_per_pixel) { +#ifdef CONFIG_FB_ENHANCED_32BPP + case 4: + orig_value = fb_readl (ptr); + break; +#endif + +#ifdef CONFIG_FB_ENHANCED_24BPP + case 3: { + u32 tmp; + orig_value = 0xff & fb_readb (ptr); + ptr++; + tmp = 0xff & fb_readb (ptr); + ptr++; + tmp <<= 8; + orig_value |= tmp; + tmp = 0xff & fb_readb (ptr); + tmp <<= 16; + orig_value |= tmp; + ptr -= 2; + break; + } +#endif + +#ifdef CONFIG_FB_ENHANCED_16BPP + case 2: + orig_value = fb_readw (ptr); + break; +#endif + + } + + orig_value = fb_pixel_to_rgb (info, orig_value); + value = fb_combine_rgb_pixels (orig_value, value, transp); + pixel = fb_pixel_from_rgb (info, value); + } + + switch (bytes_per_pixel) { +#ifdef CONFIG_FB_ENHANCED_32BPP + case 4: + fb_writel (pixel, ptr); + ptr += 4; + break; +#endif + +#ifdef CONFIG_FB_ENHANCED_24BPP + case 3: { + register u32 c = pixel; + fb_writeb (c, ptr); ptr++; c >>= 8; + fb_writeb (c, ptr); ptr++; c >>= 8; + fb_writeb (c, ptr); ptr++; + break; + } +#endif + +#ifdef CONFIG_FB_ENHANCED_16BPP + case 2: + fb_writew (pixel, ptr); + ptr += 2; + break; +#endif + }/*switch*/ + } + + ptr_save += info->fix.line_length; + } +} + +static void fb_enh_fillrect (struct fb_info *info, struct fb_draw *p) +{ + u32 offset; + short xres, yres; + short x0,x1,y0,y1; + int i, j; + + if (!info || !p) + return; + x0 = p->x0; + y0 = p->y0; + x1 = p->x1; + y1 = p->y1; + if (p->color >> 24) { + generic_fillrect (info, p); + return; + } + + if (x0 > x1) { + short tmp = x0; + x0 = x1; + x1 = tmp; + } + if (y0 > y1) { + short tmp = y0; + y0 = y1; + y1 = tmp; + } + + if (y1 < 0 || x1 < 0) + return; + xres = info->var.xres; + yres = info->var.yres; + if (x0 >= xres || y0 >= yres) + return; + if (x0 < 0) + x0 = 0; + if (x1 >= xres) + x1 = xres-1; + if (y0 < 0) + y0 = 0; + if (y1 >= yres) + y1 = yres-1; + if (p->clip_valid) { + if (y1 < p->clip_y0 || y0 > p->clip_y1 || + x1 < p->clip_x0 || x0 > p->clip_x1) + return; + + if (x0 < p->clip_x0) + x0 = p->clip_x0; + if (x1 > p->clip_x1) + x1 = p->clip_x1; + if (y0 < p->clip_y0) + y0 = p->clip_y0; + if (y1 > p->clip_y1) + y1 = p->clip_y1; + } + /*----------*/ + + switch (((info->var.bits_per_pixel + 7) >> 3)) { + case 4: { +#ifdef CONFIG_FB_ENHANCED_32BPP + u32 *ptr, *ptr_save; + short w = x1 - x0 + 1; + + ptr = ((u32*)info->screen_base); + offset = y0 * (info->fix.line_length>>2) + x0; + ptr += offset; + ptr_save = ptr; + + j = y1 - y0 + 1; + while (j-- > 0) { + register u32 px = p->color; + i = w; + + while (i > 0) { + if (i >= 8) { + register u32 *p = ptr; + fb_writel (px, p); + fb_writel (px, (p+1)); + fb_writel (px, (p+2)); + fb_writel (px, (p+3)); + fb_writel (px, (p+4)); + fb_writel (px, (p+5)); + fb_writel (px, (p+6)); + fb_writel (px, (p+7)); + ptr += 8; + i -= 8; + } else { + fb_writel (px, ptr); ptr++; + i--; + } + } + + ptr_save += info->fix.line_length>>2; + ptr = ptr_save; + } +#endif + } + break; + + case 3: { +#ifdef CONFIG_FB_ENHANCED_24BPP + u8 *ptr, *ptr_save; + u8 r,g,b; + short w = x1 - x0 + 1; + union { + u32 l[3]; + u8 c[12]; + } stripe; + r = p->color >> 16; + g = p->color >> 8; + b = p->color; + for (i=0; i<=9; i+=3) { + stripe.c[i] = b; + stripe.c[i+1] = g; + stripe.c[i+2] = r; + } + + ptr = ((u8*)info->screen_base); + offset = y0 * info->fix.line_length + x0*3; + ptr += offset; + ptr_save = ptr; + + j = y1 - y0 + 1; + while (j-- > 0) { + i = w; + + while (i > 0) { + if (i>=12 && !(3 & (u32)ptr)) { + register u32 a,b,c; + register u32 *p = (u32*)ptr; + a = stripe.l[0]; + b = stripe.l[1]; + c = stripe.l[2]; + fb_writel (a, p); p++; + fb_writel (b, p); p++; + fb_writel (c, p); p++; + fb_writel (a, p); p++; + fb_writel (b, p); p++; + fb_writel (c, p); p++; + fb_writel (a, p); p++; + fb_writel (b, p); p++; + fb_writel (c, p); p++; + ptr += 36; + i -= 12; + } else { + fb_writeb (b, ptr); ptr++; + fb_writeb (g, ptr); ptr++; + fb_writeb (r, ptr); ptr++; + i--; + } + } + + ptr_save += info->fix.line_length; + ptr = ptr_save; + } +#endif + } + break; + + case 2: { +#ifdef CONFIG_FB_ENHANCED_16BPP + u16 *ptr, *ptr_save; + u16 pix; + u32 pix2; + short w = x1 - x0 + 1; + + pix = fb_pixel_from_rgb (info,p->color); + pix2 = pix; + pix2 <<= 16; + pix2 |= pix; + ptr = ((u16*)info->screen_base); + offset = y0 * (info->fix.line_length >> 1) + x0; + ptr += offset; + ptr_save = ptr; + + j = y1 - y0 + 1; + while (j-- > 0) { + i = w; + + while (i > 0) { + if (!(3 & (u32)ptr) && i >= 8) { + register u32 *p = (u32*) ptr; + fb_writel (pix2, p); + fb_writel (pix2, (1+p)); + fb_writel (pix2, (2+p)); + fb_writel (pix2, (3+p)); + ptr += 8; + i -= 8; + } else { + fb_writew (pix, ptr); + ptr++; + i--; + } + } + + ptr_save += info->fix.line_length>>1; + ptr = ptr_save; + } +#endif + } + break; + } +} + + +static void fb_enh_copy_within (unsigned char *dest, unsigned char *src, u32 n) +{ + int dif = dest < src ? src - dest : dest - src; + while (n) { + if (dif >= 4 && n >= 16 && !(3 & (u32)src) && !(3 & (u32)dest)){ + /* This is most effective on pre-Pentium CPUs. + * and maybe could be shrunk down for >= Pentium. + */ + fb_writel (fb_readl (src), dest); src+=4; dest+=4; + fb_writel (fb_readl (src), dest); src+=4; dest+=4; + fb_writel (fb_readl (src), dest); src+=4; dest+=4; + fb_writel (fb_readl (src), dest); src+=4; dest+=4; + n -= 16; + } else { + fb_writeb (fb_readb (src), dest); src++; dest++; + n--; + } + } +} + + +/* Robust copyarea, supporting 16/24/32bpp, shifting up/down/left/right. + */ +static void fb_enh_copyarea (struct fb_info *info, struct fb_draw *p) +{ + u32 bytes_per_pixel, offset, rasterlen; + unsigned char *src; + unsigned char *dest; + short w, h; + short xres, yres; + u32 stride; + short x0,y0,x1,y1,x2,y2; + + if (!info || !p) + return; + x0 = p->x0; + y0 = p->y0; + x1 = p->x1; + y1 = p->y1; + x2 = p->x2; + y2 = p->y2; + if (x0 > x1) { + short tmp = x0; + x0 = x1; + x1 = tmp; + } + if (y0 > y1) { + short tmp = y0; + y0 = y1; + y1 = tmp; + } + if (x1 < 0 || y1 < 0) + return; + xres = info->var.xres; + yres = info->var.yres; + if (x0 >= xres || y0 >= yres || x2 >= xres || y2 >= yres) + return; + w = x1 - x0 + 1; + h = y1 - y0 + 1; + if (w<=0 || h<=0) + return; + if (x2+w-1 < 0 || y2+h-1 < 0) + return; + if (x0 < 0) { + x2 -= x0; + w += x0; + x0=0; + } + if (y0 < 0) { + y2 -= y0; + h += y0; + y0=0; + } + if (x1 >= xres) { + short diff = x1 - xres + 1; + w -= diff; + x1 = xres-1; + } + if (y1 >= yres) { + short diff = y1 - yres + 1; + h -= diff; + y1 = yres-1; + } + if (x2+w-1 >= xres) { + short diff = x2 + w - xres; + w -= diff; + } + if (y2+h-1 >= yres) { + short diff = y2+h - yres; + h -= diff; + } + if (p->clip_valid) { + if (x1 < p->clip_x0 || y1 < p->clip_y0) + return; + if (x2+w-1 < p->clip_x0 || y2+h-1 < p->clip_y0) + return; + if (x0 > p->clip_x1 || y0 > p->clip_y1) + return; + if (x2 > p->clip_x1 || y2 > p->clip_y1) + return; + if (x0 < p->clip_x0) { + short diff = p->clip_x0 - x0; + x2 += diff; + w -= diff; + x0 = p->clip_x0; + } + if (y0 < p->clip_y0) { + short diff = p->clip_y0 - y0; + y2 += diff; + h -= diff; + y0 = p->clip_y0; + } + if (x1 > p->clip_x1) { + short diff = x1 - p->clip_x1; + w -= diff; + x1 = p->clip_x1; + } + if (y1 > p->clip_y1) { + short diff = y1 - p->clip_y1; + h -= diff; + y1 = p->clip_y1; + } + if (x2 < p->clip_x0) { + short diff = p->clip_x0 - x2; + x0 += diff; + w -= diff; + x2 = p->clip_x0; + } + if (y2 < p->clip_y0) { + short diff = p->clip_y0 - y2; + y0 += diff; + h -= diff; + y2 = p->clip_y0; + } + if (x2+w-1 > p->clip_x1) { + short diff = x2+w-1 - p->clip_x1; + w -= diff; + } + if (y2+h-1 > p->clip_y1) { + short diff = y2+h-1 - p->clip_y1; + h -= diff; + } + } + /*----------*/ + +/*printk (KERN_INFO "copyarea: w=%d h=%d\n", w, h);*/ + bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3; + rasterlen = info->fix.line_length; + src = dest = info->screen_base; + offset = y0 * rasterlen + x0 * bytes_per_pixel; + src += offset; + offset = y2 * rasterlen + x2 * bytes_per_pixel; + dest += offset; + stride = w * bytes_per_pixel; + + if (y2 < y0) { + int j=0; + +#define BUFSIZECOPY 4096 + u8 *buffer = NULL; + buffer = kmalloc (BUFSIZECOPY, GFP_KERNEL); + + /* moving upward */ + while (j < h) { + if (!buffer) + fb_enh_copy_within (dest,src,stride); + else { + u8 *d = dest; + u8 *s = src; + long st = stride; + while (st > 0) { + u32 len = st > BUFSIZECOPY ? + BUFSIZECOPY : st; + memcpy_fromio (buffer, s, len); + memcpy_toio (d, buffer, len); + st -= len; + s += len; + d += len; + } + } + dest += rasterlen; + src += rasterlen; + j++; + } + if (buffer) + kfree (buffer); + } else + if (y2 > y0) { + u8 *buffer = NULL; + int j=h-1; + + buffer = kmalloc (BUFSIZECOPY, GFP_KERNEL); + + /* moving downward */ + dest += rasterlen * h; + src += rasterlen * h; + while (j >= 0) { + dest -= rasterlen; + src -= rasterlen; + if (!buffer) + fb_enh_copy_within (dest,src,stride); + else { + u8 *d = dest; + u8 *s = src; + long st = stride; + while (st > 0) { + u32 len = st > BUFSIZECOPY ? + BUFSIZECOPY : st; + memcpy_fromio (buffer, s, len); + memcpy_toio (d, buffer, len); + st -= len; + s += len; + d += len; + } + } + j--; + } + kfree (buffer); + } else { + int j=0; + /* y equal */ + + u8 *buffer = NULL; + buffer = kmalloc (BUFSIZECOPY, GFP_KERNEL); + + /* moving upward */ + while (j < h) { + if (!buffer) + fb_enh_copy_within (dest,src,stride); + else { + u8 *d = dest; + u8 *s = src; + long st = stride; + while (st > 0) { + u32 len = st > BUFSIZECOPY ? + BUFSIZECOPY : st; + memcpy_fromio (buffer, s, len); + memcpy_toio (d, buffer, len); + st -= len; + s += len; + d += len; + } + } + dest += rasterlen; + src += rasterlen; + j++; + } + kfree (buffer); + } +} + + + +static void init_bresenham (struct fb_dda *p, short x0, short y0, short x1, short y1) +{ + if (!p) + return; + + p->x = p->xprev = x0; + p->y = p->yprev = y0; + p->s1 = 1; + p->s2 = 1; + + p->dx = x1 - x0; + if (p->dx < 0) { + p->dx = -p->dx; + p->s1 = -1; + } + + p->dy = y1 - y0; + if (p->dy < 0) { + p->dy = -p->dy; + p->s2 = -1; + } + + p->xchange = 0; + + if (p->dy > p->dx) { + int tmp = p->dx; + p->dx = p->dy; + p->dy = tmp; + p->xchange = 1; + } + + p->e = (p->dy<<1) - p->dx; + p->j = 0; +} + + +static void fb_enh_filltriangle (struct fb_info *info, struct fb_draw *p) +{ + struct fb_dda left; + struct fb_dda right; + struct fb_draw p2; + short xres, yres; + + if (!info || !p) + return; + yres = info->var.yres; + if (p->x0 < 0 && p->x1 < 0 && p->x2 < 0) + return; + if (p->y0 < 0 && p->y1 < 0 && p->y2 < 0) + return; + if (p->y0 > p->y1) { + short tmp = p->x0; p->x0 = p->x1; p->x1 = tmp; + tmp = p->y0; p->y0 = p->y1; p->y1 = tmp; + } + if (p->y1 > p->y2) { + short tmp = p->x2; p->x2 = p->x1; p->x1 = tmp; + tmp = p->y2; p->y2 = p->y1; p->y1 = tmp; + } + if (p->y0 > p->y1) { + short tmp = p->x0; p->x0 = p->x1; p->x1 = tmp; + tmp = p->y0; p->y0 = p->y1; p->y1 = tmp; + } + if (p->y1 > p->y2) { + short tmp = p->x2; p->x2 = p->x1; p->x1 = tmp; + tmp = p->y2; p->y2 = p->y1; p->y1 = tmp; + } + xres = info->var.xres; + yres = info->var.yres; + if ((p->x0 >= xres && p->x1 >= xres && p->x2 >= xres) || + (p->y0 >= yres && p->y1 >= yres && p->y2 >= yres)) + return; + /*----------*/ + + init_bresenham (&left, p->x0, p->y0, p->x1, p->y1); + init_bresenham (&right, p->x0, p->y0, p->x2, p->y2); + + if ((p2.clip_valid = p->clip_valid)) { + p2.clip_x0 = p->clip_x0; + p2.clip_y0 = p->clip_y0; + p2.clip_x1 = p->clip_x1; + p2.clip_y1 = p->clip_y1; + } + + while (1) { + char got_ychange = 0; + + if (left.xprev < right.xprev) { + p2.x0 = left.xprev; + p2.x1 = right.xprev; + } else { + p2.x1 = left.xprev; + p2.x0 = right.xprev; + } + p2.y0 = p2.y1 = left.yprev; + p2.color = p->color; + info->enhanced_ops->fb_fillrect (info, &p2); + + /* Advance the left line */ + left.xprev = left.x; + left.yprev = left.y; + + while (!got_ychange) { + if (left.j == left.dx) { + if (left.y == p->y2) + return; + init_bresenham (&left, p->x1, p->y1, p->x2, p->y2); + } + left.j++; + if (left.e >= 0) { + if (left.xchange) + left.x += left.s1; + else { + got_ychange = 1; + left.y += left.s2; + } + left.e -= (left.dx << 1); + } + if (left.xchange) { + got_ychange = 1; + left.y += lef... [truncated message content] |