From 056e1ebed94379db41ebb2e40cc88a873cfb4411 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Wed, 8 Apr 2009 10:17:09 +0000 Subject: initial commit of netsurf framebuffer library svn path=/trunk/libnsfb/; revision=7060 --- src/16bpp_plotters.c | 569 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/1bpp_plotters.c | 266 ++++++++++++++++++++++++ src/24bpp_plotters.c | 469 ++++++++++++++++++++++++++++++++++++++++++ src/32bpp_plotters.c | 320 +++++++++++++++++++++++++++++ src/8bpp_plotters.c | 469 ++++++++++++++++++++++++++++++++++++++++++ src/Makefile | 6 + src/frontend.c | 98 +++++++++ src/frontend_linux.c | 9 + src/frontend_sdl.c | 105 ++++++++++ src/libnsfb.c | 71 +++++++ src/plot.c | 114 +++++++++++ src/plot_util.c | 150 ++++++++++++++ src/plotters.c | 522 ++++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 3168 insertions(+) create mode 100644 src/16bpp_plotters.c create mode 100644 src/1bpp_plotters.c create mode 100644 src/24bpp_plotters.c create mode 100644 src/32bpp_plotters.c create mode 100644 src/8bpp_plotters.c create mode 100644 src/Makefile create mode 100644 src/frontend.c create mode 100644 src/frontend_linux.c create mode 100644 src/frontend_sdl.c create mode 100644 src/libnsfb.c create mode 100644 src/plot.c create mode 100644 src/plot_util.c create mode 100644 src/plotters.c (limited to 'src') diff --git a/src/16bpp_plotters.c b/src/16bpp_plotters.c new file mode 100644 index 0000000..b97c42d --- /dev/null +++ b/src/16bpp_plotters.c @@ -0,0 +1,569 @@ +/* + * Copyright 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "utils/log.h" +#include "utils/utf8.h" +#include "desktop/plotters.h" + +#include "framebuffer/fb_gui.h" +#include "framebuffer/fb_plotters.h" +#include "framebuffer/fb_bitmap.h" +#include "framebuffer/fb_font.h" + +static inline uint16_t * +fb_16bpp_get_xy_loc(int x, int y) +{ + return (void *)(framebuffer->ptr + + (y * framebuffer->linelen) + + (x << 1)); +} + +static inline colour fb_16bpp_to_colour(uint16_t pixel) +{ + return ((pixel & 0x1F) << 19) | + ((pixel & 0x7E0) << 5) | + ((pixel & 0xF800) >> 8); +} + +/* convert a colour value to a 16bpp pixel value ready for screen output */ +static inline uint16_t fb_colour_to_pixel(colour c) +{ + return ((c & 0xF8) << 8) | ((c & 0xFC00 ) >> 5) | ((c & 0xF80000) >> 19); +} + +#define SIGN(x) ((x<0) ? -1 : ((x>0) ? 1 : 0)) + +static bool fb_16bpp_line(int x0, int y0, int x1, int y1, int width, + colour c, bool dotted, bool dashed) +{ + int w; + uint16_t ent; + uint16_t *pvideo; + + int x, y, i; + int dx, dy, sdy; + int dxabs, dyabs; + + /*LOG(("%d, %d, %d, %d, %d, 0x%lx, %d, %d", + x0,y0,x1,y1,width,c,dotted,dashed));*/ + + if (y1 > fb_plot_ctx.y1) + return true; + if (y0 < fb_plot_ctx.y0) + return true; + + ent = fb_colour_to_pixel(c); + + if (y0 == y1) { + /* horizontal line special cased */ + if (!fb_plotters_clip_rect_ctx(&x0, &y0, &x1, &y1)) + return true; /* line outside clipping */ + + /*LOG(("horiz: %d, %d, %d, %d, %d, 0x%lx, %d, %d", + x0,y0,x1,y1,width,c,dotted,dashed));*/ + + pvideo = fb_16bpp_get_xy_loc(x0, y0); + + w = x1 - x0; + while (w-- > 0) { + *(pvideo + w) = ent; + } + return true; + } else { + /* standard bresenham line */ + if (!fb_plotters_clip_line_ctx(&x0, &y0, &x1, &y1)) + return true; /* line outside clipping */ + + //LOG(("%d, %d, %d, %d", x0,y0,x1,y1)); + + /* the horizontal distance of the line */ + dx = x1 - x0; + dxabs = abs (dx); + + /* the vertical distance of the line */ + dy = y1 - y0; + dyabs = abs (dy); + + sdy = dx ? SIGN(dy) * SIGN(dx) : SIGN(dy); + + if (dx >= 0) + pvideo = fb_16bpp_get_xy_loc(x0, y0); + else + pvideo = fb_16bpp_get_xy_loc(x1, y1); + + x = dyabs >> 1; + y = dxabs >> 1; + + if (dxabs >= dyabs) { + /* the line is more horizontal than vertical */ + for (i = 0; i <= dxabs; i++) { + *pvideo = ent; + + pvideo++; + y += dyabs; + if (y > dxabs) { + y -= dxabs; + pvideo += sdy * (framebuffer->linelen>>1); + } + } + } else { + /* the line is more vertical than horizontal */ + for (i = 0; i <= dyabs; i++) { + *pvideo = ent; + pvideo += sdy * (framebuffer->linelen >> 1); + + x += dxabs; + if (x > dyabs) { + x -= dyabs; + pvideo++; + } + } + } + + } + + + + return true; +} + +static bool fb_16bpp_rectangle(int x0, int y0, int width, int height, + int line_width, colour c, bool dotted, bool dashed) +{ + fb_16bpp_line(x0, y0, x0 + width, y0, line_width, c, dotted, dashed); + fb_16bpp_line(x0, y0 + height, x0 + width, y0 + height, line_width, c, dotted, dashed); + fb_16bpp_line(x0, y0, x0, y0 + height, line_width, c, dotted, dashed); + fb_16bpp_line(x0 + width, y0, x0 + width, y0 + height, line_width, c, dotted, dashed); + return true; +} + +static bool fb_16bpp_polygon(const int *p, unsigned int n, colour fill) +{ + return fb_plotters_polygon(p, n, fill, fb_16bpp_line); +} + + +static bool fb_16bpp_fill(int x0, int y0, int x1, int y1, colour c) +{ + int w; + uint16_t *pvid16; + uint16_t ent16; + uint32_t *pvid32; + uint32_t ent32; + uint32_t llen; + uint32_t width; + uint32_t height; + + if (!fb_plotters_clip_rect_ctx(&x0, &y0, &x1, &y1)) + return true; /* fill lies outside current clipping region */ + + ent16 = fb_colour_to_pixel(c); + width = x1 - x0; + height = y1 - y0; + + pvid16 = fb_16bpp_get_xy_loc(x0, y0); + + if (((x0 & 1) == 0) && ((width & 1) == 0)) { + /* aligned to 32bit value and width is even */ + width = width >> 1; + llen = (framebuffer->linelen >> 2) - width; + ent32 = ent16 | (ent16 << 16); + pvid32 = (uint32_t *)pvid16; + + while (height-- > 0) { + w = width; + while (w >= 16) { + *pvid32++ = ent32; *pvid32++ = ent32; + *pvid32++ = ent32; *pvid32++ = ent32; + *pvid32++ = ent32; *pvid32++ = ent32; + *pvid32++ = ent32; *pvid32++ = ent32; + *pvid32++ = ent32; *pvid32++ = ent32; + *pvid32++ = ent32; *pvid32++ = ent32; + *pvid32++ = ent32; *pvid32++ = ent32; + *pvid32++ = ent32; *pvid32++ = ent32; + w-=16; + } + while (w >= 4) { + *pvid32++ = ent32; *pvid32++ = ent32; + *pvid32++ = ent32; *pvid32++ = ent32; + w-=4; + } + while (w > 0) { + *pvid32++ = ent32; + w--; + } + // for (w = width; w > 0; w--) *pvid32++ = ent32; + pvid32 += llen; + } + + } else { + llen = (framebuffer->linelen >> 1) - width; + + + while (height-- > 0) { + for (w = width; w > 0; w--) *pvid16++ = ent16; + pvid16 += llen; + } + } + return true; +} + +static bool fb_16bpp_clg(colour c) +{ + /* LOG(("c %lx", c)); */ + fb_16bpp_fill(fb_plot_ctx.x0, + fb_plot_ctx.y0, + fb_plot_ctx.x1, + fb_plot_ctx.y1, + c); + return true; +} + +#ifdef FB_USE_FREETYPE + +static bool +fb_16bpp_draw_ft_monobitmap(FT_Bitmap *bp, int x, int y, colour c) +{ + return false; +} + +static bool +fb_16bpp_draw_ft_bitmap(FT_Bitmap *bp, int x, int y, colour c) +{ + uint16_t *pvideo; + uint8_t *pixel = (uint8_t *)bp->buffer; + colour abpixel; /* alphablended pixel */ + int xloop, yloop; + int x0,y0,x1,y1; + int xoff, yoff; /* x and y offset into image */ + int height = bp->rows; + int width = bp->width; + uint32_t fgcol; + + /* The part of the scaled image actually displayed is cropped to the + * current context. + */ + x0 = x; + y0 = y; + x1 = x + width; + y1 = y + height; + + if (!fb_plotters_clip_rect_ctx(&x0, &y0, &x1, &y1)) + return true; + + if (height > (y1 - y0)) + height = (y1 - y0); + + if (width > (x1 - x0)) + width = (x1 - x0); + + xoff = x0 - x; + yoff = y0 - y; + + /* plot the image */ + pvideo = fb_16bpp_get_xy_loc(x0, y0); + + fgcol = c & 0xFFFFFF; + + for (yloop = 0; yloop < height; yloop++) { + for (xloop = 0; xloop < width; xloop++) { + abpixel = (pixel[((yoff + yloop) * bp->pitch) + xloop + xoff] << 24) | fgcol; + if ((abpixel & 0xFF000000) != 0) { + if ((abpixel & 0xFF000000) != 0xFF000000) { + abpixel = fb_plotters_ablend(abpixel, + fb_16bpp_to_colour(*(pvideo + xloop))); + } + + *(pvideo + xloop) = fb_colour_to_pixel(abpixel); + + } + } + pvideo += (framebuffer->linelen >> 1); + } + + return true; +} + +static bool fb_16bpp_text(int x, int y, const struct css_style *style, + const char *text, size_t length, colour bg, colour c) +{ + uint32_t ucs4; + size_t nxtchr = 0; + FT_Glyph glyph; + FT_BitmapGlyph bglyph; + + while (nxtchr < length) { + ucs4 = utf8_to_ucs4(text + nxtchr, length - nxtchr); + nxtchr = utf8_next(text, length, nxtchr); + + glyph = fb_getglyph(style, ucs4); + if (glyph == NULL) + continue; + if (glyph->format == FT_GLYPH_FORMAT_BITMAP) { + bglyph = (FT_BitmapGlyph)glyph; + + /* now, draw to our target surface */ + if (bglyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { + fb_16bpp_draw_ft_monobitmap(&bglyph->bitmap, + x + bglyph->left, + y - bglyph->top, + c); + } else { + fb_16bpp_draw_ft_bitmap(&bglyph->bitmap, + x + bglyph->left, + y - bglyph->top, + c); + } + } + x += glyph->advance.x >> 16; + + } + return true; +} + +#else +static bool fb_16bpp_text(int x, int y, const struct css_style *style, + const char *text, size_t length, colour bg, colour c) +{ + const struct fb_font_desc* fb_font = fb_get_font(style); + const uint32_t *font_data; + int xloop, yloop; + uint32_t row; + size_t chr; + + uint16_t *pvideo; + uint16_t fgcol; + uint16_t bgcol; + + unsigned char *buffer = NULL; + int x0,y0,x1,y1; + int xoff, yoff; /* x and y offset into image */ + int height = fb_font->height; + + /* aquire thge text in local font encoding */ + utf8_to_font_encoding(fb_font, text, length, (char **)&buffer); + if (!buffer) + return true; + length = strlen((char *)buffer); + + + /* y is given to the fonts baseline we need it to the fonts top */ + y-=((fb_font->height * 75)/100); + + y+=1; /* the coord is the bottom-left of the pixels offset by 1 to make + * it work since fb coords are the top-left of pixels + */ + + /* The part of the text displayed is cropped to the current context. */ + x0 = x; + y0 = y; + x1 = x + (fb_font->width * length); + y1 = y + fb_font->height; + + if (!fb_plotters_clip_rect_ctx(&x0, &y0, &x1, &y1)) + return true; /* text lies outside current clipping region */ + + /* find width and height to plot */ + if (height > (y1 - y0)) + height = (y1 - y0); + + xoff = x0 - x; + yoff = y0 - y; + + fgcol = fb_colour_to_pixel(c); + + bgcol = fb_colour_to_pixel(bg); + + /*LOG(("x %d, y %d, style %p, txt %.*s , len %d, bg 0x%lx, fg 0x%lx", + x,y,style,length,text,length,bg,c));*/ + + for (chr = 0; chr < length; chr++, x += fb_font->width) { + if ((x + fb_font->width) > x1) + break; + + if (x < x0) + continue; + + pvideo = fb_16bpp_get_xy_loc(x, y0); + + /* move our font-data to the correct position */ + font_data = fb_font->data + (buffer[chr] * fb_font->height); + + for (yloop = 0; yloop < height; yloop++) { + row = font_data[yoff + yloop]; + for (xloop = fb_font->width; xloop > 0 ; xloop--) { + if ((row & 1) != 0) + *(pvideo + xloop) = fgcol; + row = row >> 1; + } + pvideo += (framebuffer->linelen >> 1); + } + } + + free(buffer); + return true; +} +#endif + +static bool fb_16bpp_disc(int x, int y, int radius, colour c, bool filled) +{ + LOG(("x %d, y %d, r %d, c 0x%lx, fill %d", + x, y, radius, (unsigned long)c, filled)); + + return true; +} + +static bool fb_16bpp_arc(int x, int y, int radius, int angle1, int angle2, + colour c) +{ + LOG(("x %d, y %d, r %d, a1 %d, a2 %d, c 0x%lx", + x, y, radius, angle1, angle2, (unsigned long)c)); + return true; +} + + +static bool fb_16bpp_bitmap(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg, + struct content *content) +{ + uint16_t *pvideo; + colour *pixel = (colour *)bitmap->pixdata; + colour abpixel; /* alphablended pixel */ + int xloop, yloop; + int x0,y0,x1,y1; + int xoff, yoff; /* x and y offset into image */ + + + /* TODO here we should scale the image from bitmap->width to width, for + * now simply crop. + */ + if (width > bitmap->width) + width = bitmap->width; + + if (height > bitmap->height) + height = bitmap->height; + + /* The part of the scaled image actually displayed is cropped to the + * current context. + */ + x0 = x; + y0 = y; + x1 = x + width; + y1 = y + height; + + if (!fb_plotters_clip_rect_ctx(&x0, &y0, &x1, &y1)) + return true; + + + LOG(("%d, %d %d, %d bitmap %p, content %p", + x0,y0,x1,y1,bitmap,content)); + + if (height > (y1 - y0)) + height = (y1 - y0); + + if (width > (x1 - x0)) + width = (x1 - x0); + + + xoff = x0 - x; + yoff = (y0 - y) * bitmap->width; + height = height * bitmap->width + yoff; + + /* plot the image */ + pvideo = fb_16bpp_get_xy_loc(x0, y0); + + if (bitmap->opaque) { + for (yloop = yoff; yloop < height; yloop += bitmap->width) { + for (xloop = 0; xloop < width; xloop++) { + abpixel = pixel[yloop + xloop + xoff]; + *(pvideo + xloop) = fb_colour_to_pixel(abpixel); + } + pvideo += (framebuffer->linelen >> 1); + } + } else { + for (yloop = yoff; yloop < height; yloop += bitmap->width) { + for (xloop = 0; xloop < width; xloop++) { + abpixel = pixel[yloop + xloop + xoff]; + if ((abpixel & 0xFF000000) != 0) { + if ((abpixel & 0xFF000000) != 0xFF000000) { + abpixel = fb_plotters_ablend(abpixel, + fb_16bpp_to_colour(*(pvideo + xloop))); + } + + *(pvideo + xloop) = fb_colour_to_pixel(abpixel); + } + } + pvideo += (framebuffer->linelen >> 1); + } + } + + return true; +} + +static bool fb_16bpp_bitmap_tile(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg, + bool repeat_x, bool repeat_y, + struct content *content) +{ + return fb_plotters_bitmap_tile(x, y, width, height, + bitmap, bg, repeat_x, repeat_y, + content, fb_16bpp_bitmap); +} + +static bool fb_16bpp_flush(void) +{ + LOG(("optional")); + return true; +} + +static bool fb_16bpp_path(const float *p, unsigned int n, colour fill, float width, + colour c, const float transform[6]) +{ + LOG(("%f, %d, 0x%lx, %f, 0x%lx, %f", + *p, n, (unsigned long)fill, width, (unsigned long)c, *transform)); + return true; +} + + +const struct plotter_table framebuffer_16bpp_plot = { + .clg = fb_16bpp_clg, + .rectangle = fb_16bpp_rectangle, + .line = fb_16bpp_line, + .polygon = fb_16bpp_polygon, + .fill = fb_16bpp_fill, + .clip = fb_clip, + .text = fb_16bpp_text, + .disc = fb_16bpp_disc, + .arc = fb_16bpp_arc, + .bitmap = fb_16bpp_bitmap, + .bitmap_tile = fb_16bpp_bitmap_tile, + .flush = fb_16bpp_flush, + .path = fb_16bpp_path, + .option_knockout = true, +}; + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/src/1bpp_plotters.c b/src/1bpp_plotters.c new file mode 100644 index 0000000..bd511e7 --- /dev/null +++ b/src/1bpp_plotters.c @@ -0,0 +1,266 @@ +/* + * Copyright 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "utils/log.h" +#include "utils/utf8.h" +#include "desktop/plotters.h" + +#include "framebuffer/fb_gui.h" +#include "framebuffer/fb_plotters.h" +#include "framebuffer/fb_bitmap.h" +#include "framebuffer/fb_font.h" + +extern struct fb_info_s *fbinfo; + + +static bool fb_1bpp_rectangle(int x0, int y0, int width, int height, + int line_width, colour c, bool dotted, bool dashed) +{ + LOG(("%s(%d, %d, %d, %d, %d, 0x%lx, %d, %d)\n", __func__, + x0,y0,width,height,line_width,c,dotted,dashed)); + return true; +} + +static bool fb_1bpp_line(int x0, int y0, int x1, int y1, int width, + colour c, bool dotted, bool dashed) +{ + LOG(("%s(%d, %d, %d, %d, %d, 0x%lx, %d, %d)\n", __func__, + x0,y0,x1,y1,width,c,dotted,dashed)); + + return true; +} + +static bool fb_1bpp_polygon(const int *p, unsigned int n, colour fill) +{ + LOG(("%s(%p, %d, 0x%lx)\n", __func__, p,n,fill)); + return true; +} + + +static bool fb_1bpp_fill(int x0, int y0, int x1, int y1, colour c) +{ + int x; + int y; + int pent; + + LOG(("%s(%d, %d, %d, %d, 0x%lx)\n", __func__, + x0,y0,x1,y1,c)); + + if (c != 0) + pent = 0xff; + else + pent = 0; + + fb_plotters_clip_rect_ctx(&x0, &y0, &x1, &y1); + + x = x1 - x0; + for (y = y0; y < y1; y++) { + memset(fb_plotters_get_xy_loc(x0, y, fbinfo), pent, x); + } + return true; +} + +static bool fb_1bpp_clg(colour c) +{ + LOG(("%s(%lx)\n", __func__, c)); + fb_1bpp_fill(fb_plot_ctx.x0, + fb_plot_ctx.y0, + fb_plot_ctx.x1, + fb_plot_ctx.y1, + c); + return true; +} + + +static bool fb_1bpp_text(int x, int y, const struct css_style *style, + const char *text, size_t length, colour bg, colour c) +{ + const struct fb_font_desc* fb_font = fb_get_font(style); + u8_t *video_char_start; + const u8_t *font_data; + int yloop; + unsigned char row; + int chr; + + LOG(("%s(%d, %d, %p, %.*s , %d, 0x%lx, 0x%lx)\n", __func__, + x,y,style,length,text,length,bg,c)); + + for (chr=0; chr < length; chr++) { + video_char_start = fb_plotters_get_xy_loc(x + (chr * (fb_font->width)), y, fbinfo); + + /* move our font-data to the correct position */ + font_data = fb_font->data + (text[chr] * fb_font->height); + + for (yloop = 0; yloop < fb_font->height; yloop++) { + row = font_data[yloop]; + *video_char_start = row; + video_char_start += fbinfo->line_len; + } + } + return true; + + + /* copied from css/css.h - need to open the correct font here + * font properties * + css_font_family font_family; + struct { + css_font_size_type size; + union { + struct css_length length; + float absolute; + float percent; + } value; + } font_size; + css_font_style font_style; + css_font_variant font_variant; + css_font_weight font_weight; + */ + return true; +} + +static bool fb_1bpp_disc(int x, int y, int radius, colour c, bool filled) +{ + LOG(("%s(%d, %d, %d, 0x%lx, %d)\n", __func__, + x, y, radius, c, filled)); + return true; +} + +static bool fb_1bpp_arc(int x, int y, int radius, int angle1, int angle2, + colour c) +{ + LOG(("x %d, y %d, radius %d, angle1 %d, angle2 %d, c 0x%lx", + x, y, radius, angle1, angle2, c)); + return true; +} + +static inline colour ablend(colour pixel) +{ + return pixel; +} + + +static bool fb_1bpp_bitmap(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg, + struct content *content) +{ + u8_t *video_char_start; + colour *pixel = (colour *)bitmap->pixdata; + colour abpixel; /* alphablended pixel */ + int xloop,yloop; + + video_char_start = fb_plotters_get_xy_loc(x, y, fbinfo); + + for (yloop = 0; yloop < height; yloop++) { + for (xloop = 0; xloop < width; xloop++) { + abpixel = pixel[(yloop * bitmap->width) + xloop]; + if ((abpixel & 0xFF000000) != 0) { + if ((abpixel & 0xFF000000) != 0xFF) + abpixel = ablend(abpixel); + if (abpixel == 0) + video_char_start[xloop] |= (1 << (xloop % 8)); + else + video_char_start[xloop] &= ~(1 << (xloop % 8)); + + } + } + video_char_start += fbinfo->line_len; + } + + return true; +} + +static bool fb_1bpp_bitmap_tile(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg, + bool repeat_x, bool repeat_y, + struct content *content) +{ + unsigned long xf,yf,wf,hf; + + if (!(repeat_x || repeat_y)) { + /* Not repeating at all, so just pass it on */ + return fb_1bpp_bitmap(x,y,width,height,bitmap,bg,content); + } + + for (xf = 0; xf < width; xf += bitmap->width) { + for(yf = 0;yf < height; yf += bitmap->height) { + if(width > xf+bitmap->width) + { + wf = width-(xf+bitmap->width); + } + else + { + wf=bitmap->width; + } + + if(height > yf+bitmap->height) + { + hf = height-(yf+bitmap->height); + } + else + { + hf=bitmap->height; + } + + fb_1bpp_bitmap(x+xf, y+yf, wf, hf, bitmap, bg, content); + + } + } + + return true; +} + +static bool fb_1bpp_flush(void) +{ + LOG(("%s()\n", __func__)); + return true; +} + +static bool fb_1bpp_path(const float *p, unsigned int n, colour fill, float width, + colour c, const float transform[6]) +{ + LOG(("%s(%f, %d, 0x%lx, %f, 0x%lx, %f)\n", __func__, + *p, n, fill, width, c, *transform)); + return true; +} + +const struct plotter_table framebuffer_1bpp_plot = { + .clg = fb_1bpp_clg, + .rectangle = fb_1bpp_rectangle, + .line = fb_1bpp_line, + .polygon = fb_1bpp_polygon, + .fill = fb_1bpp_fill, + .clip = fb_clip, + .text = fb_1bpp_text, + .disc = fb_1bpp_disc, + .arc = fb_1bpp_arc, + .bitmap = fb_1bpp_bitmap, + .bitmap_tile = fb_1bpp_bitmap_tile, + .flush = fb_1bpp_flush, + .path = fb_1bpp_path +}; + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/src/24bpp_plotters.c b/src/24bpp_plotters.c new file mode 100644 index 0000000..777b896 --- /dev/null +++ b/src/24bpp_plotters.c @@ -0,0 +1,469 @@ +/* + * Copyright 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "utils/log.h" +#include "utils/utf8.h" +#include "desktop/plotters.h" + +#include "framebuffer/fb_gui.h" +#include "framebuffer/fb_plotters.h" +#include "framebuffer/fb_bitmap.h" +#include "framebuffer/fb_font.h" + +static inline uint8_t * +fb_8bpp_get_xy_loc(int x, int y) +{ + return (uint8_t *)(framebuffer->ptr + + (y * framebuffer->linelen) + + (x)); +} + + +static bool fb_8bpp_line(int x0, int y0, int x1, int y1, int width, + colour c, bool dotted, bool dashed) +{ + LOG(("%d, %d, %d, %d, %d, 0x%lx, %d, %d", + x0, y0, x1, y1, width, (unsigned long)c, dotted, dashed)); + + return true; +} + +static bool fb_8bpp_rectangle(int x0, int y0, int width, int height, + int line_width, colour c, bool dotted, bool dashed) +{ + fb_8bpp_line(x0, y0, x0 + width, y0, line_width, c, dotted, dashed); + fb_8bpp_line(x0, y0 + height, x0 + width, y0 + height, line_width, c, dotted, dashed); + fb_8bpp_line(x0, y0, x0, y0 + height, line_width, c, dotted, dashed); + fb_8bpp_line(x0 + width, y0, x0 + width, y0 + height, line_width, c, dotted, dashed); + return true; +} + +static bool fb_8bpp_polygon(const int *p, unsigned int n, colour fill) +{ + /*LOG(("%p, %d, 0x%lx", p,n,fill));*/ + return fb_plotters_polygon(p, n, fill, fb_8bpp_line); +} + +static colour calc_colour(uint8_t c) +{ + return framebuffer->palette[c]; +} + + +static int +find_closest_palette_entry(colour c) +{ + colour palent; + int col; + + int dr, dg, db; /* delta red, green blue values */ + + int cur_distance; + int best_distance = INT_MAX; + int best_col = 0; + + for (col = 0; col < 256; col++) { + palent = framebuffer->palette[col]; + + dr = (c & 0xFF) - (palent & 0xFF); + dg = ((c >> 8) & 0xFF) - ((palent >> 8) & 0xFF); + db = ((c >> 16) & 0xFF) - ((palent >> 16) & 0xFF); + cur_distance = ((dr * dr) + (dg * dg) + (db *db)); + if (cur_distance < best_distance) { + best_distance = cur_distance; + best_col = col; + } + } + + return best_col; +} + +static inline uint8_t fb_colour_to_pixel(colour c) +{ + return find_closest_palette_entry(c); +} + +static inline colour fb_8bpp_to_colour(uint8_t pixel) +{ + return framebuffer->palette[pixel]; +} + +static bool fb_8bpp_fill(int x0, int y0, int x1, int y1, colour c) +{ + int y; + uint8_t ent; + uint8_t *pvideo; + + if (!fb_plotters_clip_rect_ctx(&x0, &y0, &x1, &y1)) + return true; /* fill lies outside current clipping region */ + + pvideo = fb_8bpp_get_xy_loc(x0, y0); + + ent = find_closest_palette_entry(c); + + for (y = y0; y < y1; y++) { + memset(pvideo, ent, x1 - x0); + pvideo += framebuffer->linelen; + } + + return true; +} + +static bool fb_8bpp_clg(colour c) +{ + LOG(("colour %lx", (unsigned long)c)); + fb_8bpp_fill(fb_plot_ctx.x0, + fb_plot_ctx.y0, + fb_plot_ctx.x1, + fb_plot_ctx.y1, + c); + return true; +} + +#ifdef FB_USE_FREETYPE + +static bool +fb_8bpp_draw_ft_monobitmap(FT_Bitmap *bp, int x, int y, colour c) +{ + return false; +} + +static bool +fb_8bpp_draw_ft_bitmap(FT_Bitmap *bp, int x, int y, colour c) +{ + uint8_t *pvideo; + uint8_t *pixel = (uint8_t *)bp->buffer; + colour abpixel; /* alphablended pixel */ + int xloop, yloop; + int x0,y0,x1,y1; + int xoff, yoff; /* x and y offset into image */ + int height = bp->rows; + int width = bp->width; + uint32_t fgcol; + + /* The part of the scaled image actually displayed is cropped to the + * current context. + */ + x0 = x; + y0 = y; + x1 = x + width; + y1 = y + height; + + if (!fb_plotters_clip_rect_ctx(&x0, &y0, &x1, &y1)) + return true; + + if (height > (y1 - y0)) + height = (y1 - y0); + + if (width > (x1 - x0)) + width = (x1 - x0); + + xoff = x0 - x; + yoff = y0 - y; + + /* plot the image */ + pvideo = fb_8bpp_get_xy_loc(x0, y0); + + fgcol = c & 0xFFFFFF; + + for (yloop = 0; yloop < height; yloop++) { + for (xloop = 0; xloop < width; xloop++) { + abpixel = (pixel[((yoff + yloop) * bp->pitch) + xloop + xoff] << 24) | fgcol; + if ((abpixel & 0xFF000000) != 0) { + if ((abpixel & 0xFF000000) != 0xFF000000) { + abpixel = fb_plotters_ablend(abpixel, + fb_8bpp_to_colour(*(pvideo + xloop))); + } + + *(pvideo + xloop) = fb_colour_to_pixel(abpixel); + + } + } + pvideo += framebuffer->linelen; + } + + return true; +} + +static bool fb_8bpp_text(int x, int y, const struct css_style *style, + const char *text, size_t length, colour bg, colour c) +{ + uint32_t ucs4; + size_t nxtchr = 0; + FT_Glyph glyph; + FT_BitmapGlyph bglyph; + + while (nxtchr < length) { + ucs4 = utf8_to_ucs4(text + nxtchr, length - nxtchr); + nxtchr = utf8_next(text, length, nxtchr); + + glyph = fb_getglyph(style, ucs4); + if (glyph == NULL) + continue; + if (glyph->format == FT_GLYPH_FORMAT_BITMAP) { + bglyph = (FT_BitmapGlyph)glyph; + + /* now, draw to our target surface */ + if (bglyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { + fb_8bpp_draw_ft_monobitmap(&bglyph->bitmap, + x + bglyph->left, + y - bglyph->top, + c); + } else { + fb_8bpp_draw_ft_bitmap(&bglyph->bitmap, + x + bglyph->left, + y - bglyph->top, + c); + } + } + x += glyph->advance.x >> 16; + + } + return true; + +} +#else +static bool fb_8bpp_text(int x, int y, const struct css_style *style, + const char *text, size_t length, colour bg, colour c) +{ + const struct fb_font_desc* fb_font = fb_get_font(style); + const uint32_t *font_data; + uint32_t row; + + int xloop, yloop; + size_t chr; + + uint8_t *pvideo; + uint8_t fgcol; + + unsigned char *buffer = NULL; + int x0,y0,x1,y1; + int xoff, yoff; /* x and y offset into image */ + int height = fb_font->height; + + /* aquire thge text in local font encoding */ + utf8_to_font_encoding(fb_font, text, length, (char **)&buffer); + if (!buffer) + return true; + length = strlen((char *)buffer); + + + /* y is given to the fonts baseline we need it to the fonts top */ + y-=((fb_font->height * 75)/100); + + y+=1; /* the coord is the bottom-left of the pixels offset by 1 to make + * it work since fb coords are the top-left of pixels + */ + + /* The part of the text displayed is cropped to the current context. */ + x0 = x; + y0 = y; + x1 = x + (fb_font->width * length); + y1 = y + fb_font->height; + + if (!fb_plotters_clip_rect_ctx(&x0, &y0, &x1, &y1)) + return true; /* text lies outside current clipping region */ + + /* find width and height to plot */ + if (height > (y1 - y0)) + height = (y1 - y0); + + xoff = x0 - x; + yoff = y0 - y; + + fgcol = find_closest_palette_entry(c); + + /*LOG(("x %d, y %d, style %p, txt %.*s , len %d, bg 0x%lx, fg 0x%lx", + x,y,style,length,text,length,bg,c));*/ + + for (chr = 0; chr < length; chr++, x += fb_font->width) { + if ((x + fb_font->width) > x1) + break; + + if (x < x0) + continue; + + pvideo = fb_8bpp_get_xy_loc(x, y0); + + /* move our font-data to the correct position */ + font_data = fb_font->data + (buffer[chr] * fb_font->height); + + for (yloop = 0; yloop < height; yloop++) { + row = font_data[yoff + yloop]; + for (xloop = fb_font->width; xloop > 0 ; xloop--) { + if ((row & 1) != 0) + *(pvideo + xloop) = fgcol; + row = row >> 1; + } + pvideo += framebuffer->linelen; + } + } + + free(buffer); + return true; +} +#endif + +static bool fb_8bpp_disc(int x, int y, int radius, colour c, bool filled) +{ + LOG(("x %d, y %d, rad %d, c 0x%lx, fill %d", x, y, radius, (unsigned long)c, filled)); + return true; +} + +static bool fb_8bpp_arc(int x, int y, int radius, int angle1, int angle2, + colour c) +{ + LOG(("x %d, y %d, radius %d, angle1 %d, angle2 %d, c 0x%lx", + x, y, radius, angle1, angle2, (unsigned long)c)); + return true; +} + + + +static bool fb_8bpp_bitmap(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg, + struct content *content) +{ + uint8_t *pvideo; + colour *pixel = (colour *)bitmap->pixdata; + colour abpixel; /* alphablended pixel */ + int xloop, yloop; + int x0,y0,x1,y1; + int xoff, yoff; /* x and y offset into image */ + + /* LOG(("x %d, y %d, width %d, height %d, bitmap %p, content %p", + x,y,width,height,bitmap,content));*/ + + /* TODO here we should scale the image from bitmap->width to width, for + * now simply crop. + */ + if (width > bitmap->width) + width = bitmap->width; + + if (height > bitmap->height) + height = bitmap->height; + + /* The part of the scaled image actually displayed is cropped to the + * current context. + */ + x0 = x; + y0 = y; + x1 = x + width; + y1 = y + height; + + if (!fb_plotters_clip_rect_ctx(&x0, &y0, &x1, &y1)) + return true; + + if (height > (y1 - y0)) + height = (y1 - y0); + + if (width > (x1 - x0)) + width = (x1 - x0); + + xoff = x0 - x; + yoff = (y0 - y) * bitmap->width; + height = height * bitmap->width + yoff; + + /* plot the image */ + pvideo = fb_8bpp_get_xy_loc(x0, y0); + + if (bitmap->opaque) { + for (yloop = yoff; yloop < height; yloop += bitmap->width) { + for (xloop = 0; xloop < width; xloop++) { + abpixel = pixel[yloop + xloop + xoff]; + *(pvideo + xloop) = fb_colour_to_pixel(abpixel); + } + pvideo += framebuffer->linelen; + } + } else { + for (yloop = yoff; yloop < height; yloop += bitmap->width) { + for (xloop = 0; xloop < width; xloop++) { + abpixel = pixel[yloop + xloop + xoff]; + if ((abpixel & 0xFF000000) != 0) { + if ((abpixel & 0xFF000000) != 0xFF000000) { + abpixel = fb_plotters_ablend(abpixel, + fb_8bpp_to_colour(*(pvideo + xloop))); + } + + *(pvideo + xloop) = fb_colour_to_pixel(abpixel); + } + } + pvideo += framebuffer->linelen; + } + } + + return true; +} + +static bool fb_8bpp_bitmap_tile(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg, + bool repeat_x, bool repeat_y, + struct content *content) +{ + return fb_plotters_bitmap_tile(x, y, width, height, + bitmap, bg, repeat_x, repeat_y, + content, fb_8bpp_bitmap); +} + +static bool fb_8bpp_flush(void) +{ + LOG(("%s()\n", __func__)); + return true; +} + +static bool fb_8bpp_path(const float *p, + unsigned int n, + colour fill, + float width, + colour c, + const float transform[6]) +{ + LOG(("%f, %d, 0x%lx, %f, 0x%lx, %f", + *p, n, (unsigned long)fill, width, (unsigned long)c, *transform)); + + return true; +} + +const struct plotter_table framebuffer_8bpp_plot = { + .clg = fb_8bpp_clg, + .rectangle = fb_8bpp_rectangle, + .line = fb_8bpp_line, + .polygon = fb_8bpp_polygon, + .fill = fb_8bpp_fill, + .clip = fb_clip, + .text = fb_8bpp_text, + .disc = fb_8bpp_disc, + .arc = fb_8bpp_arc, + .bitmap = fb_8bpp_bitmap, + .bitmap_tile = fb_8bpp_bitmap_tile, + .flush = fb_8bpp_flush, + .path = fb_8bpp_path, + .option_knockout = true, +}; + + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/src/32bpp_plotters.c b/src/32bpp_plotters.c new file mode 100644 index 0000000..eb6d162 --- /dev/null +++ b/src/32bpp_plotters.c @@ -0,0 +1,320 @@ +/* + * Copyright 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "libnsfb.h" +#include "nsfb.h" +#include "nsfb_plot.h" +#include "plotters.h" +#include "plot_util.h" + +/* global plot context */ +extern nsfb_t *_plot_nsfb; + +static inline uint32_t * +get_xy_loc(nsfb_t *nsfb, int x, int y) +{ + return (uint32_t *)(nsfb->ptr + (y * nsfb->linelen) + (x << 2)); +} + +#if __BYTE_ORDER == __BIG_ENDIAN +static inline nsfb_colour_t pixel_to_colour(uint32_t pixel) +{ + return (pixel >> 8) & ~0xFF000000U; +} + +/* convert a colour value to a 32bpp pixel value ready for screen output */ +static inline uint32_t colour_to_pixel(nsfb_colour_t c) +{ + return (c << 8); +} +#else /* __BYTE_ORDER == __BIG_ENDIAN */ +static inline nsfb_colour_t pixel_to_colour(uint32_t pixel) +{ + return ((pixel & 0xFF) << 16) | + ((pixel & 0xFF00)) | + ((pixel & 0xFF0000) >> 16); +} + +/* convert a colour value to a 32bpp pixel value ready for screen output */ +static inline uint32_t colour_to_pixel(nsfb_colour_t c) +{ + return ((c & 0xff0000) >> 16) | (c & 0xff00) | ((c & 0xff) << 16); +} +#endif + +#define SIGN(x) ((x<0) ? -1 : ((x>0) ? 1 : 0)) + +static bool +line(nsfb_t *nsfb, + nsfb_bbox_t *line, + int line_width, + nsfb_colour_t c, + bool dotted, + bool dashed) +{ + int w; + uint32_t ent; + uint32_t *pvideo; + int x, y, i; + int dx, dy, sdy; + int dxabs, dyabs; + + line_width = line_width; + dotted = dotted; + dashed = dashed; + + ent = colour_to_pixel(c); + + if (line->y0 == line->y1) { + /* horizontal line special cased */ + if (!nsfb_plot_clip_ctx(nsfb, line)) + return true; /* line outside clipping */ + + pvideo = get_xy_loc(nsfb, line->x0, line->y0); + + w = line->x1 - line->x0; + while (w-- > 0) + *(pvideo + w) = ent; + + return true; + } else { + /* standard bresenham line */ + if (!nsfb_plot_clip_line_ctx(nsfb, line)) + return true; /* line outside clipping */ + + /* the horizontal distance of the line */ + dx = line->x1 - line->x0; + dxabs = abs (dx); + + /* the vertical distance of the line */ + dy = line->y1 - line->y0; + dyabs = abs (dy); + + sdy = dx ? SIGN(dy) * SIGN(dx) : SIGN(dy); + + if (dx >= 0) + pvideo = get_xy_loc(nsfb, line->x0, line->y0); + else + pvideo = get_xy_loc(nsfb, line->x1, line->y1); + + x = dyabs >> 1; + y = dxabs >> 1; + + if (dxabs >= dyabs) { + /* the line is more horizontal than vertical */ + for (i = 0; i < dxabs; i++) { + *pvideo = ent; + + pvideo++; + y += dyabs; + if (y >= dxabs) { + y -= dxabs; + pvideo += sdy * (nsfb->linelen>>2); + } + } + } else { + /* the line is more vertical than horizontal */ + for (i = 0; i < dyabs; i++) { + *pvideo = ent; + pvideo += sdy * (nsfb->linelen >> 2); + + x += dxabs; + if (x >= dyabs) { + x -= dyabs; + pvideo++; + } + } + } + + } + + return true; +} + + + +static bool fill(nsfb_t *nsfb, nsfb_bbox_t *rect, nsfb_colour_t c) +{ + int w; + uint32_t *pvid; + uint32_t ent; + uint32_t llen; + uint32_t width; + uint32_t height; + + if (!nsfb_plot_clip_ctx(nsfb, rect)) + return true; /* fill lies outside current clipping region */ + + ent = colour_to_pixel(c); + width = rect->x1 - rect->x0; + height = rect->y1 - rect->y0; + llen = (nsfb->linelen >> 2) - width; + + pvid = get_xy_loc(nsfb, rect->x0, rect->y0); + + while (height-- > 0) { + w = width; + while (w >= 16) { + *pvid++ = ent; *pvid++ = ent; + *pvid++ = ent; *pvid++ = ent; + *pvid++ = ent; *pvid++ = ent; + *pvid++ = ent; *pvid++ = ent; + *pvid++ = ent; *pvid++ = ent; + *pvid++ = ent; *pvid++ = ent; + *pvid++ = ent; *pvid++ = ent; + *pvid++ = ent; *pvid++ = ent; + w-=16; + } + while (w >= 4) { + *pvid++ = ent; *pvid++ = ent; + *pvid++ = ent; *pvid++ = ent; + w-=4; + } + while (w > 0) { + *pvid++ = ent; + w--; + } + pvid += llen; + } + + return true; +} + + + + +static bool point(nsfb_t *nsfb, int x, int y, nsfb_colour_t c) +{ + uint32_t *pvideo; + + /* check point lies within clipping region */ + if ((x < nsfb->clip.x0) || + (x >= nsfb->clip.x1) || + (y < nsfb->clip.y0) || + (y >= nsfb->clip.y1)) + return true; + + pvideo = get_xy_loc(nsfb, x, y); + + if ((c & 0xFF000000) != 0) { + if ((c & 0xFF000000) != 0xFF000000) { + c = nsfb_plot_ablend(c, pixel_to_colour(*pvideo)); + } + + *pvideo = colour_to_pixel(c); + } + return true; +} + +static bool +bitmap(nsfb_t *nsfb, + nsfb_bbox_t *loc, + nsfb_colour_t *pixel, + int bmp_width, + int bmp_height, + int bmp_stride, + bool alpha) +{ + uint32_t *pvideo; + nsfb_colour_t abpixel = 0; /* alphablended pixel */ + int xloop, yloop; + int xoff, yoff; /* x and y offset into image */ + int x = loc->x0; + int y = loc->y0; + int width = loc->x1 - loc->x0; + int height = loc->y1 - loc->y0; + nsfb_bbox_t clipped; /* clipped display */ + + /* TODO here we should scale the image from bmp_width to width, for + * now simply crop. + */ + if (width > bmp_width) + width = bmp_width; + + if (height > bmp_height) + height = bmp_height; + + /* The part of the scaled image actually displayed is cropped to the + * current context. + */ + clipped.x0 = x; + clipped.y0 = y; + clipped.x1 = x + width; + clipped.y1 = y + height; + + if (!nsfb_plot_clip_ctx(nsfb, &clipped)) + return true; + + if (height > (clipped.y1 - clipped.y0)) + height = (clipped.y1 - clipped.y0); + + if (width > (clipped.x1 - clipped.x0)) + width = (clipped.x1 - clipped.x0); + + xoff = clipped.x0 - x; + yoff = (clipped.y0 - y) * bmp_width; + height = height * bmp_stride + yoff; + + /* plot the image */ + pvideo = get_xy_loc(nsfb, clipped.x0, clipped.y0); + + if (alpha) { + for (yloop = yoff; yloop < height; yloop += bmp_stride) { + for (xloop = 0; xloop < width; xloop++) { + abpixel = pixel[yloop + xloop + xoff]; + if ((abpixel & 0xFF000000) != 0) { + if ((abpixel & 0xFF000000) != 0xFF000000) { + abpixel = nsfb_plot_ablend(abpixel, + pixel_to_colour(*(pvideo + xloop))); + } + + *(pvideo + xloop) = colour_to_pixel(abpixel); + } + } + pvideo += (nsfb->linelen >> 2); + } + } else { + for (yloop = yoff; yloop < height; yloop += bmp_stride) { + for (xloop = 0; xloop < width; xloop++) { + abpixel = pixel[yloop + xloop + xoff]; + *(pvideo + xloop) = colour_to_pixel(abpixel); + } + pvideo += (nsfb->linelen >> 2); + } + } + return true; +} + + + +const nsfb_plotter_fns_t _nsfb_32bpp_plotters = { + .line = line, + .fill = fill, + .point = point, + .bitmap = bitmap, +}; + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/src/8bpp_plotters.c b/src/8bpp_plotters.c new file mode 100644 index 0000000..777b896 --- /dev/null +++ b/src/8bpp_plotters.c @@ -0,0 +1,469 @@ +/* + * Copyright 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "utils/log.h" +#include "utils/utf8.h" +#include "desktop/plotters.h" + +#include "framebuffer/fb_gui.h" +#include "framebuffer/fb_plotters.h" +#include "framebuffer/fb_bitmap.h" +#include "framebuffer/fb_font.h" + +static inline uint8_t * +fb_8bpp_get_xy_loc(int x, int y) +{ + return (uint8_t *)(framebuffer->ptr + + (y * framebuffer->linelen) + + (x)); +} + + +static bool fb_8bpp_line(int x0, int y0, int x1, int y1, int width, + colour c, bool dotted, bool dashed) +{ + LOG(("%d, %d, %d, %d, %d, 0x%lx, %d, %d", + x0, y0, x1, y1, width, (unsigned long)c, dotted, dashed)); + + return true; +} + +static bool fb_8bpp_rectangle(int x0, int y0, int width, int height, + int line_width, colour c, bool dotted, bool dashed) +{ + fb_8bpp_line(x0, y0, x0 + width, y0, line_width, c, dotted, dashed); + fb_8bpp_line(x0, y0 + height, x0 + width, y0 + height, line_width, c, dotted, dashed); + fb_8bpp_line(x0, y0, x0, y0 + height, line_width, c, dotted, dashed); + fb_8bpp_line(x0 + width, y0, x0 + width, y0 + height, line_width, c, dotted, dashed); + return true; +} + +static bool fb_8bpp_polygon(const int *p, unsigned int n, colour fill) +{ + /*LOG(("%p, %d, 0x%lx", p,n,fill));*/ + return fb_plotters_polygon(p, n, fill, fb_8bpp_line); +} + +static colour calc_colour(uint8_t c) +{ + return framebuffer->palette[c]; +} + + +static int +find_closest_palette_entry(colour c) +{ + colour palent; + int col; + + int dr, dg, db; /* delta red, green blue values */ + + int cur_distance; + int best_distance = INT_MAX; + int best_col = 0; + + for (col = 0; col < 256; col++) { + palent = framebuffer->palette[col]; + + dr = (c & 0xFF) - (palent & 0xFF); + dg = ((c >> 8) & 0xFF) - ((palent >> 8) & 0xFF); + db = ((c >> 16) & 0xFF) - ((palent >> 16) & 0xFF); + cur_distance = ((dr * dr) + (dg * dg) + (db *db)); + if (cur_distance < best_distance) { + best_distance = cur_distance; + best_col = col; + } + } + + return best_col; +} + +static inline uint8_t fb_colour_to_pixel(colour c) +{ + return find_closest_palette_entry(c); +} + +static inline colour fb_8bpp_to_colour(uint8_t pixel) +{ + return framebuffer->palette[pixel]; +} + +static bool fb_8bpp_fill(int x0, int y0, int x1, int y1, colour c) +{ + int y; + uint8_t ent; + uint8_t *pvideo; + + if (!fb_plotters_clip_rect_ctx(&x0, &y0, &x1, &y1)) + return true; /* fill lies outside current clipping region */ + + pvideo = fb_8bpp_get_xy_loc(x0, y0); + + ent = find_closest_palette_entry(c); + + for (y = y0; y < y1; y++) { + memset(pvideo, ent, x1 - x0); + pvideo += framebuffer->linelen; + } + + return true; +} + +static bool fb_8bpp_clg(colour c) +{ + LOG(("colour %lx", (unsigned long)c)); + fb_8bpp_fill(fb_plot_ctx.x0, + fb_plot_ctx.y0, + fb_plot_ctx.x1, + fb_plot_ctx.y1, + c); + return true; +} + +#ifdef FB_USE_FREETYPE + +static bool +fb_8bpp_draw_ft_monobitmap(FT_Bitmap *bp, int x, int y, colour c) +{ + return false; +} + +static bool +fb_8bpp_draw_ft_bitmap(FT_Bitmap *bp, int x, int y, colour c) +{ + uint8_t *pvideo; + uint8_t *pixel = (uint8_t *)bp->buffer; + colour abpixel; /* alphablended pixel */ + int xloop, yloop; + int x0,y0,x1,y1; + int xoff, yoff; /* x and y offset into image */ + int height = bp->rows; + int width = bp->width; + uint32_t fgcol; + + /* The part of the scaled image actually displayed is cropped to the + * current context. + */ + x0 = x; + y0 = y; + x1 = x + width; + y1 = y + height; + + if (!fb_plotters_clip_rect_ctx(&x0, &y0, &x1, &y1)) + return true; + + if (height > (y1 - y0)) + height = (y1 - y0); + + if (width > (x1 - x0)) + width = (x1 - x0); + + xoff = x0 - x; + yoff = y0 - y; + + /* plot the image */ + pvideo = fb_8bpp_get_xy_loc(x0, y0); + + fgcol = c & 0xFFFFFF; + + for (yloop = 0; yloop < height; yloop++) { + for (xloop = 0; xloop < width; xloop++) { + abpixel = (pixel[((yoff + yloop) * bp->pitch) + xloop + xoff] << 24) | fgcol; + if ((abpixel & 0xFF000000) != 0) { + if ((abpixel & 0xFF000000) != 0xFF000000) { + abpixel = fb_plotters_ablend(abpixel, + fb_8bpp_to_colour(*(pvideo + xloop))); + } + + *(pvideo + xloop) = fb_colour_to_pixel(abpixel); + + } + } + pvideo += framebuffer->linelen; + } + + return true; +} + +static bool fb_8bpp_text(int x, int y, const struct css_style *style, + const char *text, size_t length, colour bg, colour c) +{ + uint32_t ucs4; + size_t nxtchr = 0; + FT_Glyph glyph; + FT_BitmapGlyph bglyph; + + while (nxtchr < length) { + ucs4 = utf8_to_ucs4(text + nxtchr, length - nxtchr); + nxtchr = utf8_next(text, length, nxtchr); + + glyph = fb_getglyph(style, ucs4); + if (glyph == NULL) + continue; + if (glyph->format == FT_GLYPH_FORMAT_BITMAP) { + bglyph = (FT_BitmapGlyph)glyph; + + /* now, draw to our target surface */ + if (bglyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { + fb_8bpp_draw_ft_monobitmap(&bglyph->bitmap, + x + bglyph->left, + y - bglyph->top, + c); + } else { + fb_8bpp_draw_ft_bitmap(&bglyph->bitmap, + x + bglyph->left, + y - bglyph->top, + c); + } + } + x += glyph->advance.x >> 16; + + } + return true; + +} +#else +static bool fb_8bpp_text(int x, int y, const struct css_style *style, + const char *text, size_t length, colour bg, colour c) +{ + const struct fb_font_desc* fb_font = fb_get_font(style); + const uint32_t *font_data; + uint32_t row; + + int xloop, yloop; + size_t chr; + + uint8_t *pvideo; + uint8_t fgcol; + + unsigned char *buffer = NULL; + int x0,y0,x1,y1; + int xoff, yoff; /* x and y offset into image */ + int height = fb_font->height; + + /* aquire thge text in local font encoding */ + utf8_to_font_encoding(fb_font, text, length, (char **)&buffer); + if (!buffer) + return true; + length = strlen((char *)buffer); + + + /* y is given to the fonts baseline we need it to the fonts top */ + y-=((fb_font->height * 75)/100); + + y+=1; /* the coord is the bottom-left of the pixels offset by 1 to make + * it work since fb coords are the top-left of pixels + */ + + /* The part of the text displayed is cropped to the current context. */ + x0 = x; + y0 = y; + x1 = x + (fb_font->width * length); + y1 = y + fb_font->height; + + if (!fb_plotters_clip_rect_ctx(&x0, &y0, &x1, &y1)) + return true; /* text lies outside current clipping region */ + + /* find width and height to plot */ + if (height > (y1 - y0)) + height = (y1 - y0); + + xoff = x0 - x; + yoff = y0 - y; + + fgcol = find_closest_palette_entry(c); + + /*LOG(("x %d, y %d, style %p, txt %.*s , len %d, bg 0x%lx, fg 0x%lx", + x,y,style,length,text,length,bg,c));*/ + + for (chr = 0; chr < length; chr++, x += fb_font->width) { + if ((x + fb_font->width) > x1) + break; + + if (x < x0) + continue; + + pvideo = fb_8bpp_get_xy_loc(x, y0); + + /* move our font-data to the correct position */ + font_data = fb_font->data + (buffer[chr] * fb_font->height); + + for (yloop = 0; yloop < height; yloop++) { + row = font_data[yoff + yloop]; + for (xloop = fb_font->width; xloop > 0 ; xloop--) { + if ((row & 1) != 0) + *(pvideo + xloop) = fgcol; + row = row >> 1; + } + pvideo += framebuffer->linelen; + } + } + + free(buffer); + return true; +} +#endif + +static bool fb_8bpp_disc(int x, int y, int radius, colour c, bool filled) +{ + LOG(("x %d, y %d, rad %d, c 0x%lx, fill %d", x, y, radius, (unsigned long)c, filled)); + return true; +} + +static bool fb_8bpp_arc(int x, int y, int radius, int angle1, int angle2, + colour c) +{ + LOG(("x %d, y %d, radius %d, angle1 %d, angle2 %d, c 0x%lx", + x, y, radius, angle1, angle2, (unsigned long)c)); + return true; +} + + + +static bool fb_8bpp_bitmap(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg, + struct content *content) +{ + uint8_t *pvideo; + colour *pixel = (colour *)bitmap->pixdata; + colour abpixel; /* alphablended pixel */ + int xloop, yloop; + int x0,y0,x1,y1; + int xoff, yoff; /* x and y offset into image */ + + /* LOG(("x %d, y %d, width %d, height %d, bitmap %p, content %p", + x,y,width,height,bitmap,content));*/ + + /* TODO here we should scale the image from bitmap->width to width, for + * now simply crop. + */ + if (width > bitmap->width) + width = bitmap->width; + + if (height > bitmap->height) + height = bitmap->height; + + /* The part of the scaled image actually displayed is cropped to the + * current context. + */ + x0 = x; + y0 = y; + x1 = x + width; + y1 = y + height; + + if (!fb_plotters_clip_rect_ctx(&x0, &y0, &x1, &y1)) + return true; + + if (height > (y1 - y0)) + height = (y1 - y0); + + if (width > (x1 - x0)) + width = (x1 - x0); + + xoff = x0 - x; + yoff = (y0 - y) * bitmap->width; + height = height * bitmap->width + yoff; + + /* plot the image */ + pvideo = fb_8bpp_get_xy_loc(x0, y0); + + if (bitmap->opaque) { + for (yloop = yoff; yloop < height; yloop += bitmap->width) { + for (xloop = 0; xloop < width; xloop++) { + abpixel = pixel[yloop + xloop + xoff]; + *(pvideo + xloop) = fb_colour_to_pixel(abpixel); + } + pvideo += framebuffer->linelen; + } + } else { + for (yloop = yoff; yloop < height; yloop += bitmap->width) { + for (xloop = 0; xloop < width; xloop++) { + abpixel = pixel[yloop + xloop + xoff]; + if ((abpixel & 0xFF000000) != 0) { + if ((abpixel & 0xFF000000) != 0xFF000000) { + abpixel = fb_plotters_ablend(abpixel, + fb_8bpp_to_colour(*(pvideo + xloop))); + } + + *(pvideo + xloop) = fb_colour_to_pixel(abpixel); + } + } + pvideo += framebuffer->linelen; + } + } + + return true; +} + +static bool fb_8bpp_bitmap_tile(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg, + bool repeat_x, bool repeat_y, + struct content *content) +{ + return fb_plotters_bitmap_tile(x, y, width, height, + bitmap, bg, repeat_x, repeat_y, + content, fb_8bpp_bitmap); +} + +static bool fb_8bpp_flush(void) +{ + LOG(("%s()\n", __func__)); + return true; +} + +static bool fb_8bpp_path(const float *p, + unsigned int n, + colour fill, + float width, + colour c, + const float transform[6]) +{ + LOG(("%f, %d, 0x%lx, %f, 0x%lx, %f", + *p, n, (unsigned long)fill, width, (unsigned long)c, *transform)); + + return true; +} + +const struct plotter_table framebuffer_8bpp_plot = { + .clg = fb_8bpp_clg, + .rectangle = fb_8bpp_rectangle, + .line = fb_8bpp_line, + .polygon = fb_8bpp_polygon, + .fill = fb_8bpp_fill, + .clip = fb_clip, + .text = fb_8bpp_text, + .disc = fb_8bpp_disc, + .arc = fb_8bpp_arc, + .bitmap = fb_8bpp_bitmap, + .bitmap_tile = fb_8bpp_bitmap_tile, + .flush = fb_8bpp_flush, + .path = fb_8bpp_path, + .option_knockout = true, +}; + + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..5220060 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,6 @@ +# Sources +DIR_SOURCES := libnsfb.c frontend.c frontend_sdl.c plot.c plot_util.c plotters.c 32bpp_plotters.c + +#frontend_linux.c + +include build/makefiles/Makefile.subdir diff --git a/src/frontend.c b/src/frontend.c new file mode 100644 index 0000000..171a7f3 --- /dev/null +++ b/src/frontend.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include + +#include "frontend.h" +#include "plotters.h" + +#define MAX_FRONTENDS 16 + +struct nsfb_frontend_s { + enum nsfb_frontend_e type; + const nsfb_frontend_rtns_t *rtns; + const char *name; +}; + +static struct nsfb_frontend_s frontends[MAX_FRONTENDS]; +static int frontend_count = 0; + +/* internal routine which lets frontends register their presence at runtime */ +void _nsfb_register_frontend(const enum nsfb_frontend_e type, + const nsfb_frontend_rtns_t *rtns, + const char *name) +{ + if (frontend_count >= MAX_FRONTENDS) + return; /* no space for additional frontends */ + + frontends[frontend_count].type = type; + frontends[frontend_count].rtns = rtns; + frontends[frontend_count].name = name; + frontend_count++; +} + +/* default frontend implementations */ +static int frontend_defaults(nsfb_t *nsfb) +{ + nsfb->width = 800; + nsfb->height = 600; + nsfb->bpp = 32; + + /* select plotters for bpp */ + select_plotters(nsfb); + + return 0; +} + +static int frontend_claim(nsfb_t *nsfb, nsfb_bbox_t *box) +{ + nsfb=nsfb; + box=box; + return 0; +} + +static int frontend_release(nsfb_t *nsfb, nsfb_bbox_t *box) +{ + nsfb=nsfb; + box=box; + return 0; +} + +nsfb_frontend_rtns_t *nsfb_frontend_get_rtns(enum nsfb_frontend_e type) +{ + int fend_loop; + nsfb_frontend_rtns_t *rtns = NULL; + + for (fend_loop = 0; fend_loop < frontend_count; fend_loop++) { + if (frontends[fend_loop].type == type) { + rtns = malloc(sizeof(nsfb_frontend_rtns_t)); + memcpy(rtns, + frontends[fend_loop].rtns, + sizeof(nsfb_frontend_rtns_t)); + + /* frontend must have an initialisor, finaliser and input method */ + if ((rtns->initialise == NULL) || + (rtns->finalise == NULL) || + (rtns->input == NULL) ) { + free(rtns); + rtns = NULL; + } + + /* The rest may be empty but to avoid the null check every time + * provide default implementations. + */ + if (rtns->defaults == NULL) + rtns->defaults = frontend_defaults; + + if (rtns->claim == NULL) + rtns->claim = frontend_claim; + + if (rtns->release == NULL) + rtns->release = frontend_release; + + break; + } + } + return rtns; +} diff --git a/src/frontend_linux.c b/src/frontend_linux.c new file mode 100644 index 0000000..ad95542 --- /dev/null +++ b/src/frontend_linux.c @@ -0,0 +1,9 @@ +#include "libnsfb.h" +#include "nsfb.h" +#include "frontend.h" + +const nsfb_frontend_rtns_t linux_rtns = { + .foo = 2, +}; + +NSFB_FRONTEND_DEF(linux, NSFB_FRONTEND_LINUX, &linux_rtns) diff --git a/src/frontend_sdl.c b/src/frontend_sdl.c new file mode 100644 index 0000000..a9da09e --- /dev/null +++ b/src/frontend_sdl.c @@ -0,0 +1,105 @@ + +#include + +#include "libnsfb.h" +#include "nsfb.h" +#include "frontend.h" + +static void +set_palette(nsfb_t *nsfb) +{ + SDL_Surface *sdl_screen = nsfb->frontend_priv; + SDL_Color colors[256]; + int loop; + for(loop=0; loop < 256; loop++){ + colors[loop].r = loop; + colors[loop].g = loop; + colors[loop].b = loop; + nsfb->palette[loop] = loop << 16 | loop << 8 | loop; + } + + /* Set palette */ + SDL_SetColors(sdl_screen, colors, 0, 256); + +} + +static int sdl_initialise(nsfb_t *nsfb) +{ + SDL_Surface *sdl_screen; + + if (nsfb->frontend_priv != NULL) + return -1; + + /* sanity checked depth. */ + if ((nsfb->bpp != 32) && (nsfb->bpp != 16) && (nsfb->bpp != 8)) + nsfb->bpp = 16; + + /* initialise SDL library */ + if (SDL_Init(SDL_INIT_VIDEO) < 0 ) { + fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError()); + return -1; + } + atexit(SDL_Quit); + + sdl_screen = SDL_SetVideoMode(nsfb->width, + nsfb->height, + nsfb->bpp, + SDL_SWSURFACE); + + if (sdl_screen == NULL ) { + fprintf(stderr, "Unable to set video: %s\n", SDL_GetError()); + return -1; + } + + nsfb->frontend_priv = sdl_screen; + + if (nsfb->bpp == 8) + set_palette(nsfb); + + nsfb->ptr = sdl_screen->pixels; + nsfb->linelen = sdl_screen->pitch; + + SDL_ShowCursor(SDL_DISABLE); + + return 0; +} + +static int sdl_finalise(nsfb_t *nsfb) +{ + nsfb=nsfb; + return 0; +} + +static int sdl_input(nsfb_t *nsfb) +{ + int got_event; + SDL_Event event; + + got_event = SDL_WaitEvent(&event); + if (event.type == SDL_QUIT) + exit(0); + nsfb=nsfb; + return 1; +} + +static int sdl_release(nsfb_t *nsfb, nsfb_bbox_t *box) +{ + SDL_Surface *sdl_screen = nsfb->frontend_priv; + + SDL_UpdateRect(sdl_screen, + box->x0, + box->y0, + box->x1 - box->x0, + box->y1 - box->y0); + + return 0; +} + +const nsfb_frontend_rtns_t sdl_rtns = { + .initialise = sdl_initialise, + .finalise = sdl_finalise, + .input = sdl_input, + .release = sdl_release, +}; + +NSFB_FRONTEND_DEF(sdl, NSFB_FRONTEND_SDL, &sdl_rtns) diff --git a/src/libnsfb.c b/src/libnsfb.c new file mode 100644 index 0000000..ec6ba86 --- /dev/null +++ b/src/libnsfb.c @@ -0,0 +1,71 @@ +#include +#include + +#include "libnsfb.h" +#include "nsfb.h" +#include "frontend.h" + + +/* documented in libnsfb.h */ +nsfb_t* +nsfb_init(const enum nsfb_frontend_e frontend_type) +{ + nsfb_t *newfb; + newfb = calloc(1, sizeof(nsfb_t)); + if (newfb == NULL) + return NULL; + + /* obtain frontend routines */ + newfb->frontend_rtns = nsfb_frontend_get_rtns(frontend_type); + if (newfb->frontend_rtns == NULL) { + free(newfb); + return NULL; + } + + newfb->frontend_rtns->defaults(newfb); + + return newfb; +} + + +int +nsfb_init_frontend(nsfb_t *nsfb) +{ + return nsfb->frontend_rtns->initialise(nsfb); +} + +int nsfb_input(nsfb_t *nsfb) +{ + return nsfb->frontend_rtns->input(nsfb); +} + +int nsfb_claim(nsfb_t *nsfb, nsfb_bbox_t *box) +{ + return nsfb->frontend_rtns->claim(nsfb, box); +} + +int nsfb_release(nsfb_t *nsfb, nsfb_bbox_t *box) +{ + return nsfb->frontend_rtns->release(nsfb, box); +} + +int nsfb_get_geometry(nsfb_t *nsfb, int *width, int *height, int *bpp) +{ + if (width != NULL) + *width = nsfb->width; + + if (height != NULL) + *height = nsfb->height; + + if (bpp != NULL) + *bpp = nsfb->bpp; + + return 0; +} + +int nsfb_get_framebuffer(nsfb_t *nsfb, uint8_t **ptr, int *linelen) +{ + *ptr = nsfb->ptr; + *linelen = nsfb->linelen; + return 0; +} diff --git a/src/plot.c b/src/plot.c new file mode 100644 index 0000000..8f46cfe --- /dev/null +++ b/src/plot.c @@ -0,0 +1,114 @@ +/* public plotter interface */ + +#include + +#include "libnsfb.h" +#include "libnsfb_plot.h" +#include "nsfb.h" +#include "nsfb_plot.h" + +/** Sets a clip rectangle for subsequent plots. + * + * Sets a clipping area which constrains all subsequent plotting operations. + * The clipping area must lie within the framebuffer visible screen or false + * will be returned and the new clipping area not set. + */ +bool nsfb_plot_set_clip(nsfb_t *nsfb, nsfb_bbox_t *clip) +{ + return nsfb->plotter_fns->clip(nsfb, clip); +} + +/** Clears plotting area to a flat colour. + */ +bool nsfb_plot_clg(nsfb_t *nsfb, nsfb_colour_t c) +{ + return nsfb->plotter_fns->clg(nsfb, c); +} + +/** Plots a rectangle outline. + * + * The line can be solid, dotted or dashed. Top left corner at (x0,y0) and + * rectangle has given width and height. + */ +bool +nsfb_plot_rectangle(nsfb_t *nsfb, + nsfb_bbox_t *rect, + int line_width, + nsfb_colour_t c, + bool dotted, + bool dashed) +{ + return nsfb->plotter_fns->rectangle(nsfb, rect, line_width, c, dotted, dashed); + +} + +/** Plots a filled rectangle. Top left corner at (x0,y0), bottom + * right corner at (x1,y1). Note: (x0,y0) is inside filled area, + * but (x1,y1) is below and to the right. See diagram below. + */ +bool nsfb_plot_rectangle_fill(nsfb_t *nsfb, nsfb_bbox_t *rect, nsfb_colour_t c) +{ + return nsfb->plotter_fns->fill(nsfb, rect, c); +} + +/** Plots a line. + * + * Draw a line from (x0,y0) to (x1,y1). Coordinates are at centre of line + * width/thickness. + */ +bool nsfb_plot_line(nsfb_t *nsfb, nsfb_bbox_t *line, int line_width, nsfb_colour_t c, bool dotted, bool dashed) +{ + return nsfb->plotter_fns->line(nsfb, line, line_width, c, dotted, dashed); +} + +/** Plots a filled polygon. + * + * Plots a filled polygon with straight lines between points. The lines around + * the edge of the ploygon are not plotted. The polygon is filled with a + * non-zero winding rule. + * + * + */ +bool nsfb_plot_polygon(nsfb_t *nsfb, const int *p, unsigned int n, nsfb_colour_t fill) +{ + return nsfb->plotter_fns->polygon(nsfb, p, n, fill); +} + +/** Plots an arc. + * + * around (x,y), from anticlockwise from angle1 to angle2. Angles are measured + * anticlockwise from horizontal, in degrees. + */ +bool nsfb_plot_arc(nsfb_t *nsfb, int x, int y, int radius, int angle1, int angle2, nsfb_colour_t c) +{ + return nsfb->plotter_fns->arc(nsfb, x, y, radius, angle1, angle2, c); +} + +/** Plots an alpha blended pixel. + * + * plots an alpha blended pixel. + */ +bool nsfb_plot_point(nsfb_t *nsfb, int x, int y, nsfb_colour_t c) +{ + return nsfb->plotter_fns->point(nsfb, x, y, c); +} + +bool nsfb_plot_ellipse(nsfb_t *nsfb, nsfb_bbox_t *ellipse, nsfb_colour_t c) +{ + return nsfb->plotter_fns->ellipse(nsfb, ellipse, c); +} + +bool nsfb_plot_ellipse_fill(nsfb_t *nsfb, nsfb_bbox_t *ellipse, nsfb_colour_t c) +{ + return nsfb->plotter_fns->ellipse_fill(nsfb, ellipse, c); +} + +bool nsfb_plot_copy(nsfb_t *nsfb, int srcx, int srcy, int width, int height, int dstx, int dsty) +{ + return nsfb->plotter_fns->copy(nsfb, srcx, srcy, width, height, dstx, dsty); +} + +bool nsfb_plot_bitmap(nsfb_t *nsfb, nsfb_bbox_t *loc, nsfb_colour_t *pixel, int bmp_width, int bmp_height, int bmp_stride, bool alpha) +{ + return nsfb->plotter_fns->bitmap(nsfb, loc, pixel, bmp_width, bmp_height, bmp_stride, alpha); +} diff --git a/src/plot_util.c b/src/plot_util.c new file mode 100644 index 0000000..058a0f9 --- /dev/null +++ b/src/plot_util.c @@ -0,0 +1,150 @@ +#include + +#include "libnsfb.h" +#include "nsfb.h" +#include "plot_util.h" + +enum { + POINT_LEFTOF_REGION = 1, + POINT_RIGHTOF_REGION = 2, + POINT_ABOVE_REGION = 4, + POINT_BELOW_REGION = 8, +}; + +#define REGION(x,y,cx1,cx2,cy1,cy2) \ + ( ( (y) > (cy2) ? POINT_BELOW_REGION : 0) | \ + ( (y) < (cy1) ? POINT_ABOVE_REGION : 0) | \ + ( (x) > (cx2) ? POINT_RIGHTOF_REGION : 0) | \ + ( (x) < (cx1) ? POINT_LEFTOF_REGION : 0) ) + +#define SWAP(a, b) do { int t; t=(a); (a)=(b); (b)=t; } while(0) + +/* clip a rectangle with another clipping rectangle. + * + * @param clip The rectangle to clip to. + * @param rect The rectangle to clip. + * @return false if the \a rect lies completely outside the \a clip rectangle, + * true if some of the \a rect is still visible. + */ +bool +nsfb_plot_clip(const nsfb_bbox_t * restrict clip, nsfb_bbox_t * restrict rect) +{ + char region1; + char region2; + + if (rect->x1 < rect->x0) SWAP(rect->x0, rect->x1); + + if (rect->y1 < rect->y0) SWAP(rect->y0, rect->y1); + + region1 = REGION(rect->x0, rect->y0, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); + region2 = REGION(rect->x1, rect->y1, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); + + /* area lies entirely outside the clipping rectangle */ + if ((region1 | region2) && (region1 & region2)) + return false; + + if (rect->x0 < clip->x0) + rect->x0 = clip->x0; + if (rect->x0 > clip->x1) + rect->x0 = clip->x1; + + if (rect->x1 < clip->x0) + rect->x1 = clip->x0; + if (rect->x1 > clip->x1) + rect->x1 = clip->x1; + + if (rect->y0 < clip->y0) + rect->y0 = clip->y0; + if (rect->y0 > clip->y1) + rect->y0 = clip->y1; + + if (rect->y1 < clip->y0) + rect->y1 = clip->y0; + if (rect->y1 > clip->y1) + rect->y1 = clip->y1; + + return true; +} + +bool +nsfb_plot_clip_ctx(nsfb_t *nsfb, nsfb_bbox_t * restrict rect) +{ + return nsfb_plot_clip(&nsfb->clip, rect); +} + +/** Clip a line to a bounding box. + */ +bool nsfb_plot_clip_line(const nsfb_bbox_t *clip, nsfb_bbox_t * restrict line) +{ + char region1; + char region2; + region1 = REGION(line->x0, line->y0, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); + region2 = REGION(line->x1, line->y1, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); + + while (region1 | region2) { + if (region1 & region2) { + /* line lies entirely outside the clipping rectangle */ + return false; + } + + if (region1) { + /* first point */ + if (region1 & POINT_BELOW_REGION) { + /* divide line at bottom */ + line->x0 = (line->x0 + (line->x1 - line->x0) * + (clip->y1 - 1 - line->y0) / (line->y1 - line->y0)); + line->y0 = clip->y1 - 1; + } else if (region1 & POINT_ABOVE_REGION) { + /* divide line at top */ + line->x0 = (line->x0 + (line->x1 - line->x0) * + (clip->y0 - line->y0) / (line->y1 - line->y0)); + line->y0 = clip->y0; + } else if (region1 & POINT_RIGHTOF_REGION) { + /* divide line at right */ + line->y0 = (line->y0 + (line->y1 - line->y0) * + (clip->x1 - 1 - line->x0) / (line->x1 - line->x0)); + line->x0 = clip->x1 - 1; + } else if (region1 & POINT_LEFTOF_REGION) { + /* divide line at right */ + line->y0 = (line->y0 + (line->y1 - line->y0) * + (clip->x0 - line->x0) / (line->x1 - line->x0)); + line->x0 = clip->x0; + } + + region1 = REGION(line->x0, line->y0, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); + } else { + /* second point */ + if (region2 & POINT_BELOW_REGION) { + /* divide line at bottom*/ + line->x1 = (line->x0 + (line->x1 - line->x0) * + (clip->y1 - 1 - line->y0) / (line->y1 - line->y0)); + line->y1 = clip->y1 - 1; + } else if (region2 & POINT_ABOVE_REGION) { + /* divide line at top*/ + line->x1 = (line->x0 + (line->x1 - line->x0) * + (clip->y0 - line->y0) / (line->y1 - line->y0)); + line->y1 = clip->y0; + } else if (region2 & POINT_RIGHTOF_REGION) { + /* divide line at right*/ + line->y1 = (line->y0 + (line->y1 - line->y0) * + (clip->x1 - 1 - line->x0) / (line->x1 - line->x0)); + line->x1 = clip->x1 - 1; + } else if (region2 & POINT_LEFTOF_REGION) { + /* divide line at right*/ + line->y1 = (line->y0 + (line->y1 - line->y0) * + (clip->x0 - line->x0) / (line->x1 - line->x0)); + line->x1 = clip->x0; + } + + region2 = REGION(line->x1, line->y1, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); + } + } + + return true; +} + +bool nsfb_plot_clip_line_ctx(nsfb_t *nsfb, nsfb_bbox_t * restrict line) +{ + return nsfb_plot_clip_line(&nsfb->clip, line); +} + diff --git a/src/plotters.c b/src/plotters.c new file mode 100644 index 0000000..b439d55 --- /dev/null +++ b/src/plotters.c @@ -0,0 +1,522 @@ +/* generic plotter functions which are not depth dependant */ + +#include +#include +#include +#include + +#include "libnsfb.h" +#include "nsfb.h" +#include "nsfb_plot.h" +#include "plot_util.h" +#include "plotters.h" + +extern const nsfb_plotter_fns_t _nsfb_1bpp_plotters; +extern const nsfb_plotter_fns_t _nsfb_8bpp_plotters; +extern const nsfb_plotter_fns_t _nsfb_16bpp_plotters; +extern const nsfb_plotter_fns_t _nsfb_24bpp_plotters; +extern const nsfb_plotter_fns_t _nsfb_32bpp_plotters; + +static bool set_clip(nsfb_t *nsfb, nsfb_bbox_t *clip) +{ + nsfb_bbox_t fbarea; + + /* screen area */ + fbarea.x0 = 0; + fbarea.y0 = 0; + fbarea.x1 = nsfb->width; + fbarea.y1 = nsfb->height; + + if (clip == NULL) { + nsfb->clip = fbarea; + } else { + if (!nsfb_plot_clip(&fbarea, clip)) + return false; + + nsfb->clip = *clip; + } + return true; +} + +static bool clg(nsfb_t *nsfb, nsfb_colour_t c) +{ + return nsfb->plotter_fns->fill(nsfb, &nsfb->clip, c); +} + +/** + * Find find first filled span along horizontal line at given coordinate + * + * \param p array of polygon vertices (x1, y1, x2, y2, ... , xN, yN) + * \param n number of polygon vertex values (N * 2) + * \param x current position along current scan line + * \param y position of current scan line + * \param x0 updated to start of filled area + * \param x1 updated to end of filled area + * \return true if an intersection was found + */ +static bool find_span(const int *p, int n, int x, int y, int *x0, int *x1) +{ + int i; + int p_x0, p_y0; + int p_x1, p_y1; + int x_new; + bool direction = false; + + *x0 = *x1 = INT_MAX; + + for (i = 0; i < n; i = i + 2) { + /* get line endpoints */ + if (i != n - 2) { + /* not the last line */ + p_x0 = p[i]; p_y0 = p[i + 1]; + p_x1 = p[i + 2]; p_y1 = p[i + 3]; + } else { + /* last line; 2nd endpoint is first vertex */ + p_x0 = p[i]; p_y0 = p[i + 1]; + p_x1 = p[0]; p_y1 = p[1]; + } + /* ignore horizontal lines */ + if (p_y0 == p_y1) + continue; + + /* ignore lines that don't cross this y level */ + if ((y < p_y0 && y < p_y1) || (y > p_y0 && y > p_y1)) + continue; + + if (p_x0 == p_x1) { + /* vertical line, x is constant */ + x_new = p_x0; + } else { + /* find intersect */ + x_new = p_x0 + ((long long)(y - p_y0) * (p_x1 - p_x0)) / + (p_y1 - p_y0); + } + + /* ignore intersections before current x */ + if (x_new < x) + continue; + + /* set nearest intersections as filled area endpoints */ + if (x_new < *x0) { + /* nearer than first endpoint */ + *x1 = *x0; + *x0 = x_new; + direction = (p_y0 > p_y1); + } else if (x_new == *x0) { + /* same as first endpoint */ + if ((p_y0 > p_y1) != direction) + *x1 = x_new; + } else if (x_new < *x1) { + /* nearer than second endpoint */ + *x1 = x_new; + } + + } + if (*x0 == INT_MAX) + /* no span found */ + return false; + + /* span found */ + if (*x1 == INT_MAX) { + *x1 = *x0; + *x0 = x; + return true; + } + + return true; +} + + +/** + * Plot a polygon + * + * \param p array of polygon vertices (x1, y1, x2, y2, ... , xN, yN) + * \param n number of polygon vertices (N) + * \param c fill colour + * \param linefn function to call to plot a horizontal line + * \return true if no errors + */ +static bool polygon(nsfb_t *nsfb, const int *p, unsigned int n, nsfb_colour_t c) +{ + int poly_x0, poly_y0; /* Bounding box top left corner */ + int poly_x1, poly_y1; /* Bounding box bottom right corner */ + int i, j; /* indexes */ + int x0, x1; /* filled span extents */ + int y; /* current y coordinate */ + int y_max; /* bottom of plot area */ + nsfb_bbox_t fline; + + /* find no. of vertex values */ + int v = n * 2; + + /* Can't plot polygons with 2 or fewer vertices */ + if (n <= 2) + return true; + + /* Find polygon bounding box */ + poly_x0 = poly_x1 = *p; + poly_y0 = poly_y1 = p[1]; + for (i = 2; i < v; i = i + 2) { + j = i + 1; + if (p[i] < poly_x0) + poly_x0 = p[i]; + else if (p[i] > poly_x1) + poly_x1 = p[i]; + if (p[j] < poly_y0) + poly_y0 = p[j]; + else if (p[j] > poly_y1) + poly_y1 = p[j]; + } + + /* Don't try to plot it if it's outside the clip rectangle */ + if (nsfb->clip.y1 < poly_y0 || + nsfb->clip.y0 > poly_y1 || + nsfb->clip.x1 < poly_x0 || + nsfb->clip.x0 > poly_x1) + return true; + + /* Find the top of the important area */ + if (poly_y0 > nsfb->clip.y0) + y = poly_y0; + else + y = nsfb->clip.y0; + + /* Find the bottom of the important area */ + if (poly_y1 < nsfb->clip.y1) + y_max = poly_y1; + else + y_max = nsfb->clip.y1; + + for (; y < y_max; y++) { + x1 = poly_x0; + /* For each row */ + while (find_span(p, v, x1, y, &x0, &x1)) { + /* don't draw anything outside clip region */ + if (x1 < nsfb->clip.x0) + continue; + else if (x0 < nsfb->clip.x0) + x0 = nsfb->clip.x0; + if (x0 > nsfb->clip.x1) + break; + else if (x1 > nsfb->clip.x1) + x1 = nsfb->clip.x1; + + fline.x0 = x0; + fline.y0 = y; + fline.x1 = x1; + fline.y1 = y; + + /* draw this filled span on current row */ + nsfb->plotter_fns->line(nsfb, &fline, 1, c, false, false); + + /* don't look for more spans if already at end of clip + * region or polygon */ + if (x1 == nsfb->clip.x1 || x1 == poly_x1) + break; + + if (x0 == x1) + x1++; + } + } + return true; +} + +static bool rectangle(nsfb_t *nsfb, nsfb_bbox_t *rect, + int line_width, nsfb_colour_t c, + bool dotted, bool dashed) +{ + nsfb_bbox_t side; + + side = *rect; + side.y1 = side.y0; + + nsfb->plotter_fns->line(nsfb, &side, line_width, c, dotted, dashed); + + side = *rect; + side.y0 = side.y1; + + nsfb->plotter_fns->line(nsfb, &side, line_width, c, dotted, dashed); + + side = *rect; + side.x1 = side.x0; + + nsfb->plotter_fns->line(nsfb, &side, line_width, c, dotted, dashed); + + side = *rect; + side.x0 = side.x1; + + return nsfb->plotter_fns->line(nsfb, &side, line_width, c, dotted, dashed); +} + +/* plotter routine for ellipse points */ +static void ellipsepoints(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c) +{ + nsfb->plotter_fns->point(nsfb, cx + x, cy + y, c); + nsfb->plotter_fns->point(nsfb, cx - x, cy + y, c); + nsfb->plotter_fns->point(nsfb, cx + x, cy - y, c); + nsfb->plotter_fns->point(nsfb, cx - x, cy - y, c); +} + +static void ellipsefill(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c) +{ + nsfb_bbox_t fline; + fline.x0 = cx - x; + fline.x1 = cx + x; + fline.y0 = fline.y1 = cy + y; + nsfb->plotter_fns->line(nsfb, &fline, 1, c, false, false); + + fline.x0 = cx - x; + fline.x1 = cx + x; + fline.y0 = fline.y1 = cy - y; + nsfb->plotter_fns->line(nsfb, &fline, 1, c, false, false); + +} + +#define ROUND(a) ((int)(a+0.5)) + +static bool ellipse_midpoint(nsfb_t *nsfb, + int cx, + int cy, + int rx, + int ry, + nsfb_colour_t c, + void (ellipsefn)(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c)) +{ + int rx2 = rx * rx; + int ry2 = ry * ry; + int tworx2 = 2 * rx2; + int twory2 = 2 * ry2; + int p; + int x = 0; + int y = ry; + int px = 0; + int py = tworx2 * y; + + ellipsefn(nsfb, cx, cy, x, y, c); + + /* region 1 */ + p = ROUND(ry2 - (rx2 * ry) + (0.25 * rx2)); + while (px < py) { + x++; + px += twory2; + if (p <0) { + p+=ry2 + px; + } else { + y--; + py -= tworx2; + p+=ry2 + px - py; + } + ellipsefn(nsfb, cx, cy, x, y, c); + } + + /* region 2 */ + p = ROUND(ry2*(x+0.5)*(x+0.5) + rx2*(y-1)*(y-1) - rx2*ry2); + while (y > 0) { + y--; + py -= tworx2; + if (p > 0) { + p+=rx2 - py; + } else { + x++; + px += twory2; + p+=rx2 - py + px; + } + ellipsefn(nsfb, cx, cy, x, y, c); + } + return true; +} + + +/* plotter routine for 8way circle symetry */ +static void circlepoints(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c) +{ + nsfb->plotter_fns->point(nsfb, cx + x, cy + y, c); + nsfb->plotter_fns->point(nsfb, cx - x, cy + y, c); + nsfb->plotter_fns->point(nsfb, cx + x, cy - y, c); + nsfb->plotter_fns->point(nsfb, cx - x, cy - y, c); + nsfb->plotter_fns->point(nsfb, cx + y, cy + x, c); + nsfb->plotter_fns->point(nsfb, cx - y, cy + x, c); + nsfb->plotter_fns->point(nsfb, cx + y, cy - x, c); + nsfb->plotter_fns->point(nsfb, cx - y, cy - x, c); +} + +static void circlefill(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c) +{ + nsfb_bbox_t fline; + fline.x0 = cx - x; + fline.x1 = cx + x; + fline.y0 = fline.y1 = cy + y; + nsfb->plotter_fns->line(nsfb, &fline, 1, c, false, false); + + fline.x0 = cx - x; + fline.x1 = cx + x; + fline.y0 = fline.y1 = cy - y; + nsfb->plotter_fns->line(nsfb, &fline, 1, c, false, false); + + fline.x0 = cx - y; + fline.x1 = cx + y; + fline.y0 = fline.y1 = cy + x; + nsfb->plotter_fns->line(nsfb, &fline, 1, c, false, false); + + fline.x0 = cx - y; + fline.x1 = cx + y; + fline.y0 = fline.y1 = cy - x; + nsfb->plotter_fns->line(nsfb, &fline, 1, c, false, false); +} + +static bool circle_midpoint(nsfb_t *nsfb, + int cx, + int cy, + int r, + nsfb_colour_t c, + void (circfn)(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c)) +{ + int x = 0; + int y = r; + int p = 1 - r; + + circfn(nsfb, cx, cy, x, y, c); + while (x < y) { + x++; + if (p < 0) { + p += 2 * x + 1; + } else { + y--; + p += 2 * (x - y) + 1; + } + circfn(nsfb, cx, cy, x, y, c); + } + return true; +} + +static bool ellipse(nsfb_t *nsfb, nsfb_bbox_t *ellipse, nsfb_colour_t c) +{ + int width = (ellipse->x1 - ellipse->x0)>>1; + int height = (ellipse->y1 - ellipse->y0)>>1; + + if (width == height) { + /* circle */ + return circle_midpoint(nsfb, ellipse->x0 + width, ellipse->y0 + height, width, c, circlepoints); + } else { + return ellipse_midpoint(nsfb, ellipse->x0 + width, ellipse->y0 + height, width, height, c, ellipsepoints); + } +} + +static bool ellipse_fill(nsfb_t *nsfb, nsfb_bbox_t *ellipse, nsfb_colour_t c) +{ + int width = (ellipse->x1 - ellipse->x0) >> 1; + int height = (ellipse->y1 - ellipse->y0) >> 1; + + if (width == height) { + /* circle */ + return circle_midpoint(nsfb, ellipse->x0 + width, ellipse->y0 + height, width, c, circlefill); + } else { + return ellipse_midpoint(nsfb, ellipse->x0 + width, ellipse->y0 + height, width, height, c, ellipsefill); + } +} + +static bool copy(nsfb_t *nsfb, int srcx, int srcy, int width, int height, int dstx, int dsty) +{ + uint8_t *srcptr = (nsfb->ptr + + (srcy * nsfb->linelen) + + ((srcx * nsfb->bpp) / 8)); + + uint8_t *dstptr = (nsfb->ptr + + (dsty * nsfb->linelen) + + ((dstx * nsfb->bpp) / 8)); + + int hloop; + + if (width == nsfb->width) { + /* take shortcut and use memmove */ + memmove(dstptr, srcptr, (width * height * nsfb->bpp) / 8); + } else { + if (srcy > dsty) { + for (hloop = height; hloop > 0; hloop--) { + memmove(dstptr, srcptr, (width * nsfb->bpp) / 8); + srcptr += nsfb->linelen; + dstptr += nsfb->linelen; + } + } else { + srcptr += height * nsfb->linelen; + dstptr += height * nsfb->linelen; + for (hloop = height; hloop > 0; hloop--) { + srcptr -= nsfb->linelen; + dstptr -= nsfb->linelen; + memmove(dstptr, srcptr, (width * nsfb->bpp) / 8); + } + } + } + return true; +} + +static bool arc(nsfb_t *nsfb, int x, int y, int radius, int angle1, int angle2, nsfb_colour_t c) +{ + nsfb=nsfb; + x = x; + y = y; + radius = radius; + c = c; + angle1=angle1; + angle2=angle2; + return true; +} + +bool select_plotters(nsfb_t *nsfb) +{ + const nsfb_plotter_fns_t *table; + + switch (nsfb->bpp) { + /* case 1: + table = &_nsfb_1bpp_plotters; + break; + */ + /* + + case 8: + table = &_nsfb_8bpp_plotters; + break; + */ + /* + + case 16: + table = &_nsfb_16bpp_plotters; + break; + */ + /* + case 24: + table = &_nsfb_24bpp_plotters; + break; + */ + case 32: + table = &_nsfb_32bpp_plotters; + break; + + default: + return false; + } + + if (nsfb->plotter_fns != NULL) + free(nsfb->plotter_fns); + + nsfb->plotter_fns = calloc(1, sizeof(nsfb_plotter_fns_t)); + memcpy(nsfb->plotter_fns, table, sizeof(nsfb_plotter_fns_t)); + + /* set the generics */ + nsfb->plotter_fns->clg = clg; + nsfb->plotter_fns->clip = set_clip; + nsfb->plotter_fns->polygon = polygon; + nsfb->plotter_fns->rectangle = rectangle; + nsfb->plotter_fns->ellipse = ellipse; + nsfb->plotter_fns->ellipse_fill = ellipse_fill; + nsfb->plotter_fns->copy = copy; + nsfb->plotter_fns->arc = arc; + + /* set default clip rectangle to size of framebuffer */ + nsfb->clip.x0 = 0; + nsfb->clip.y0 = 0; + nsfb->clip.x1 = nsfb->width; + nsfb->clip.y1 = nsfb->height; + + return true; +} -- cgit v1.2.3