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 --- Makefile | 23 + include/frontend.h | 35 + include/libnsfb.h | 75 ++ include/libnsfb_plot.h | 72 ++ include/nsfb.h | 30 + include/nsfb_plot.h | 88 ++ include/plot_util.h | 24 + include/plotters.h | 1 + libnsfb.pc.in | 10 + 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 +++++++++ test/Makefile | 110 ++ test/nsglobe.c | 2933 ++++++++++++++++++++++++++++++++++++++++++++++++ test/plottest.c | 172 +++ usage | 40 + 26 files changed, 6781 insertions(+) create mode 100644 Makefile create mode 100644 include/frontend.h create mode 100644 include/libnsfb.h create mode 100644 include/libnsfb_plot.h create mode 100644 include/nsfb.h create mode 100644 include/nsfb_plot.h create mode 100644 include/plot_util.h create mode 100644 include/plotters.h create mode 100644 libnsfb.pc.in 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 create mode 100644 test/Makefile create mode 100644 test/nsglobe.c create mode 100644 test/plottest.c create mode 100644 usage diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e5695d7 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +# Component settings +COMPONENT := nsfb +COMPONENT_VERSION := 0.0.1 +# Default to a static library +COMPONENT_TYPE ?= lib-static + +# Setup the tooling +include build/makefiles/Makefile.tools + +# Toolchain flags +WARNFLAGS := -Wall -Wextra -Wundef -Wpointer-arith -Wcast-align \ + -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes \ + -Wmissing-declarations -Wnested-externs -Werror -pedantic +CFLAGS := -g $(CFLAGS) -std=c99 -D_BSD_SOURCE -I$(CURDIR)/include/ \ + -I$(CURDIR)/src $(WARNFLAGS) + +include build/makefiles/Makefile.top + +# Extra installation rules +I := /include/libnsfb$(major-version) +INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):include/libnsfb.h +INSTALL_ITEMS := $(INSTALL_ITEMS) /lib/pkgconfig:lib$(COMPONENT).pc.in +INSTALL_ITEMS := $(INSTALL_ITEMS) /lib:$(OUTPUT) diff --git a/include/frontend.h b/include/frontend.h new file mode 100644 index 0000000..ee76632 --- /dev/null +++ b/include/frontend.h @@ -0,0 +1,35 @@ +#include "libnsfb.h" /* exported interface */ +#include "nsfb.h" + +/* frontend default options */ +typedef int (nsfb_fendfn_defaults_t)(nsfb_t *nsfb); +/* frontend init */ +typedef int (nsfb_fendfn_init_t)(nsfb_t *nsfb); +/* frontend finalise */ +typedef int (nsfb_fendfn_fini_t)(nsfb_t *nsfb); +/* frontend input */ +typedef int (nsfb_fendfn_input_t)(nsfb_t *nsfb); +/* frontend area claim */ +typedef int (nsfb_fendfn_claim_t)(nsfb_t *nsfb, nsfb_bbox_t *box); +/* frontend area release */ +typedef int (nsfb_fendfn_release_t)(nsfb_t *nsfb, nsfb_bbox_t *box); + +typedef struct nsfb_frontend_rtns_s { + nsfb_fendfn_defaults_t *defaults; + nsfb_fendfn_init_t *initialise; + nsfb_fendfn_fini_t *finalise; + nsfb_fendfn_input_t *input; + nsfb_fendfn_claim_t *claim; + nsfb_fendfn_release_t *release; +} nsfb_frontend_rtns_t; + +void _nsfb_register_frontend(const enum nsfb_frontend_e type, const nsfb_frontend_rtns_t *rtns, const char *name); + +nsfb_frontend_rtns_t *nsfb_frontend_get_rtns(enum nsfb_frontend_e type); + +/* macro which adds a builtin command with no argument limits */ +#define NSFB_FRONTEND_DEF(__name, __type, __rtns) \ + static void __name##_register_frontend(void) __attribute__((constructor)); \ + void __name##_register_frontend(void) { \ + _nsfb_register_frontend(__type, __rtns, #__name); \ + } diff --git a/include/libnsfb.h b/include/libnsfb.h new file mode 100644 index 0000000..0af8ab3 --- /dev/null +++ b/include/libnsfb.h @@ -0,0 +1,75 @@ +#ifndef _LIBNSFB_H +#define _LIBNSFB_H 1 + +#include + +typedef struct nsfb_cursor_s nsfb_cursor_t; +typedef struct nsfb_s nsfb_t; +typedef uint32_t nsfb_colour_t; + +/* bounding box */ +typedef struct nsfb_bbox_s { + int x0; + int y0; + int x1; + int y1; +} nsfb_bbox_t; + +/** The type of frontend for a framebuffer context. */ +enum nsfb_frontend_e { + NSFB_FRONTEND_NONE = 0, /* Empty frontend. */ + NSFB_FRONTEND_SDL, + NSFB_FRONTEND_LINUX, + NSFB_FRONTEND_VNC, + NSFB_FRONTEND_ABLE, + NSFB_FRONTEND_RAM, +}; + +/** Initialise nsfb context. + * + * This initialises a framebuffer context. + * + * @param frontend The type of frontend to create a context for. + */ +nsfb_t *nsfb_init(enum nsfb_frontend_e frontend); + +/** Initialise selected frontend. + * + * @param nsfb The context frturned from ::nsfb_init + */ +int nsfb_init_frontend(nsfb_t *nsfb); + +/** Process input from a frontend. + */ +int nsfb_input(nsfb_t *nsfb); + +/** Claim an area of screen to be redrawn. + * + * Informs the nsfb library that an area of screen will be updated by the user + * program, used for soft cursor plotting. + * + * @param box The bounding box of the area which might be altered. + */ +int nsfb_claim(nsfb_t *nsfb, nsfb_bbox_t *box); + +/** Release an area of screen which has been redrawn. + * + * Informs the nsfb library that an area of screen has been updated by the user + * program. Some frontends only update on area release. + * + * @param box The bounding box of the area which has been altered. + */ +int nsfb_release(nsfb_t *nsfb, nsfb_bbox_t *box); + +/** Obtain the geometry of a nsfb context. + * + * @param width a variable to store the framebuffer width in or NULL + * @param height a variable to store the framebuffer height in or NULL + * @param bpp a variable to store the framebuffer bpp in or NULL + */ +int nsfb_get_geometry(nsfb_t *nsfb, int *width, int *height, int *bpp); + +/** Obtain the framebuffer memory base and stride. */ +int nsfb_get_framebuffer(nsfb_t *nsfb, uint8_t **ptr, int *linelen); + +#endif diff --git a/include/libnsfb_plot.h b/include/libnsfb_plot.h new file mode 100644 index 0000000..64aa729 --- /dev/null +++ b/include/libnsfb_plot.h @@ -0,0 +1,72 @@ +/** 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); + +/** Clears plotting area to a flat colour. + */ +bool nsfb_plot_clg(nsfb_t *nsfb, nsfb_colour_t 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); + +/** 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); + +/** 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); + +/** 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); + +/** Plot an ellipse. + */ +bool nsfb_plot_ellipse(nsfb_t *nsfb, nsfb_bbox_t *ellipse, nsfb_colour_t c); + +/** Plot a filled ellipse. + */ +bool nsfb_plot_ellipse_fill(nsfb_t *nsfb, nsfb_bbox_t *ellipse, nsfb_colour_t c); + +/** 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); + +/** 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); + +/** copy an area of screen + * + * Copy an area of the display. + */ +bool nsfb_plot_copy(nsfb_t *nsfb, int srcx, int srcy, int width, int height, int dstx, int dsty); + +/** Plot bitmap. + */ +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); diff --git a/include/nsfb.h b/include/nsfb.h new file mode 100644 index 0000000..cec4a63 --- /dev/null +++ b/include/nsfb.h @@ -0,0 +1,30 @@ +#ifndef _NSFB_H +#define _NSFB_H 1 + +#include + +/** NS Framebuffer context + */ +struct nsfb_s { + int width; /**< Visible width. */ + int height; /**< Visible height. */ + int bpp; /**< Bits per pixel. */ + + int refresh; /**< Desired refresh rate for physical displays. */ + char *output_dev; /**> Path to output device for frontends that require it. */ + + uint8_t *ptr; /**< Base of video memory. */ + int linelen; /**< length of a video line. */ + + nsfb_colour_t palette[256]; /**< palette for index modes */ + nsfb_cursor_t *cursor; + + struct nsfb_frontend_rtns_s *frontend_rtns; /**< frontend routines. */ + void *frontend_priv; + + nsfb_bbox_t clip; /**< current clipping rectangle for plotters */ + struct nsfb_plotter_fns_s *plotter_fns; /**< Plotter methods */ +}; + + +#endif diff --git a/include/nsfb_plot.h b/include/nsfb_plot.h new file mode 100644 index 0000000..2dbd22a --- /dev/null +++ b/include/nsfb_plot.h @@ -0,0 +1,88 @@ + + +/** Clears plotting area to a flat colour (if needed) + */ +typedef bool (nsfb_plotfn_clg_t)(nsfb_t *nsfb, nsfb_colour_t 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. + */ +typedef bool (nsfb_plotfn_rectangle_t)(nsfb_t *nsfb, nsfb_bbox_t *rect, int line_width, nsfb_colour_t c, bool dotted, bool dashed); + +/** Plots a line from (x0,y0) to (x1,y1). Coordinates are at + * centre of line width/thickness. + */ +typedef bool (nsfb_plotfn_line_t)(nsfb_t *nsfb, nsfb_bbox_t *line, int line_width, nsfb_colour_t c, bool dotted, bool dashed); + +/** 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 the non-zero winding rule. + */ +typedef bool (nsfb_plotfn_polygon_t)(nsfb_t *nsfb, const int *p, unsigned int n, nsfb_colour_t fill); + +/** 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. + */ +typedef bool (nsfb_plotfn_fill_t)(nsfb_t *nsfb, nsfb_bbox_t *rect, nsfb_colour_t c); + +/** Sets a clip rectangle for subsequent plots. + */ +typedef bool (nsfb_plotfn_clip_t)(nsfb_t *nsfb, nsfb_bbox_t *clip); + +/** Plots an arc, around (x,y), from anticlockwise from angle1 to + * angle2. Angles are measured anticlockwise from horizontal, in + * degrees. + */ +typedef bool (nsfb_plotfn_arc_t)(nsfb_t *nsfb, int x, int y, int radius, int angle1, int angle2, nsfb_colour_t c); + +/** Plots a point. + * + * Plot a single alpha blended pixel. + */ +typedef bool (nsfb_plotfn_point_t)(nsfb_t *nsfb, int x, int y, nsfb_colour_t c); + +/** Plot an ellipse. + * + * plot an ellipse outline, note if teh bounding box is square this will plot a + * circle. + */ +typedef bool (nsfb_plotfn_ellipse_t)(nsfb_t *nsfb, nsfb_bbox_t *ellipse, nsfb_colour_t c); + +/** Plot a filled ellipse. + * + * plot a filled ellipse, note if the bounding box is square this will plot a + * circle. + */ +typedef bool (nsfb_plotfn_ellipse_fill_t)(nsfb_t *nsfb, nsfb_bbox_t *ellipse, nsfb_colour_t c); + + +/** Plot bitmap + */ +typedef bool (nsfb_plotfn_bitmap_t)(nsfb_t *nsfb, nsfb_bbox_t *loc, nsfb_colour_t *pixel, int bmp_width, int bmp_height, int bmp_stride, bool alpha); + + +/** Copy an area of screen + * + * Copy an area of the display. + */ +typedef bool (nsfb_plotfn_copy_t)(nsfb_t *nsfb, int srcx, int srcy, int width, int height, int dstx, int dsty); + +/** plotter function table. */ +typedef struct nsfb_plotter_fns_s { + nsfb_plotfn_clg_t *clg; + nsfb_plotfn_rectangle_t *rectangle; + nsfb_plotfn_line_t *line; + nsfb_plotfn_polygon_t *polygon; + nsfb_plotfn_fill_t *fill; + nsfb_plotfn_clip_t *clip; + nsfb_plotfn_ellipse_t *ellipse; + nsfb_plotfn_ellipse_fill_t *ellipse_fill; + nsfb_plotfn_arc_t *arc; + nsfb_plotfn_bitmap_t *bitmap; + nsfb_plotfn_point_t *point; + nsfb_plotfn_copy_t *copy; +} nsfb_plotter_fns_t; + + diff --git a/include/plot_util.h b/include/plot_util.h new file mode 100644 index 0000000..6565cb1 --- /dev/null +++ b/include/plot_util.h @@ -0,0 +1,24 @@ +/* alpha blend two pixels together */ +static inline nsfb_colour_t +nsfb_plot_ablend(nsfb_colour_t pixel, nsfb_colour_t scrpixel) +{ + int opacity = pixel >> 24; + int transp = 0x100 - opacity; + uint32_t rb, g; + + rb = ((pixel & 0xFF00FF) * opacity + + (scrpixel & 0xFF00FF) * transp) >> 8; + g = ((pixel & 0x00FF00) * opacity + + (scrpixel & 0x00FF00) * transp) >> 8; + + return (rb & 0xFF00FF) | (g & 0xFF00); +} + + +bool nsfb_plot_clip(const nsfb_bbox_t * restrict clip, nsfb_bbox_t * restrict rect); + +bool nsfb_plot_clip_ctx(nsfb_t *nsfb, nsfb_bbox_t * restrict rect); + +bool nsfb_plot_clip_line(const nsfb_bbox_t * restrict clip, nsfb_bbox_t * restrict line); + +bool nsfb_plot_clip_line_ctx(nsfb_t *nsfb, nsfb_bbox_t * restrict line); diff --git a/include/plotters.h b/include/plotters.h new file mode 100644 index 0000000..dc4648b --- /dev/null +++ b/include/plotters.h @@ -0,0 +1 @@ +bool select_plotters(nsfb_t *nsfb); diff --git a/libnsfb.pc.in b/libnsfb.pc.in new file mode 100644 index 0000000..fe99142 --- /dev/null +++ b/libnsfb.pc.in @@ -0,0 +1,10 @@ +prefix=PREFIX +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: libnsfb +Description: Provides framebuffer access for netsurf. +Version: VERSION +Libs: -L${libdir} -lnsfbMAJOR +Cflags: -I${includedir}/libnsfbMAJOR 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; +} diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..a87bc36 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,110 @@ +# Child makefile fragment for libhubbub +# +# Toolchain is provided by top-level makefile +# +# Variables provided by top-level makefile +# +# COMPONENT The name of the component +# EXPORT The location of the export directory +# TOP The location of the source tree root +# RELEASEDIR The place to put release objects +# DEBUGDIR The place to put debug objects +# +# do_include Canned command sequence to include a child makefile +# +# Variables provided by parent makefile: +# +# DIR The name of the directory we're in, relative to $(TOP) +# +# Variables we can manipulate: +# +# ITEMS_CLEAN The list of items to remove for "make clean" +# ITEMS_DISTCLEAN The list of items to remove for "make distclean" +# TARGET_TESTS The list of target names to run for "make test" +# +# SOURCES The list of sources to build for $(COMPONENT) +# +# Plus anything from the toolchain + +# Push parent directory onto the directory stack +sp := $(sp).x +dirstack_$(sp) := $(d) +d := $(DIR) + +ifeq ($(MAKECMDGOALS),test) +# Extend toolchain settings +CFLAGS := $(CFLAGS) -I$(TOP)/include/ +LDFLAGS := $(LDFLAGS) -Wl,--whole-archive -lnsfb0 -Wl,--no-whole-archive -lSDL +endif + +ifdef PROFILE + CFLAGS := $(CFLAGS) -pg -fno-omit-frame-pointer + LDFLAGS := $(LDFLAGS) -pg +endif + +# Tests +TESTS_$(d) := plottest + +# Items for top-level makefile to use +ITEMS_CLEAN := $(ITEMS_CLEAN) \ + $(addprefix $(d), $(addsuffix $(EXEEXT), $(TESTS_$(d)))) \ + $(addprefix $(d), $(addsuffix .gcda, $(TESTS_$(d)))) \ + $(addprefix $(d), $(addsuffix .gcno, $(TESTS_$(d)))) \ + $(addprefix $(d), $(addsuffix .d, $(TESTS_$(d)))) +ITEMS_DISTCLEAN := $(ITEMS_DISTCLEAN) $(d)log + +# Targets for top-level makefile to run +TARGET_TESTS := $(TARGET_TESTS) test_$(d) + +# Now we get to hack around so that we know what directory we're in. +# $(d) no longer exists when running the commands for a target, so we can't +# simply use it verbatim. Assigning to a variable doesn't really help, as +# there's no guarantee that someone else hasn't overridden that variable. +# So, what we do is make the target depend on $(d), then pick it out of the +# dependency list when running commands. This isn't pretty, but is effective. +test_$(d): $(d) $(addprefix $(d), $(TESTS_$(d))) + @$(PERL) $(TOP)/$ $(1)" + @$$(CC) -c -g $$(DEBUGCFLAGS) -o $$@.o $(1) + @$$(LD) -g -o $$@ $$@.o -lhubbub-debug $$(LDFLAGS) -lgcov + @$$(RM) $$(RMFLAGS) $$@.o + +endef + +$(eval $(foreach TEST,$(addprefix $(d), $(TESTS_$(d))), \ + $(call dep_test,$(addsuffix .c, $(TEST)),$(addsuffix .d, $(TEST)),$(TEST)))) + +ifeq ($(MAKECMDGOALS),test) +-include $(sort $(DEP_$(d))) +endif + +$(eval $(foreach TEST,$(addprefix $(d), $(TESTS_$(d))), \ + $(call compile_test,$(addsuffix .c, $(TEST)),$(TEST),$(addsuffix .d, $(TEST))))) + +# Now include any children we may have +MAKE_INCLUDES := $(wildcard $(d)*/Makefile) +$(eval $(foreach INC, $(MAKE_INCLUDES), $(call do_include,$(INC)))) + +# Finally, pop off the directory stack +d := $(dirstack_$(sp)) +sp := $(basename $(sp)) diff --git a/test/nsglobe.c b/test/nsglobe.c new file mode 100644 index 0000000..9ba98bb --- /dev/null +++ b/test/nsglobe.c @@ -0,0 +1,2933 @@ +/* GIMP RGBA C-Source image dump (nnglobe.c) */ + +const struct { + unsigned int width; + unsigned int height; + unsigned int bytes_per_pixel; /* 3:RGB, 4:RGBA */ + unsigned char pixel_data[132 * 135 * 4 + 1]; +} nsglobe = { + 132, 135, 4, + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\17$0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\4\12\15\377\24.=\377\36Qn\3773x\236\377G\211\253\377\\\301\370\377\\\301" + "\370\377\201\324\367\377|\326\377\377z\325\377\377z\325\377\377z\325\377" + "\377x\324\377\377v\323\377\377v\323\377\377o\317\377\377Z\277\367\377Z\277" + "\367\377G\211\253\3773x\236\377\36Qn\377\17$0\377\4\12\15\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\10\24\32\377\31Fb\377G\211\253\377|\326\377\377\211\334" + "\377\377\207\333\377\377\207\333\377\377\207\333\377\377\177\330\377\377" + "~\327\377\377~\327\377\377~\327\377\377~\327\377\377|\326\377\377z\325\377" + "\377z\325\377\377x\324\377\377v\323\377\377t\322\377\377t\322\377\377t\322" + "\377\377t\322\377\377o\317\377\377o\317\377\377k\315\377\377k\315\377\377" + "k\315\377\377Z\277\367\377G\211\253\377\36Qn\377\10\27\40\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\25\211\263\377\25\211\263\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\17$0\377]\306\377\377" + "]\304\375\377]\304\375\377]\304\375\377]\304\375\377]\306\377\377W\303\377" + "\377W\303\377\377W\303\377\377W\303\377\377W\303\377\377W\303\377\377W\303" + "\377\377W\303\377\377V\302\377\377V\302\377\377W\303\377\377W\303\377\377" + "T\301\376\377T\301\376\377N\276\377\377T\301\376\377N\276\377\377N\276\377" + "\377T\301\376\377J\274\377\377J\274\377\377T\301\376\377J\274\377\377N\276" + "\377\377J\274\377\377I\274\377\377I\274\377\377H\273\377\377H\273\377\377" + "D\271\376\377H\273\377\377D\271\376\377D\271\376\377B\270\376\377B\270\376" + "\377B\270\376\377B\270\376\377<\265\376\377@\267\377\377<\265\376\377<\265" + "\376\377<\265\376\377;\264\376\3775\262\377\3775\262\377\3775\262\377\377" + "5\262\377\3772\260\377\3772\260\377\3771\257\377\3771\257\377\377/\256\377" + "\377,\255\377\377,\255\377\377(\252\376\377(\252\376\377'\252\376\377\27" + "[\207\377\6\16\24\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377!\216\323\377" + "\37\246\376\377\37\246\376\377\32\243\376\377\32\243\376\377\32\243\376\377" + "\27\242\377\377\27\242\377\377\27\242\377\377\21\237\377\377\21\237\377\377" + "\21\237\377\377\17\236\377\377\27[\207\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\10" + "\27\40\377T\301\376\377V\302\377\377V\302\377\377V\302\377\377V\302\377\377" + "V\302\377\377V\302\377\377W\303\377\377W\303\377\377]\304\375\377V\302\377" + "\377W\303\377\377W\303\377\377W\303\377\377W\303\377\377V\302\377\377]\306" + "\377\377]\304\375\377]\306\377\377]\304\375\377]\306\377\377]\304\375\377" + "]\304\375\377]\304\375\377\10\24\32\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377>\211\263\377W\303\377\377W\303\377\377]\304\375\377W\303\377\377W" + "\303\377\377W\303\377\377W\303\377\377W\303\377\377W\303\377\377W\303\377" + "\377T\301\376\377W\303\377\377W\303\377\377W\303\377\377V\302\377\377T\301" + "\376\377T\301\376\377T\301\376\377T\301\376\377N\276\377\377R\300\376\377" + "R\300\376\377N\276\377\377J\274\377\377T\301\376\377J\274\377\377J\274\377" + "\377J\274\377\377J\274\377\377J\274\377\377H\273\377\377H\273\377\377H\273" + "\377\377H\273\377\377D\271\376\377D\271\376\377D\271\376\377B\270\376\377" + "B\270\376\377B\270\376\377<\265\376\377B\270\376\377<\265\376\377;\264\376" + "\377;\264\376\377;\264\376\3775\262\377\377;\264\376\3777\262\377\3771\257" + "\377\3772\260\377\3772\260\377\3772\260\377\3771\257\377\3771\257\377\377" + "/\256\377\377,\255\377\377,\255\377\377,\255\377\377\37\222\335\377\36Qn" + "\377\6\16\24\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\24""5H\377\37\246\376\377\33\244\376\377\32\243\376\377\27\242\377\377" + "\32\243\376\377\32\243\376\377\27\242\377\377\27\242\377\377\21\237\377\377" + "\21\237\377\377\17\236\377\377\17\236\377\377\11\33'\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377>\211\263\377V\302\377\377T\301\376\377T\301\376\377" + "V\302\377\377V\302\377\377V\302\377\377V\302\377\377V\302\377\377W\303\377" + "\377W\303\377\377W\303\377\377W\303\377\377]\304\375\377V\302\377\377]\304" + "\375\377W\303\377\377W\303\377\377W\303\377\377W\303\377\377W\303\377\377" + "W\303\377\377W\303\377\377V\302\377\377\35Wz\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\24""5H\377W\303\377\377W\303\377\377W\303\377\377W\303\377" + "\377W\303\377\377W\303\377\377W\303\377\377T\301\376\377W\303\377\377W\303" + "\377\377W\303\377\377T\301\376\377T\301\376\377T\301\376\377T\301\376\377" + "T\301\376\377T\301\376\377N\276\377\377N\276\377\377R\300\376\377N\276\377" + "\377N\276\377\377L\275\377\377J\274\377\377J\274\377\377J\274\377\377J\274" + "\377\377I\274\377\377I\274\377\377I\274\377\377H\273\377\377H\273\377\377" + "D\271\376\377D\271\376\377D\271\376\377B\270\376\377B\270\376\377B\270\376" + "\377@\267\377\377<\265\376\377<\265\376\377<\265\376\377<\265\376\377;\264" + "\376\377;\264\376\3775\262\377\377;\264\376\3777\262\377\3777\262\377\377" + "5\262\377\3771\257\377\3772\260\377\3771\257\377\3771\257\377\377/\256\377" + "\377,\255\377\377,\255\377\3771\210\275\377\25\211\263\377W\303\377\377V\302\377\377V\302\377\377V\302\377\377" + "V\302\377\377V\302\377\377W\303\377\377T\301\376\377T\301\376\377T\301\376" + "\377T\301\376\377T\301\376\377N\276\377\377N\276\377\377N\276\377\377N\276" + "\377\377N\276\377\377R\300\376\377N\276\377\377N\276\377\377L\275\377\377" + "L\275\377\377J\274\377\377J\274\377\377J\274\377\377J\274\377\377H\273\377" + "\377H\273\377\377I\274\377\377D\271\376\377D\271\376\377D\271\376\377D\271" + "\376\377@\267\377\377D\266\372\377@\267\377\377@\267\377\377<\265\376\377" + "<\265\376\377<\265\376\377<\265\376\377<\265\376\377<\265\376\377;\264\376" + "\3775\262\377\3777\262\377\3775\262\377\3775\262\377\3771\257\377\3772\260" + "\377\377/\256\377\3771\257\377\377/\256\377\377,\255\377\377,\255\377\377" + "*\253\377\377\17$0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\10\24\32\377\32e\225\377\32\243\376\377\33" + "\244\376\377\32\243\376\377\27\242\377\377\27\242\377\377\32\243\376\377" + "\27\242\377\377\21\237\377\377\21\237\377\377\17\236\377\377\17\236\377\377" + "\17\236\377\377\31Fb\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377D\266\372\377T\301\376\377J\274\377\377Q\277\377\377Q\277\377\377T\301" + "\376\377T\301\376\377T\301\376\377T\301\376\377T\301\376\377T\301\376\377" + "V\302\377\377V\302\377\377V\302\377\377V\302\377\377V\302\377\377V\302\377" + "\377V\302\377\377V\302\377\377T\301\376\377V\302\377\377W\303\377\377V\302" + "\377\377V\302\377\377\36Qn\377\0\0\0\377\0\0\0\377\4\12\15\377\35Wz\377C" + "\267\373\377V\302\377\377V\302\377\377V\302\377\377T\301\376\377T\301\376" + "\377T\301\376\377T\301\376\377T\301\376\377N\276\377\377T\301\376\377N\276" + "\377\377T\301\376\377N\276\377\377T\301\376\377T\301\376\377N\276\377\377" + "N\276\377\377N\276\377\377L\275\377\377L\275\377\377L\275\377\377L\275\377" + "\377J\274\377\377I\274\377\377I\274\377\377I\274\377\377H\273\377\377H\273" + "\377\377D\271\376\377D\271\376\377D\271\376\377D\271\376\377@\267\377\377" + "D\271\376\377@\267\377\377<\265\376\377@\267\377\377@\267\377\377<\265\376" + "\377<\265\376\377<\265\376\3778\263\376\3775\262\377\3777\262\377\3777\262" + "\377\3777\262\377\3775\262\377\3772\260\377\3772\260\377\3771\257\377\377" + "/\256\377\377/\256\377\377,\255\377\377,\255\377\377*\253\377\377,\255\377" + "\377\32e\225\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\11\33'\377\36k\234\377\37\246\376\377\37\246\376\377\33\244\376\377" + "\32\243\376\377\27\242\377\377\27\242\377\377\27\242\377\377\21\237\377\377" + "\32\243\376\377\21\237\377\377\21\237\377\377\17\236\377\377\16\235\377\377" + "\32\225\347\377\4\12\15\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\6\16\24\377>\211\263\377J\274\377\377T\301\376\377T\301\376\377Q\277" + "\377\377Q\277\377\377T\301\376\377T\301\376\377T\301\376\377T\301\376\377" + "T\301\376\377T\301\376\377T\301\376\377T\301\376\377T\301\376\377T\301\376" + "\377T\301\376\377T\301\376\377T\301\376\377T\301\376\377T\301\376\377T\301" + "\376\377T\301\376\377D\266\372\377\6\16\24\377\35Wz\377D\266\372\377T\301" + "\376\377V\302\377\377T\301\376\377T\301\376\377T\301\376\377T\301\376\377" + "T\301\376\377T\301\376\377T\301\376\377T\301\376\377N\276\377\377T\301\376" + "\377T\301\376\377N\276\377\377N\276\377\377L\275\377\377N\276\377\377N\276" + "\377\377L\275\377\377L\275\377\377L\275\377\377I\274\377\377L\275\377\377" + "I\274\377\377I\274\377\377I\274\377\377I\274\377\377D\271\376\377H\273\377" + "\377D\271\376\377D\271\376\377D\271\376\377@\267\377\377D\271\376\377@\267" + "\377\377@\267\377\377<\265\376\377<\265\376\377<\265\376\377<\265\376\377" + "<\265\376\377<\265\376\3778\263\376\3778\263\376\3778\263\376\3777\262\377" + "\3777\262\377\3771\257\377\3771\257\377\3771\257\377\3771\257\377\3771\257" + "\377\377/\256\377\377,\255\377\377,\255\377\377,\255\377\377*\253\377\377" + "*\253\377\377\30\230\355\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\24." + "=\377\36k\234\377\37\246\376\377\37\246\376\377\37\246\376\377\33\244\376" + "\377\32\243\376\377\27\242\377\377\27\242\377\377\27\242\377\377\27\242\377" + "\377\21\237\377\377\27\242\377\377\21\237\377\377\21\237\377\377\17\236\377" + "\377\16\235\377\377\35Wz\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\17$0\3773x\236\377I\274\377\377Q\277" + "\377\377J\274\377\377T\301\376\377J\274\377\377T\301\376\377J\274\377\377" + "T\301\376\377T\301\376\377N\276\377\377T\301\376\377T\301\376\377T\301\376" + "\377T\301\376\377Q\277\377\377T\301\376\377T\301\376\377N\276\377\377T\301" + "\376\377T\301\376\377V\302\377\377J\274\377\377T\301\376\377T\301\376\377" + "T\301\376\377T\301\376\377J\274\377\377T\301\376\377J\274\377\377T\301\376" + "\377T\301\376\377J\274\377\377N\276\377\377N\276\377\377N\276\377\377N\276" + "\377\377N\276\377\377N\276\377\377N\276\377\377L\275\377\377L\275\377\377" + "L\275\377\377L\275\377\377L\275\377\377L\275\377\377L\275\377\377G\272\377" + "\377G\272\377\377I\274\377\377H\273\377\377D\271\376\377D\271\376\377D\271" + "\376\377D\271\376\377B\270\376\377B\270\376\377@\267\377\377@\267\377\377" + "@\267\377\377@\267\377\377@\267\377\377<\265\376\377<\265\376\3778\263\376" + "\377=\265\376\3778\263\376\3778\263\376\3778\263\376\3777\262\377\3771\257" + "\376\377&p\234\3771\210\275\3772\260\377\3771\257\377\3771\257\377\377/\256" + "\377\377,\255\377\377,\255\377\377*\253\377\377,\255\377\377*\253\377\377" + "'\252\376\377'\252\376\377\24""5H\377\3\4\6\377\31Fb\377!\216\323\377#\250" + "\376\377\37\246\376\377\37\246\376\377\37\246\376\377\33\244\376\377\33\244" + "\376\377\27\242\377\377\27\242\377\377\27\242\377\377\27\242\377\377\21\237" + "\377\377\21\237\377\377\21\237\377\377\17\236\377\377\17\236\377\377\17\236" + "\377\377\22\231\364\377\4\12\15\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\16\24\377\36" + "Qn\377>\211\263\377T\301\376\377T\301\376\377J\274\377\377T\301\376\377N" + "\276\377\377N\276\377\377N\276\377\377J\274\377\377T\301\376\377J\274\377" + "\377T\301\376\377Q\277\377\377T\301\376\377J\274\377\377T\301\376\377N\276" + "\377\377T\301\376\377T\301\376\377Q\277\377\377T\301\376\377J\274\377\377" + "J\274\377\377T\301\376\377T\301\376\377T\301\376\377J\274\377\377T\301\376" + "\377J\274\377\377T\301\376\377N\276\377\377N\276\377\377N\276\377\377L\275" + "\377\377L\275\377\377L\275\377\377L\275\377\377L\275\377\377I\274\377\377" + "L\275\377\377G\272\377\377L\275\377\377G\272\377\377G\272\377\377G\272\377" + "\377G\272\377\377D\271\376\377D\271\376\377D\271\376\377D\271\376\377B\270" + "\376\377B\270\376\377B\270\376\377B\270\376\377@\267\377\377@\267\377\377" + "<\265\376\377<\265\376\377<\265\376\377<\265\376\377=\265\376\3778\263\376" + "\3778\263\376\3778\263\376\3771\210\275\377\35Wz\377\17$0\377\0\0\0\377\0" + "\0\0\377\24""5H\3771\257\377\3771\257\377\377/\256\377\377,\255\377\377," + "\255\377\377,\255\377\377*\253\377\377*\253\377\377*\253\377\377'\252\376" + "\377'\252\376\377\37\222\335\377\30\230\355\377#\250\376\377#\250\376\377" + "\37\246\376\377\37\246\376\377\37\246\376\377\33\244\376\377\33\244\376\377" + "\32\243\376\377\27\242\377\377\27\242\377\377\27\242\377\377\27\242\377\377" + "\21\237\377\377\21\237\377\377\21\237\377\377\17\236\377\377\17\236\377\377" + "\17\236\377\377\27[\207\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\11\33'\377\35Wz\377D\266\372\377N\276\377\377N\276\377" + "\377N\276\377\377T\301\376\377N\276\377\377N\276\377\377T\301\376\377T\301" + "\376\377J\274\377\377N\276\377\377T\301\376\377N\276\377\377T\301\376\377" + "J\274\377\377T\301\376\377J\274\377\377Q\277\377\377T\301\376\377T\301\376" + "\377J\274\377\377J\274\377\377T\301\376\377J\274\377\377T\301\376\377J\274" + "\377\377J\274\377\377L\275\377\377L\275\377\377L\275\377\377L\275\377\377" + "L\275\377\377L\275\377\377L\275\377\377I\274\377\377I\274\377\377I\274\377" + "\377G\272\377\377G\272\377\377G\272\377\377G\272\377\377G\272\377\377D\271" + "\376\377D\271\376\377D\271\376\377D\271\376\377B\270\376\377B\270\376\377" + "@\267\377\377@\267\377\377<\265\376\377@\267\377\377<\265\376\377<\265\376" + "\377<\265\376\377=\265\376\377\37\222\335\377&p\234\377\36Qn\377\24.=\377" + "\3\4\6\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\16\24\377" + "/\256\377\377/\256\377\377,\255\377\377,\255\377\377,\255\377\377*\253\377" + "\377*\253\377\377'\252\376\377'\252\376\377'\252\376\377#\250\376\377#\250" + "\376\377#\250\376\377#\250\376\377\37\246\376\377\37\246\376\377\37\246\376" + "\377\33\244\376\377\33\244\376\377\32\243\376\377\32\243\376\377\27\242\377" + "\377\32\243\376\377\27\242\377\377\21\237\377\377\21\237\377\377\21\237\377" + "\377\17\236\377\377\17\236\377\377\17\236\377\377\21\233\371\377\6\16\24" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\3\4\6\377D\266\372\377J\274\377\377J\274\377\377Q\277\377\377J\274\377" + "\377Q\277\377\377Q\277\377\377J\274\377\377J\274\377\377T\301\376\377J\274" + "\377\377Q\277\377\377J\274\377\377T\301\376\377Q\277\377\377J\274\377\377" + "T\301\376\377T\301\376\377J\274\377\377T\301\376\377Q\277\377\377J\274\377" + "\377J\274\377\377J\274\377\377J\274\377\377Q\277\377\377J\274\377\377J\274" + "\377\377J\274\377\377I\274\377\377I\274\377\377J\274\377\377I\274\377\377" + "H\273\377\377H\273\377\377G\272\377\377I\274\377\377G\272\377\377G\272\377" + "\377G\272\377\377D\271\376\377G\272\377\377B\270\376\377B\270\376\377B\270" + "\376\377B\270\376\377B\270\376\377B\270\376\377<\265\376\377<\265\376\377" + "?\267\377\377<\265\376\377;\264\376\377?\267\377\377;\264\376\377\32e\225" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377&\212\311\377*\253\377\377/\256\377" + "\377(\252\376\377+\254\377\377(\252\376\377'\252\376\377'\252\376\377'\252" + "\376\377#\250\376\377#\250\376\377#\250\376\377#\250\376\377\37\246\376\377" + "\37\246\376\377\37\246\376\377\37\246\376\377\32\243\376\377\32\243\376\377" + "\32\243\376\377\27\242\377\377\27\242\377\377\27\242\377\377\21\237\377\377" + "\21\237\377\377\21\237\377\377\21\237\377\377\21\237\377\377\17\236\377\377" + "\17\236\377\377\35Wz\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\24""5H\377I\274\377\377J\274\377\377J\274" + "\377\377Q\277\377\377J\274\377\377J\274\377\377J\274\377\377J\274\377\377" + "Q\277\377\377J\274\377\377J\274\377\377J\274\377\377J\274\377\377J\274\377" + "\377J\274\377\377J\274\377\377J\274\377\377J\274\377\377J\274\377\377J\274" + "\377\377J\274\377\377J\274\377\377J\274\377\377J\274\377\377I\274\377\377" + "I\274\377\377J\274\377\377J\274\377\377J\274\377\377I\274\377\377H\273\377" + "\377H\273\377\377H\273\377\377H\273\377\377H\273\377\377G\272\377\377G\272" + "\377\377G\272\377\377B\270\376\377D\271\376\377D\271\376\377B\270\376\377" + "B\270\376\377B\270\376\3778\263\376\377?\267\377\377<\265\376\377<\265\376" + "\377?\267\377\377<\265\376\377<\265\376\377<\265\376\377;\264\376\377;\264" + "\376\3775\262\377\377&\212\311\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\27" + "[\207\377/\256\377\377(\252\376\377/\256\377\377(\252\376\377'\252\376\377" + "'\252\376\377'\252\376\377#\250\376\377#\250\376\377#\250\376\377#\250\376" + "\377\37\246\376\377\37\246\376\377\37\246\376\377\33\244\376\377\32\243\376" + "\377\32\243\376\377\27\242\377\377\27\242\377\377\27\242\377\377\27\242\377" + "\377\27\242\377\377\21\237\377\377\21\237\377\377\21\237\377\377\17\236\377" + "\377\16\235\377\377\17\236\377\377\37\222\335\377\3\4\6\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\4\12\15\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377&p\234" + "\377I\274\377\377I\274\377\377I\274\377\377I\274\377\377J\274\377\377J\274" + "\377\377J\274\377\377J\274\377\377J\274\377\377J\274\377\377D\266\372\377" + "\0\0\0\377\11\33'\377\17$0\377\31Fb\377\36Qn\377&p\234\377&p\234\3773x\236" + "\377&\212\311\377&\212\311\377&\212\311\377D\266\372\377I\274\377\377I\274" + "\377\377H\273\377\377H\273\377\377I\274\377\377H\273\377\377H\273\377\377" + "H\273\377\377D\271\376\377H\273\377\377\37\222\335\377&\212\311\377&p\234" + "\377&p\234\377\27[\207\377\31Fb\377\31Fb\377\17$0\377\17$0\377\10\27\40\377" + "\0\0\0\377\35Wz\377<\265\376\377B\270\376\377<\265\376\377<\265\376\377;" + "\264\376\377;\264\376\377;\264\376\377;\264\376\377;\264\376\377;\264\376" + "\3771\257\377\377\1\2\2\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\10\27\40\377\31Fb\377\36k\234\377(\252\376\377*" + "\253\377\377(\252\376\377+\254\377\377(\252\376\377(\252\376\377%\250\376" + "\377%\250\376\377#\250\376\377#\250\376\377\37\246\376\377\37\246\376\377" + "\37\246\376\377\37\246\376\377\33\244\376\377\33\244\376\377\32\243\376\377" + "\27\242\377\377\27\242\377\377\27\242\377\377\21\237\377\377\27\242\377\377" + "\21\237\377\377\21\237\377\377\21\237\377\377\17\236\377\377\16\235\377\377" + "\16\235\377\377\17\236\377\377\24""5H\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\27[\207\377\35Wz\377\17$0\377\0\0\0\377:\262\373\377H\273" + "\377\377H\273\377\377H\273\377\377H\273\377\377H\273\377\377H\273\377\377" + "H\273\377\377I\274\377\377J\274\377\377J\274\377\377!\216\323\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377=\265\376\377H\273\377\377H\273\377\377H\273" + "\377\377I\274\377\377H\273\377\377H\273\377\377D\271\376\377H\273\377\377" + "D\271\376\377D\271\376\377\31Fb\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\31Fb\377<\265" + "\376\377<\265\376\377<\265\376\377<\265\376\377;\264\376\377:\263\374\377" + ";\264\376\3775\262\377\3775\262\377\3775\262\377\3775\262\377\377\17$0\377" + "\0\0\0\377\1\2\2\377\0\0\0\377\10\27\40\377\25 +#include +#include + +#include "libnsfb.h" +#include "libnsfb_plot.h" + +extern const struct { + unsigned int width; + unsigned int height; + unsigned int bytes_per_pixel; /* 3:RGB, 4:RGBA */ + unsigned char pixel_data[132 * 135 * 4 + 1]; +} nsglobe; + +int main(int argc, char **argv) +{ + nsfb_t *nsfb; + nsfb_bbox_t box; + nsfb_bbox_t box2; + nsfb_bbox_t box3; + uint8_t *fbptr; + int fbstride; + int p[] = { 300,300, 350,350, 400,300, 450,250, 400,200}; + int loop; + + nsfb = nsfb_init(NSFB_FRONTEND_SDL); + if (nsfb == NULL) { + fprintf(stderr, "Unable to initialise nsfb with SDL frontend\n"); + return 1; + } + + if (nsfb_init_frontend(nsfb) == -1) { + fprintf(stderr, "Unable to initialise nsfb frontend\n"); + + } + + /* get the geometry of the whole screen */ + box.x0 = box.y0 = 0; + nsfb_get_geometry(nsfb, &box.x1, &box.y1, NULL); + + nsfb_get_framebuffer(nsfb, &fbptr, &fbstride); + + /* claim the whole screen for update */ + nsfb_claim(nsfb, &box); + + /* first test, repeatedly clear the graphics area, should result in teh + * same operation as a single clear to the final colour + */ + for (loop = 0; loop < 256;loop++) { + nsfb_plot_clg(nsfb, 0xffffff00 | loop); + } + + /* draw radial lines from the origin */ + for (loop = 0; loop < box.x1; loop += 20) { + box2 = box; + box2.x1 = loop; + nsfb_plot_line(nsfb, &box2, 1, 0xff000000, false, false); + } + + /* draw radial lines from the bottom right */ + for (loop = 0; loop < box.x1; loop += 20) { + box2 = box; + box2.x0 = loop; + nsfb_plot_line(nsfb, &box2, 1, 0xffff0000, false, false); + } + + /* draw radial lines from the bottom left */ + for (loop = 0; loop < box.x1; loop += 20) { + box2.x0 = box.x0; + box2.x1 = loop; + box2.y0 = box.y1; + box2.y1 = box.y0; + nsfb_plot_line(nsfb, &box2, 1, 0xff00ff00, false, false); + } + + /* draw radial lines from the top right */ + for (loop = 0; loop < box.x1; loop += 20) { + box2.x0 = box.x1; + box2.x1 = loop; + box2.y0 = box.y0; + box2.y1 = box.y1; + nsfb_plot_line(nsfb, &box2, 1, 0xff0000ff, false, false); + } + + /* draw an unclipped rectangle */ + box2.x0 = box2.y0 = 100; + box2.x1 = box2.y1 = 300; + + nsfb_plot_rectangle_fill(nsfb, &box2, 0xff0000ff); + + nsfb_plot_rectangle(nsfb, &box2, 1, 0xff00ff00, false, false); + + nsfb_plot_polygon(nsfb, p, 5, 0xffff0000); + + nsfb_plot_set_clip(nsfb, &box2); + + box3.x0 = box3.y0 = 200; + box3.x1 = box3.y1 = 400; + + nsfb_plot_rectangle_fill(nsfb, &box3, 0xff00ffff); + + nsfb_plot_rectangle(nsfb, &box3, 1, 0xffffff00, false, false); + + for (loop = 100; loop < 400;loop++) { + nsfb_plot_point(nsfb, loop, 150, 0xffaa1111); + nsfb_plot_point(nsfb, loop, 160, 0x99aa1111); + } + + nsfb_plot_set_clip(nsfb, NULL); + + box3.x0 = box3.y0 = 400; + box3.x1 = box3.y1 = 600; + + nsfb_plot_ellipse_fill(nsfb, &box3, 0xffff0000); + + nsfb_plot_ellipse(nsfb, &box3, 0xff0000ff); + + box3.x0 = 500; + box3.x1 = 700; + box3.y0 = 400; + box3.y1 = 500; + + nsfb_plot_ellipse_fill(nsfb, &box3, 0xffff0000); + + nsfb_plot_ellipse(nsfb, &box3, 0xff0000ff); + + box3.x0 = 600; + box3.x1 = 700; + box3.y0 = 300; + box3.y1 = 500; + + nsfb_plot_ellipse_fill(nsfb, &box3, 0xff0000ff); + + nsfb_plot_ellipse(nsfb, &box3, 0xffff0000); + + nsfb_plot_copy(nsfb,400,400,100,100,600,200); + + box3.x0 = 50; + box3.x1 = 200; + box3.y0 = 300; + box3.y1 = 500; + + nsfb_plot_bitmap(nsfb, &box3, (nsfb_colour_t *)nsglobe.pixel_data, nsglobe.width, nsglobe.height, nsglobe.width, true); + + nsfb_release(nsfb, &box); + + /* random rectangles in clipped area*/ + box2.x0 = 400; + box2.y0 = 50; + box2.x1 = 600; + box2.y1 = 100; + + nsfb_plot_set_clip(nsfb, &box2); + + srand(1234); + + for (loop=0; loop < 10000; loop++) { + nsfb_claim(nsfb, &box2); + box3.x0 = rand() / (RAND_MAX / box.x1); + box3.y0 = rand() / (RAND_MAX / box.y1); + box3.x1 = rand() / (RAND_MAX / 400); + box3.y1 = rand() / (RAND_MAX / 400); + nsfb_plot_rectangle_fill(nsfb, &box3, 0xff000000 | rand()); + nsfb_release(nsfb, &box2); + } + + + while (true ) + nsfb_input(nsfb); + + return 0; +} diff --git a/usage b/usage new file mode 100644 index 0000000..7bb8d7d --- /dev/null +++ b/usage @@ -0,0 +1,40 @@ +Framebuffer abstraction library +------------------------------- + +This library provides a generic abstraction to a linear section of +memory which coresponds to a visible array of pixel elements on a +display device. The memory starts at a base location and is logically +split into rows, the length of each row in memory is refered to as the +stride length. + +The display device may use differing numbers of bits to represent each +pixel, this represented by the bpp value and may be one of 1, 4, 8, +16, 24 or 32. The library assumes packed pixels and does not support +colour depths which do not correspond to the bpp. The 1bpp mode is a +fixed black and white pallette, the 4 and 8 bpp modes use a 16 and 256 +entry pallette respectively and the 16, and 24 bpp modes are direct +indexed RGB modes in 565 and 888 format. The 32bpp mode is RGB in 888 +format with the top byte unused. + +The library may be coupled to several display devices. The available +devices are controlled by compile time configuration. + +Usage +----- + +The library is initialised by calling nsfb_init() with the frontend +type as a parameter. The nsfb_t pointer returned is used in all calls +to the library. The geometry and physical settings may be altered +using the nsfb_set_geometry() and nsfb_set_physical() functions. The +frontend *must* then be initialised. The nsfb_set_geometry() and +nsfb_set_physical() functions may be called at any time, however they +are likely to fail once the frontend has been initialised. + +The nsfb_finalise() function should be called to shut the library down +when finished. + +When directly acessing the framebuffer memory the nsfb_claim() and +nsfb_release() calls should be used. These allow the frontends to +syncronise access and only update the altered regions of the drawing +area. Note the area claimed and released may not neccisarily be the +same. -- cgit v1.2.3