1 /*
2  * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
3  *
4  * Framebuffer interface
5  *
6  * This file is part of NetSurf, http://www.netsurf-browser.org/
7  *
8  * NetSurf is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; version 2 of the License.
11  *
12  * NetSurf is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <stdio.h>
22 #include <stdbool.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include <libnsfb.h>
27 #include <libnsfb_plot.h>
28 #include <libnsfb_event.h>
29 #include <libnsfb_cursor.h>
30 
31 #include "utils/utils.h"
32 #include "utils/log.h"
33 #include "utils/utf8.h"
34 #include "netsurf/browser_window.h"
35 #include "netsurf/plotters.h"
36 #include "netsurf/bitmap.h"
37 
38 #include "framebuffer/gui.h"
39 #include "framebuffer/fbtk.h"
40 #include "framebuffer/framebuffer.h"
41 #include "framebuffer/font.h"
42 #include "framebuffer/bitmap.h"
43 
44 /* netsurf framebuffer library handle */
45 static nsfb_t *nsfb;
46 
47 
48 /**
49  * \brief Sets a clip rectangle for subsequent plot operations.
50  *
51  * \param ctx The current redraw context.
52  * \param clip The rectangle to limit all subsequent plot
53  *              operations within.
54  * \return NSERROR_OK on success else error code.
55  */
56 static nserror
framebuffer_plot_clip(const struct redraw_context * ctx,const struct rect * clip)57 framebuffer_plot_clip(const struct redraw_context *ctx, const struct rect *clip)
58 {
59 	nsfb_bbox_t nsfb_clip;
60 	nsfb_clip.x0 = clip->x0;
61 	nsfb_clip.y0 = clip->y0;
62 	nsfb_clip.x1 = clip->x1;
63 	nsfb_clip.y1 = clip->y1;
64 
65 	if (!nsfb_plot_set_clip(nsfb, &nsfb_clip)) {
66 		return NSERROR_INVALID;
67 	}
68 	return NSERROR_OK;
69 }
70 
71 
72 /**
73  * Plots an arc
74  *
75  * plot an arc segment around (x,y), anticlockwise from angle1
76  *  to angle2. Angles are measured anticlockwise from
77  *  horizontal, in degrees.
78  *
79  * \param ctx The current redraw context.
80  * \param style Style controlling the arc plot.
81  * \param x The x coordinate of the arc.
82  * \param y The y coordinate of the arc.
83  * \param radius The radius of the arc.
84  * \param angle1 The start angle of the arc.
85  * \param angle2 The finish angle of the arc.
86  * \return NSERROR_OK on success else error code.
87  */
88 static nserror
framebuffer_plot_arc(const struct redraw_context * ctx,const plot_style_t * style,int x,int y,int radius,int angle1,int angle2)89 framebuffer_plot_arc(const struct redraw_context *ctx,
90 	       const plot_style_t *style,
91 	       int x, int y, int radius, int angle1, int angle2)
92 {
93 	if (!nsfb_plot_arc(nsfb, x, y, radius, angle1, angle2, style->fill_colour)) {
94 		return NSERROR_INVALID;
95 	}
96 	return NSERROR_OK;
97 }
98 
99 
100 /**
101  * Plots a circle
102  *
103  * Plot a circle centered on (x,y), which is optionally filled.
104  *
105  * \param ctx The current redraw context.
106  * \param style Style controlling the circle plot.
107  * \param x x coordinate of circle centre.
108  * \param y y coordinate of circle centre.
109  * \param radius circle radius.
110  * \return NSERROR_OK on success else error code.
111  */
112 static nserror
framebuffer_plot_disc(const struct redraw_context * ctx,const plot_style_t * style,int x,int y,int radius)113 framebuffer_plot_disc(const struct redraw_context *ctx,
114 		const plot_style_t *style,
115 		int x, int y, int radius)
116 {
117 	nsfb_bbox_t ellipse;
118 	ellipse.x0 = x - radius;
119 	ellipse.y0 = y - radius;
120 	ellipse.x1 = x + radius;
121 	ellipse.y1 = y + radius;
122 
123 	if (style->fill_type != PLOT_OP_TYPE_NONE) {
124 		nsfb_plot_ellipse_fill(nsfb, &ellipse, style->fill_colour);
125 	}
126 
127 	if (style->stroke_type != PLOT_OP_TYPE_NONE) {
128 		nsfb_plot_ellipse(nsfb, &ellipse, style->stroke_colour);
129 	}
130 	return NSERROR_OK;
131 }
132 
133 
134 /**
135  * Plots a line
136  *
137  * plot a line from (x0,y0) to (x1,y1). Coordinates are at
138  *  centre of line width/thickness.
139  *
140  * \param ctx The current redraw context.
141  * \param style Style controlling the line plot.
142  * \param line A rectangle defining the line to be drawn
143  * \return NSERROR_OK on success else error code.
144  */
145 static nserror
framebuffer_plot_line(const struct redraw_context * ctx,const plot_style_t * style,const struct rect * line)146 framebuffer_plot_line(const struct redraw_context *ctx,
147 		const plot_style_t *style,
148 		const struct rect *line)
149 {
150 	nsfb_bbox_t rect;
151 	nsfb_plot_pen_t pen;
152 
153 	rect.x0 = line->x0;
154 	rect.y0 = line->y0;
155 	rect.x1 = line->x1;
156 	rect.y1 = line->y1;
157 
158 	if (style->stroke_type != PLOT_OP_TYPE_NONE) {
159 
160 		if (style->stroke_type == PLOT_OP_TYPE_DOT) {
161 			pen.stroke_type = NFSB_PLOT_OPTYPE_PATTERN;
162 			pen.stroke_pattern = 0xAAAAAAAA;
163 		} else if (style->stroke_type == PLOT_OP_TYPE_DASH) {
164 			pen.stroke_type = NFSB_PLOT_OPTYPE_PATTERN;
165 			pen.stroke_pattern = 0xF0F0F0F0;
166 		} else {
167 			pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID;
168 		}
169 
170 		pen.stroke_colour = style->stroke_colour;
171 		pen.stroke_width = plot_style_fixed_to_int(style->stroke_width);
172 		nsfb_plot_line(nsfb, &rect, &pen);
173 	}
174 
175 	return NSERROR_OK;
176 }
177 
178 
179 /**
180  * Plots a rectangle.
181  *
182  * The rectangle can be filled an outline or both controlled
183  *  by the plot style The line can be solid, dotted or
184  *  dashed. Top left corner at (x0,y0) and rectangle has given
185  *  width and height.
186  *
187  * \param ctx The current redraw context.
188  * \param style Style controlling the rectangle plot.
189  * \param nsrect A rectangle defining the line to be drawn
190  * \return NSERROR_OK on success else error code.
191  */
192 static nserror
framebuffer_plot_rectangle(const struct redraw_context * ctx,const plot_style_t * style,const struct rect * nsrect)193 framebuffer_plot_rectangle(const struct redraw_context *ctx,
194 		     const plot_style_t *style,
195 		     const struct rect *nsrect)
196 {
197 	nsfb_bbox_t rect;
198 	bool dotted = false;
199 	bool dashed = false;
200 
201 	rect.x0 = nsrect->x0;
202 	rect.y0 = nsrect->y0;
203 	rect.x1 = nsrect->x1;
204 	rect.y1 = nsrect->y1;
205 
206 	if (style->fill_type != PLOT_OP_TYPE_NONE) {
207 		nsfb_plot_rectangle_fill(nsfb, &rect, style->fill_colour);
208 	}
209 
210 	if (style->stroke_type != PLOT_OP_TYPE_NONE) {
211 		if (style->stroke_type == PLOT_OP_TYPE_DOT) {
212 			dotted = true;
213 		}
214 
215 		if (style->stroke_type == PLOT_OP_TYPE_DASH) {
216 			dashed = true;
217 		}
218 
219 		nsfb_plot_rectangle(nsfb, &rect,
220 				plot_style_fixed_to_int(style->stroke_width),
221 				style->stroke_colour, dotted, dashed);
222 	}
223 	return NSERROR_OK;
224 }
225 
226 
227 /**
228  * Plot a polygon
229  *
230  * Plots a filled polygon with straight lines between
231  * points. The lines around the edge of the ploygon are not
232  * plotted. The polygon is filled with the non-zero winding
233  * rule.
234  *
235  * \param ctx The current redraw context.
236  * \param style Style controlling the polygon plot.
237  * \param p verticies of polygon
238  * \param n number of verticies.
239  * \return NSERROR_OK on success else error code.
240  */
241 static nserror
framebuffer_plot_polygon(const struct redraw_context * ctx,const plot_style_t * style,const int * p,unsigned int n)242 framebuffer_plot_polygon(const struct redraw_context *ctx,
243 		   const plot_style_t *style,
244 		   const int *p,
245 		   unsigned int n)
246 {
247 	if (!nsfb_plot_polygon(nsfb, p, n, style->fill_colour)) {
248 		return NSERROR_INVALID;
249 	}
250 	return NSERROR_OK;
251 }
252 
253 
254 /**
255  * Plots a path.
256  *
257  * Path plot consisting of cubic Bezier curves. Line and fill colour is
258  *  controlled by the plot style.
259  *
260  * \param ctx The current redraw context.
261  * \param pstyle Style controlling the path plot.
262  * \param p elements of path
263  * \param n nunber of elements on path
264  * \param transform A transform to apply to the path.
265  * \return NSERROR_OK on success else error code.
266  */
267 static nserror
framebuffer_plot_path(const struct redraw_context * ctx,const plot_style_t * pstyle,const float * p,unsigned int n,const float transform[6])268 framebuffer_plot_path(const struct redraw_context *ctx,
269 		const plot_style_t *pstyle,
270 		const float *p,
271 		unsigned int n,
272 		const float transform[6])
273 {
274 	NSLOG(netsurf, INFO, "path unimplemented");
275 	return NSERROR_OK;
276 }
277 
278 
279 /**
280  * Plot a bitmap
281  *
282  * Tiled plot of a bitmap image. (x,y) gives the top left
283  * coordinate of an explicitly placed tile. From this tile the
284  * image can repeat in all four directions -- up, down, left
285  * and right -- to the extents given by the current clip
286  * rectangle.
287  *
288  * The bitmap_flags say whether to tile in the x and y
289  * directions. If not tiling in x or y directions, the single
290  * image is plotted. The width and height give the dimensions
291  * the image is to be scaled to.
292  *
293  * \param ctx The current redraw context.
294  * \param bitmap The bitmap to plot
295  * \param x The x coordinate to plot the bitmap
296  * \param y The y coordiante to plot the bitmap
297  * \param width The width of area to plot the bitmap into
298  * \param height The height of area to plot the bitmap into
299  * \param bg the background colour to alpha blend into
300  * \param flags the flags controlling the type of plot operation
301  * \return NSERROR_OK on success else error code.
302  */
303 static nserror
framebuffer_plot_bitmap(const struct redraw_context * ctx,struct bitmap * bitmap,int x,int y,int width,int height,colour bg,bitmap_flags_t flags)304 framebuffer_plot_bitmap(const struct redraw_context *ctx,
305 		  struct bitmap *bitmap,
306 		  int x, int y,
307 		  int width,
308 		  int height,
309 		  colour bg,
310 		  bitmap_flags_t flags)
311 {
312 	nsfb_bbox_t loc;
313 	nsfb_bbox_t clipbox;
314 	bool repeat_x = (flags & BITMAPF_REPEAT_X);
315 	bool repeat_y = (flags & BITMAPF_REPEAT_Y);
316 	int bmwidth;
317 	int bmheight;
318 	int bmstride;
319 	enum nsfb_format_e bmformat;
320 	unsigned char *bmptr;
321 	nsfb_t *bm = (nsfb_t *)bitmap;
322 
323 	/* x and y define coordinate of top left of of the initial explicitly
324 	 * placed tile. The width and height are the image scaling and the
325 	 * bounding box defines the extent of the repeat (which may go in all
326 	 * four directions from the initial tile).
327 	 */
328 
329 	if (!(repeat_x || repeat_y)) {
330 		/* Not repeating at all, so just plot it */
331 		loc.x0 = x;
332 		loc.y0 = y;
333 		loc.x1 = loc.x0 + width;
334 		loc.y1 = loc.y0 + height;
335 
336 		if (!nsfb_plot_copy(bm, NULL, nsfb, &loc)) {
337 			return NSERROR_INVALID;
338 		}
339 		return NSERROR_OK;
340 	}
341 
342 	nsfb_plot_get_clip(nsfb, &clipbox);
343 	nsfb_get_geometry(bm, &bmwidth, &bmheight, &bmformat);
344 	nsfb_get_buffer(bm, &bmptr, &bmstride);
345 
346 	/* Optimise tiled plots of 1x1 bitmaps by replacing with a flat fill
347 	 * of the area.  Can only be done when image is fully opaque. */
348 	if ((bmwidth == 1) && (bmheight == 1)) {
349 		if ((*(nsfb_colour_t *)bmptr & 0xff000000) != 0) {
350 			if (!nsfb_plot_rectangle_fill(nsfb, &clipbox,
351 						      *(nsfb_colour_t *)bmptr)) {
352 				return NSERROR_INVALID;
353 			}
354 			return NSERROR_OK;
355 		}
356 	}
357 
358 	/* Optimise tiled plots of bitmaps scaled to 1x1 by replacing with
359 	 * a flat fill of the area.  Can only be done when image is fully
360 	 * opaque. */
361 	if ((width == 1) && (height == 1)) {
362 		if (framebuffer_bitmap_get_opaque(bm)) {
363 			/** TODO: Currently using top left pixel. Maybe centre
364 			 *        pixel or average value would be better. */
365 			if (!nsfb_plot_rectangle_fill(nsfb, &clipbox,
366 						      *(nsfb_colour_t *)bmptr)) {
367 				return NSERROR_INVALID;
368 			}
369 			return NSERROR_OK;
370 		}
371 	}
372 
373 	/* get left most tile position */
374 	if (repeat_x) {
375 		for (; x > clipbox.x0; x -= width);
376 	}
377 
378 	/* get top most tile position */
379 	if (repeat_y) {
380 		for (; y > clipbox.y0; y -= height);
381 	}
382 
383 	/* set up top left tile location */
384 	loc.x0 = x;
385 	loc.y0 = y;
386 	loc.x1 = loc.x0 + width;
387 	loc.y1 = loc.y0 + height;
388 
389 	/* plot tiling across and down to extents */
390 	nsfb_plot_bitmap_tiles(nsfb, &loc,
391 			repeat_x ? ((clipbox.x1 - x) + width  - 1) / width  : 1,
392 			repeat_y ? ((clipbox.y1 - y) + height - 1) / height : 1,
393 			(nsfb_colour_t *)bmptr, bmwidth, bmheight,
394 			bmstride * 8 / 32, bmformat == NSFB_FMT_ABGR8888);
395 
396 	return NSERROR_OK;
397 }
398 
399 
400 #ifdef FB_USE_FREETYPE
401 /**
402  * Text plotting.
403  *
404  * \param ctx The current redraw context.
405  * \param fstyle plot style for this text
406  * \param x x coordinate
407  * \param y y coordinate
408  * \param text UTF-8 string to plot
409  * \param length length of string, in bytes
410  * \return NSERROR_OK on success else error code.
411  */
412 static nserror
framebuffer_plot_text(const struct redraw_context * ctx,const struct plot_font_style * fstyle,int x,int y,const char * text,size_t length)413 framebuffer_plot_text(const struct redraw_context *ctx,
414 		const struct plot_font_style *fstyle,
415 		int x,
416 		int y,
417 		const char *text,
418 		size_t length)
419 {
420 	uint32_t ucs4;
421 	size_t nxtchr = 0;
422 	FT_Glyph glyph;
423 	FT_BitmapGlyph bglyph;
424 	nsfb_bbox_t loc;
425 
426 	while (nxtchr < length) {
427 		ucs4 = utf8_to_ucs4(text + nxtchr, length - nxtchr);
428 		nxtchr = utf8_next(text, length, nxtchr);
429 
430 		glyph = fb_getglyph(fstyle, ucs4);
431 		if (glyph == NULL)
432 			continue;
433 
434 		if (glyph->format == FT_GLYPH_FORMAT_BITMAP) {
435 			bglyph = (FT_BitmapGlyph)glyph;
436 
437 			loc.x0 = x + bglyph->left;
438 			loc.y0 = y - bglyph->top;
439 			loc.x1 = loc.x0 + bglyph->bitmap.width;
440 			loc.y1 = loc.y0 + bglyph->bitmap.rows;
441 
442 			/* now, draw to our target surface */
443 			if (bglyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
444 			    nsfb_plot_glyph1(nsfb,
445 					     &loc,
446 					     bglyph->bitmap.buffer,
447 					     bglyph->bitmap.pitch,
448 					     fstyle->foreground);
449 			} else {
450 			    nsfb_plot_glyph8(nsfb,
451 					     &loc,
452 					     bglyph->bitmap.buffer,
453 					     bglyph->bitmap.pitch,
454 					     fstyle->foreground);
455 			}
456 		}
457 		x += glyph->advance.x >> 16;
458 
459 	}
460 	return NSERROR_OK;
461 
462 }
463 
464 #else
465 
466 /**
467  * Text plotting.
468  *
469  * \param ctx The current redraw context.
470  * \param fstyle plot style for this text
471  * \param x x coordinate
472  * \param y y coordinate
473  * \param text UTF-8 string to plot
474  * \param length length of string, in bytes
475  * \return NSERROR_OK on success else error code.
476  */
477 static nserror
framebuffer_plot_text(const struct redraw_context * ctx,const struct plot_font_style * fstyle,int x,int y,const char * text,size_t length)478 framebuffer_plot_text(const struct redraw_context *ctx,
479 		const struct plot_font_style *fstyle,
480 		int x,
481 		int y,
482 		const char *text,
483 		size_t length)
484 {
485     enum fb_font_style style = fb_get_font_style(fstyle);
486     int size = fb_get_font_size(fstyle);
487     const uint8_t *chrp;
488     size_t nxtchr = 0;
489     nsfb_bbox_t loc;
490     uint32_t ucs4;
491     int p = FB_FONT_PITCH * size;
492     int w = FB_FONT_WIDTH * size;
493     int h = FB_FONT_HEIGHT * size;
494 
495     y -= ((h * 3) / 4);
496     /* the coord is the bottom-left of the pixels offset by 1 to make
497      * it work since fb coords are the top-left of pixels */
498     y += 1;
499 
500     while (nxtchr < length) {
501 	ucs4 = utf8_to_ucs4(text + nxtchr, length - nxtchr);
502 	nxtchr = utf8_next(text, length, nxtchr);
503 
504 	if (!codepoint_displayable(ucs4))
505 		continue;
506 
507 	loc.x0 = x;
508 	loc.y0 = y;
509 	loc.x1 = loc.x0 + w;
510 	loc.y1 = loc.y0 + h;
511 
512 	chrp = fb_get_glyph(ucs4, style, size);
513 	nsfb_plot_glyph1(nsfb, &loc, chrp, p, fstyle->foreground);
514 
515 	x += w;
516 
517     }
518 
519     return NSERROR_OK;
520 }
521 #endif
522 
523 
524 /** framebuffer plot operation table */
525 const struct plotter_table fb_plotters = {
526 	.clip = framebuffer_plot_clip,
527 	.arc = framebuffer_plot_arc,
528 	.disc = framebuffer_plot_disc,
529 	.line = framebuffer_plot_line,
530 	.rectangle = framebuffer_plot_rectangle,
531 	.polygon = framebuffer_plot_polygon,
532 	.path = framebuffer_plot_path,
533 	.bitmap = framebuffer_plot_bitmap,
534 	.text = framebuffer_plot_text,
535 	.option_knockout = true,
536 };
537 
538 
framebuffer_format_from_bpp(int bpp,enum nsfb_format_e * fmt)539 static bool framebuffer_format_from_bpp(int bpp, enum nsfb_format_e *fmt)
540 {
541 	switch (bpp) {
542 	case 32:
543 		*fmt = NSFB_FMT_XRGB8888;
544 		break;
545 
546 	case 24:
547 		*fmt = NSFB_FMT_RGB888;
548 		break;
549 
550 	case 16:
551 		*fmt = NSFB_FMT_RGB565;
552 		break;
553 
554 	case 8:
555 		*fmt = NSFB_FMT_I8;
556 		break;
557 
558 	case 4:
559 		*fmt = NSFB_FMT_I4;
560 		break;
561 
562 	case 1:
563 		*fmt = NSFB_FMT_I1;
564 		break;
565 
566 	default:
567 		NSLOG(netsurf, INFO, "Bad bits per pixel (%d)\n", bpp);
568 		return false;
569 	}
570 
571 	return true;
572 }
573 
574 
575 
576 nsfb_t *
framebuffer_initialise(const char * fename,int width,int height,int bpp)577 framebuffer_initialise(const char *fename, int width, int height, int bpp)
578 {
579     enum nsfb_type_e fbtype;
580     enum nsfb_format_e fbfmt;
581 
582     /* bpp is a proxy for the framebuffer format */
583     if (framebuffer_format_from_bpp(bpp, &fbfmt) == false) {
584 	return NULL;
585     }
586 
587     fbtype = nsfb_type_from_name(fename);
588     if (fbtype == NSFB_SURFACE_NONE) {
589 	NSLOG(netsurf, INFO,
590               "The %s surface is not available from libnsfb\n", fename);
591 	return NULL;
592     }
593 
594     nsfb = nsfb_new(fbtype);
595     if (nsfb == NULL) {
596 	NSLOG(netsurf, INFO, "Unable to create %s fb surface\n", fename);
597 	return NULL;
598     }
599 
600     if (nsfb_set_geometry(nsfb, width, height, fbfmt) == -1) {
601 	NSLOG(netsurf, INFO, "Unable to set surface geometry\n");
602 	nsfb_free(nsfb);
603 	return NULL;
604     }
605 
606     nsfb_cursor_init(nsfb);
607 
608     if (nsfb_init(nsfb) == -1) {
609 	NSLOG(netsurf, INFO, "Unable to initialise nsfb surface\n");
610 	nsfb_free(nsfb);
611 	return NULL;
612     }
613 
614     return nsfb;
615 
616 }
617 
618 bool
framebuffer_resize(nsfb_t * nsfb,int width,int height,int bpp)619 framebuffer_resize(nsfb_t *nsfb, int width, int height, int bpp)
620 {
621     enum nsfb_format_e fbfmt;
622 
623     /* bpp is a proxy for the framebuffer format */
624     if (framebuffer_format_from_bpp(bpp, &fbfmt) == false) {
625 	return false;
626     }
627 
628     if (nsfb_set_geometry(nsfb, width, height, fbfmt) == -1) {
629 	NSLOG(netsurf, INFO, "Unable to change surface geometry\n");
630 	return false;
631     }
632 
633     return true;
634 
635 }
636 
637 void
framebuffer_finalise(void)638 framebuffer_finalise(void)
639 {
640     nsfb_free(nsfb);
641 }
642 
643 bool
framebuffer_set_cursor(struct fbtk_bitmap * bm)644 framebuffer_set_cursor(struct fbtk_bitmap *bm)
645 {
646     return nsfb_cursor_set(nsfb, (nsfb_colour_t *)bm->pixdata, bm->width, bm->height, bm->width, bm->hot_x, bm->hot_y);
647 }
648 
framebuffer_set_surface(nsfb_t * new_nsfb)649 nsfb_t *framebuffer_set_surface(nsfb_t *new_nsfb)
650 {
651 	nsfb_t *old_nsfb;
652 	old_nsfb = nsfb;
653 	nsfb = new_nsfb;
654 	return old_nsfb;
655 }
656