1 /*
2  * Copyright 2008-09, 2012-13 Chris Young <chris@unsatisfactorysoftware.co.uk>
3  *
4  * This file is part of NetSurf, http://www.netsurf-browser.org/
5  *
6  * NetSurf is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * NetSurf is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "amiga/os3support.h"
20 
21 #include <proto/exec.h>
22 #include <proto/intuition.h>
23 #include <proto/layers.h>
24 #include <proto/graphics.h>
25 
26 #include <intuition/intuition.h>
27 #include <graphics/rpattr.h>
28 #include <graphics/gfxmacros.h>
29 #include <graphics/gfxbase.h>
30 
31 #ifdef __amigaos4__
32 #include <graphics/blitattr.h>
33 #include <graphics/composite.h>
34 #endif
35 
36 #include <math.h>
37 #include <assert.h>
38 #include <stdlib.h>
39 
40 #include "utils/nsoption.h"
41 #include "utils/utils.h"
42 #include "utils/log.h"
43 #include "netsurf/css.h"
44 #include "netsurf/mouse.h"
45 #include "netsurf/window.h"
46 
47 #include "amiga/plotters.h"
48 #include "amiga/bitmap.h"
49 #include "amiga/font.h"
50 #include "amiga/gui.h"
51 #include "amiga/memory.h"
52 #include "amiga/misc.h"
53 #include "amiga/rtg.h"
54 #include "amiga/utf8.h"
55 
56 HOOKF(void, ami_bitmap_tile_hook, struct RastPort *, rp, struct BackFillMessage *);
57 
58 struct bfbitmap {
59 	struct BitMap *bm;
60 	ULONG width;
61 	ULONG height;
62 	int offsetx;
63 	int offsety;
64 	APTR mask;
65 	bool palette_mapped;
66 };
67 
68 struct ami_plot_pen {
69 	struct MinNode node;
70 	ULONG pen;
71 };
72 
73 struct bez_point {
74 	float x;
75 	float y;
76 };
77 
78 struct gui_globals {
79 	struct BitMap *bm;
80 	struct RastPort *rp;
81 	struct Layer_Info *layerinfo;
82 	APTR areabuf;
83 	APTR tmprasbuf;
84 	struct Rectangle rect;
85 	struct MinList *shared_pens;
86 	bool managed_pen_list;
87 	bool palette_mapped;
88 	ULONG apen;
89 	ULONG open;
90 	LONG apen_num;
91 	LONG open_num;
92 	int width;  /* size of bm and    */
93 	int height; /* associated memory */
94 };
95 
96 static int init_layers_count = 0;
97 static APTR pool_pens = NULL;
98 static bool palette_mapped = true; /* palette-mapped state for the screen */
99 
100 #ifndef M_PI /* For some reason we don't always get this from math.h */
101 #define M_PI		3.14159265358979323846
102 #endif
103 
104 #define PATT_DOT  0xAAAA
105 #define PATT_DASH 0xCCCC
106 #define PATT_LINE 0xFFFF
107 
108 /* This defines the size of the list for Area* functions.
109    25000 = 5000 vectors
110   */
111 #define AREA_SIZE 25000
112 
ami_plot_ra_alloc(ULONG width,ULONG height,bool force32bit,bool alloc_pen_list)113 struct gui_globals *ami_plot_ra_alloc(ULONG width, ULONG height, bool force32bit, bool alloc_pen_list)
114 {
115 	/* init shared bitmaps */
116  	int depth = 32;
117 	struct BitMap *friend = NULL;
118 	struct Screen *scrn = ami_gui_get_screen();
119 
120 	struct gui_globals *gg = malloc(sizeof(struct gui_globals));
121 
122 	if(force32bit == false) depth = GetBitMapAttr(scrn->RastPort.BitMap, BMA_DEPTH);
123 	NSLOG(netsurf, INFO, "Screen depth = %d", depth);
124 
125 #ifdef __amigaos4__
126 	if(depth < 16) {
127 		gg->palette_mapped = true;
128 		if(force32bit == false) palette_mapped = true;
129 	} else {
130 		gg->palette_mapped = false;
131 		if(force32bit == false) palette_mapped = false;
132 	}
133 #else
134 	/* Friend BitMaps are weird.
135 	 * For OS4, we shouldn't use a friend BitMap here (see below).
136 	 * For OS3 AGA, we get no display blitted if we use a friend BitMap,
137 	 * however on RTG it seems to be a benefit.
138 	 */
139 	if(nsoption_bool(friend_bitmap) == true) {
140 		friend = scrn->RastPort.BitMap;
141 	} else {
142 		/* Force friend BitMaps on for obvious RTG screens under OS3.
143 		 * If we get a bit smarter about this we can lose the user option. */
144 		if((depth > 8) && (force32bit == false)) friend = scrn->RastPort.BitMap;
145 	}
146 
147 	/* OS3 is locked to using palette-mapped display even on RTG.
148 	 * To change this, comment out the below and build with the similar OS4 lines above.
149 	 * Various bits of RTG code are OS4-only and OS3 versions will need to be written,
150 	 * however a brief test reveals a negative performance benefit, so this lock to a
151 	 * palette-mapped display is most likely permanent.
152 	 */
153 #warning OS3 locked to palette-mapped modes
154 	gg->palette_mapped = true;
155 	palette_mapped = true;
156 	if(depth > 8) depth = 8;
157 #endif
158 
159 	/* Probably need to fix this next line */
160 	if(gg->palette_mapped == true) nsoption_set_bool(font_antialiasing, false);
161 
162 	if(!width) width = nsoption_int(redraw_tile_size_x);
163 	if(!height) height = nsoption_int(redraw_tile_size_y);
164 	gg->width = width;
165 	gg->height = height;
166 
167 	gg->layerinfo = NewLayerInfo();
168 	gg->areabuf = malloc(AREA_SIZE);
169 
170 	/* OS3/AGA requires this to be in chip mem.  RTG would probably rather it wasn't. */
171 	gg->tmprasbuf = ami_memory_chip_alloc(width * height);
172 
173 	if(gg->palette_mapped == true) {
174 		gg->bm = AllocBitMap(width, height, depth, 0, friend);
175 	} else {
176 #ifdef __amigaos4__
177 		/* Screen depth is reported as 24 even when it's actually 32-bit.
178 		 * We get freezes and other problems on OS4 if we befriend at any
179 		 * other depths, hence this check.
180 		 * \todo use friend BitMaps but avoid CompositeTags() at non-32-bit
181 		 * as that seems to be the cause of the problems.
182 		 */
183 		if((depth >= 24) && (force32bit == false)) friend = scrn->RastPort.BitMap;
184 #endif
185 		gg->bm = ami_rtg_allocbitmap(width, height, 32, 0, friend, RGBFB_A8R8G8B8);
186 	}
187 
188 	if(!gg->bm) amiga_warn_user("NoMemory","");
189 
190 	gg->rp = malloc(sizeof(struct RastPort));
191 	if(!gg->rp) amiga_warn_user("NoMemory","");
192 
193 	InitRastPort(gg->rp);
194 	gg->rp->BitMap = gg->bm;
195 
196 	SetDrMd(gg->rp,BGBACKFILL);
197 
198 	gg->rp->Layer = CreateUpfrontLayer(gg->layerinfo,gg->rp->BitMap,0,0,
199 					width-1, height-1, LAYERSIMPLE, NULL);
200 
201 	InstallLayerHook(gg->rp->Layer,LAYERS_NOBACKFILL);
202 
203 	gg->rp->AreaInfo = malloc(sizeof(struct AreaInfo));
204 	if((!gg->areabuf) || (!gg->rp->AreaInfo))	amiga_warn_user("NoMemory","");
205 
206 	InitArea(gg->rp->AreaInfo, gg->areabuf, AREA_SIZE/5);
207 
208 	gg->rp->TmpRas = malloc(sizeof(struct TmpRas));
209 	if((!gg->tmprasbuf) || (!gg->rp->TmpRas))	amiga_warn_user("NoMemory","");
210 
211 	InitTmpRas(gg->rp->TmpRas, gg->tmprasbuf, width*height);
212 
213 	gg->shared_pens = NULL;
214 	gg->managed_pen_list = false;
215 
216 	if(gg->palette_mapped == true) {
217 		if(pool_pens == NULL) {
218 			pool_pens = ami_memory_itempool_create(sizeof(struct ami_plot_pen));
219 		}
220 
221 		if(alloc_pen_list == true) {
222 			gg->shared_pens = ami_AllocMinList();
223 			gg->managed_pen_list = true;
224 		}
225 	}
226 
227 	gg->apen = 0x00000000;
228 	gg->open = 0x00000000;
229 	gg->apen_num = -1;
230 	gg->open_num = -1;
231 
232 	init_layers_count++;
233 	NSLOG(netsurf, INFO, "Layer initialised (total: %d)",
234 	      init_layers_count);
235 
236 	return gg;
237 }
238 
ami_plot_ra_free(struct gui_globals * gg)239 void ami_plot_ra_free(struct gui_globals *gg)
240 {
241 	init_layers_count--;
242 
243 	if(init_layers_count < 0) return;
244 
245 	if((init_layers_count == 0) && (pool_pens != NULL)) {
246 		ami_memory_itempool_delete(pool_pens);
247 		pool_pens = NULL;
248 	}
249 
250 	if(gg->rp) {
251 		if(gg->rp->Layer != NULL) DeleteLayer(0, gg->rp->Layer);
252 		free(gg->rp->TmpRas);
253 		free(gg->rp->AreaInfo);
254 		free(gg->rp);
255 	}
256 
257 	ami_memory_chip_free(gg->tmprasbuf);
258 	free(gg->areabuf);
259 	DisposeLayerInfo(gg->layerinfo);
260 	if(gg->palette_mapped == false) {
261 		if(gg->bm) ami_rtg_freebitmap(gg->bm);
262 	} else {
263 		if(gg->bm) FreeBitMap(gg->bm);
264 	}
265 
266 	if(gg->managed_pen_list == true) {
267 		ami_plot_release_pens(gg->shared_pens);
268 		free(gg->shared_pens);
269 		gg->shared_pens = NULL;
270 	}
271 
272 	free(gg);
273 }
274 
ami_plot_ra_get_rastport(struct gui_globals * gg)275 struct RastPort *ami_plot_ra_get_rastport(struct gui_globals *gg)
276 {
277 	return gg->rp;
278 }
279 
ami_plot_ra_get_bitmap(struct gui_globals * gg)280 struct BitMap *ami_plot_ra_get_bitmap(struct gui_globals *gg)
281 {
282 	return gg->bm;
283 }
284 
ami_plot_ra_get_size(struct gui_globals * gg,int * width,int * height)285 void ami_plot_ra_get_size(struct gui_globals *gg, int *width, int *height)
286 {
287 	*width = gg->width;
288 	*height = gg->height;
289 }
290 
ami_plot_ra_set_pen_list(struct gui_globals * gg,struct MinList * pen_list)291 void ami_plot_ra_set_pen_list(struct gui_globals *gg, struct MinList *pen_list)
292 {
293 	gg->shared_pens = pen_list;
294 }
295 
ami_clearclipreg(struct gui_globals * gg)296 void ami_clearclipreg(struct gui_globals *gg)
297 {
298 	struct Region *reg = NULL;
299 	struct Screen *scrn = ami_gui_get_screen();
300 
301 	reg = InstallClipRegion(gg->rp->Layer,NULL);
302 	if(reg) DisposeRegion(reg);
303 
304 	gg->rect.MinX = 0;
305 	gg->rect.MinY = 0;
306 	gg->rect.MaxX = scrn->Width-1;
307 	gg->rect.MaxY = scrn->Height-1;
308 
309 	gg->apen = 0x00000000;
310 	gg->open = 0x00000000;
311 	gg->apen_num = -1;
312 	gg->open_num = -1;
313 }
314 
ami_plot_obtain_pen(struct MinList * shared_pens,ULONG colr)315 static ULONG ami_plot_obtain_pen(struct MinList *shared_pens, ULONG colr)
316 {
317 	struct ami_plot_pen *node;
318 	struct Screen *scrn = ami_gui_get_screen();
319 
320 	LONG pen = ObtainBestPenA(scrn->ViewPort.ColorMap,
321 			(colr & 0x000000ff) << 24,
322 			(colr & 0x0000ff00) << 16,
323 			(colr & 0x00ff0000) << 8,
324 			NULL);
325 
326 	if(pen == -1) NSLOG(netsurf, INFO,
327 			    "WARNING: Cannot allocate pen for ABGR:%lx", colr);
328 
329 	if((shared_pens != NULL) && (pool_pens != NULL)) {
330 		if((node = (struct ami_plot_pen *)ami_memory_itempool_alloc(pool_pens, sizeof(struct ami_plot_pen)))) {
331 			node->pen = pen;
332 			AddTail((struct List *)shared_pens, (struct Node *)node);
333 		}
334 	} else {
335 		/* Immediately release the pen if we can't keep track of it. */
336 		ReleasePen(scrn->ViewPort.ColorMap, pen);
337 	}
338 	return pen;
339 }
340 
ami_plot_release_pens(struct MinList * shared_pens)341 void ami_plot_release_pens(struct MinList *shared_pens)
342 {
343 	struct Screen *scrn = ami_gui_get_screen();
344 	struct ami_plot_pen *node;
345 	struct ami_plot_pen *nnode;
346 
347 	if(shared_pens == NULL) return;
348 	if(IsMinListEmpty(shared_pens)) return;
349 	node = (struct ami_plot_pen *)GetHead((struct List *)shared_pens);
350 
351 	do {
352 		nnode = (struct ami_plot_pen *)GetSucc((struct Node *)node);
353 		ReleasePen(scrn->ViewPort.ColorMap, node->pen);
354 		Remove((struct Node *)node);
355 		ami_memory_itempool_free(pool_pens, node, sizeof(struct ami_plot_pen));
356 	} while((node = nnode));
357 }
358 
ami_plot_setapen(struct gui_globals * glob,struct RastPort * rp,ULONG colr)359 static void ami_plot_setapen(struct gui_globals *glob, struct RastPort *rp, ULONG colr)
360 {
361 	if(glob->apen == colr) return;
362 
363 #ifdef __amigaos4__
364 	if(glob->palette_mapped == false) {
365 		SetRPAttrs(rp, RPTAG_APenColor,
366 			ns_color_to_nscss(colr),
367 			TAG_DONE);
368 	} else
369 #endif
370 	{
371 		LONG pen = ami_plot_obtain_pen(glob->shared_pens, colr);
372 		if((pen != -1) && (pen != glob->apen_num)) SetAPen(rp, pen);
373 	}
374 
375 	glob->apen = colr;
376 }
377 
ami_plot_setopen(struct gui_globals * glob,struct RastPort * rp,ULONG colr)378 static void ami_plot_setopen(struct gui_globals *glob, struct RastPort *rp, ULONG colr)
379 {
380 	if(glob->open == colr) return;
381 
382 #ifdef __amigaos4__
383 	if(glob->palette_mapped == false) {
384 		SetRPAttrs(rp, RPTAG_OPenColor,
385 			ns_color_to_nscss(colr),
386 			TAG_DONE);
387 	} else
388 #endif
389 	{
390 		LONG pen = ami_plot_obtain_pen(glob->shared_pens, colr);
391 		if((pen != -1) && (pen != glob->open_num)) SetOPen(rp, pen);
392 	}
393 
394 	glob->open = colr;
395 }
396 
ami_plot_clear_bbox(struct RastPort * rp,struct IBox * bbox)397 void ami_plot_clear_bbox(struct RastPort *rp, struct IBox *bbox)
398 {
399 	if((bbox == NULL) || (rp == NULL)) return;
400 
401 	EraseRect(rp, bbox->Left, bbox->Top,
402 		bbox->Width + bbox->Left, bbox->Height + bbox->Top);
403 }
404 
405 
ami_arc_gfxlib(struct RastPort * rp,int x,int y,int radius,int angle1,int angle2)406 static void ami_arc_gfxlib(struct RastPort *rp, int x, int y, int radius, int angle1, int angle2)
407 {
408 	float angle1_r = (float)(angle1) * (M_PI / 180.0);
409 	float angle2_r = (float)(angle2) * (M_PI / 180.0);
410 	float angle, b, c;
411 	float step = 0.1; //(angle2_r - angle1_r) / ((angle2_r - angle1_r) * (float)radius);
412 	int x0, y0, x1, y1;
413 
414 	x0 = x;
415 	y0 = y;
416 
417 	b = angle1_r;
418 	c = angle2_r;
419 
420 	x1 = (int)(cos(b) * (float)radius);
421 	y1 = (int)(sin(b) * (float)radius);
422 	Move(rp, x0 + x1, y0 - y1);
423 
424 	for(angle = (b + step); angle <= c; angle += step) {
425 		x1 = (int)(cos(angle) * (float)radius);
426 		y1 = (int)(sin(angle) * (float)radius);
427 		Draw(rp, x0 + x1, y0 - y1);
428 	}
429 }
430 
431 /**
432  */
433 static nserror
ami_bitmap(struct gui_globals * glob,int x,int y,int width,int height,struct bitmap * bitmap)434 ami_bitmap(struct gui_globals *glob, int x, int y, int width, int height, struct bitmap *bitmap)
435 {
436 	NSLOG(plot, DEEPDEBUG, "[ami_plotter] Entered ami_bitmap()");
437 	struct Screen *scrn = ami_gui_get_screen();
438 	struct BitMap *tbm;
439 
440 	if (!width || !height) {
441 		return NSERROR_OK;
442 	}
443 
444 	if (((x + width) < glob->rect.MinX) ||
445 	    ((y + height) < glob->rect.MinY) ||
446 	    (x > glob->rect.MaxX) ||
447 	    (y > glob->rect.MaxY)) {
448 		return NSERROR_OK;
449 	}
450 
451 	tbm = ami_bitmap_get_native(bitmap, width, height, glob->palette_mapped, glob->rp->BitMap);
452 	if (!tbm) {
453 		return NSERROR_OK;
454 	}
455 
456 	NSLOG(plot, DEEPDEBUG, "[ami_plotter] ami_bitmap() got native bitmap");
457 
458 #ifdef __amigaos4__
459 	if (__builtin_expect((GfxBase->LibNode.lib_Version >= 53) &&
460 			     (glob->palette_mapped == false), 1)) {
461 		uint32 comptype = COMPOSITE_Src_Over_Dest;
462 		uint32 compflags = COMPFLAG_IgnoreDestAlpha;
463 		if(amiga_bitmap_get_opaque(bitmap)) {
464 			compflags |= COMPFLAG_SrcAlphaOverride;
465 			comptype = COMPOSITE_Src;
466 		}
467 
468 		CompositeTags(comptype,tbm,glob->rp->BitMap,
469 					COMPTAG_Flags, compflags,
470 					COMPTAG_DestX,glob->rect.MinX,
471 					COMPTAG_DestY,glob->rect.MinY,
472 					COMPTAG_DestWidth,glob->rect.MaxX - glob->rect.MinX + 1,
473 					COMPTAG_DestHeight,glob->rect.MaxY - glob->rect.MinY + 1,
474 					COMPTAG_SrcWidth,width,
475 					COMPTAG_SrcHeight,height,
476 					COMPTAG_OffsetX,x,
477 					COMPTAG_OffsetY,y,
478 					COMPTAG_FriendBitMap, scrn->RastPort.BitMap,
479 					TAG_DONE);
480 	} else
481 #endif
482 	{
483 		ULONG tag, tag_data, minterm = 0xc0;
484 
485 		if (glob->palette_mapped == false) {
486 			tag = BLITA_UseSrcAlpha;
487 			tag_data = !amiga_bitmap_get_opaque(bitmap);
488 			minterm = 0xc0;
489 		} else {
490 			tag = BLITA_MaskPlane;
491 			if ((tag_data = (ULONG)ami_bitmap_get_mask(bitmap, width, height, tbm)))
492 				minterm = MINTERM_SRCMASK;
493 		}
494 #ifdef __amigaos4__
495 		BltBitMapTags(BLITA_Width,width,
496 						BLITA_Height,height,
497 						BLITA_Source,tbm,
498 						BLITA_Dest,glob->rp,
499 						BLITA_DestX,x,
500 						BLITA_DestY,y,
501 						BLITA_SrcType,BLITT_BITMAP,
502 						BLITA_DestType,BLITT_RASTPORT,
503 						BLITA_Minterm, minterm,
504 						tag, tag_data,
505 						TAG_DONE);
506 #else
507 		if (tag_data) {
508 			BltMaskBitMapRastPort(tbm, 0, 0, glob->rp, x, y, width, height, minterm, tag_data);
509 		} else {
510 			BltBitMapRastPort(tbm, 0, 0, glob->rp, x, y, width, height, 0xc0);
511 		}
512 #endif
513 	}
514 
515 	if ((ami_bitmap_is_nativebm(bitmap, tbm) == false)) {
516 		ami_rtg_freebitmap(tbm);
517 	}
518 
519 	return NSERROR_OK;
520 }
521 
522 
HOOKF(void,ami_bitmap_tile_hook,struct RastPort *,rp,struct BackFillMessage *)523 HOOKF(void, ami_bitmap_tile_hook, struct RastPort *, rp, struct BackFillMessage *)
524 {
525 	int xf,yf;
526 	struct bfbitmap *bfbm = (struct bfbitmap *)hook->h_Data;
527 	struct Screen *scrn = ami_gui_get_screen();
528 
529 	/* tile down and across to extents  (msg->Bounds.MinX)*/
530 	for (xf = -bfbm->offsetx; xf < msg->Bounds.MaxX; xf += bfbm->width) {
531 		for (yf = -bfbm->offsety; yf < msg->Bounds.MaxY; yf += bfbm->height) {
532 #ifdef __amigaos4__
533 			if(__builtin_expect((GfxBase->LibNode.lib_Version >= 53) &&
534 				(bfbm->palette_mapped == false), 1)) {
535 				CompositeTags(COMPOSITE_Src_Over_Dest, bfbm->bm, rp->BitMap,
536 					COMPTAG_Flags, COMPFLAG_IgnoreDestAlpha,
537 					COMPTAG_DestX, msg->Bounds.MinX,
538 					COMPTAG_DestY, msg->Bounds.MinY,
539 					COMPTAG_DestWidth, msg->Bounds.MaxX - msg->Bounds.MinX + 1,
540 					COMPTAG_DestHeight, msg->Bounds.MaxY - msg->Bounds.MinY + 1,
541 					COMPTAG_SrcWidth, bfbm->width,
542 					COMPTAG_SrcHeight, bfbm->height,
543 					COMPTAG_OffsetX, xf,
544 					COMPTAG_OffsetY, yf,
545 					COMPTAG_FriendBitMap, scrn->RastPort.BitMap,
546 					TAG_DONE);
547 			} else
548 #endif
549 			{
550 				ULONG tag, tag_data, minterm = 0xc0;
551 
552 				if(bfbm->palette_mapped == false) {
553 					tag = BLITA_UseSrcAlpha;
554 					tag_data = TRUE;
555 					minterm = 0xc0;
556 				} else {
557 					tag = BLITA_MaskPlane;
558 					if((tag_data = (ULONG)bfbm->mask))
559 						minterm = MINTERM_SRCMASK;
560 				}
561 #ifdef __amigaos4__
562 				BltBitMapTags(BLITA_Width, bfbm->width,
563 					BLITA_Height, bfbm->height,
564 					BLITA_Source, bfbm->bm,
565 					BLITA_Dest, rp,
566 					BLITA_DestX, xf,
567 					BLITA_DestY, yf,
568 					BLITA_SrcType, BLITT_BITMAP,
569 					BLITA_DestType, BLITT_RASTPORT,
570 					BLITA_Minterm, minterm,
571 					tag, tag_data,
572 					TAG_DONE);
573 #else
574 				if(tag_data) {
575 					BltMaskBitMapRastPort(bfbm->bm, 0, 0, rp, xf, yf,
576 						bfbm->width, bfbm->height, minterm, tag_data);
577 				} else {
578 					BltBitMapRastPort(bfbm->bm, 0, 0, rp, xf, yf,
579 						bfbm->width, bfbm->height, minterm);
580 				}
581 #endif
582 			}
583 		}
584 	}
585 }
586 
ami_bezier(struct bez_point * restrict a,struct bez_point * restrict b,struct bez_point * restrict c,struct bez_point * restrict d,float t,struct bez_point * restrict p)587 static void ami_bezier(struct bez_point *restrict a, struct bez_point *restrict b,
588 				struct bez_point *restrict c, struct bez_point *restrict d,
589 				float t, struct bez_point *restrict p) {
590     p->x = pow((1 - t), 3) * a->x + 3 * t * pow((1 -t), 2) * b->x + 3 * (1-t) * pow(t, 2)* c->x + pow (t, 3)* d->x;
591     p->y = pow((1 - t), 3) * a->y + 3 * t * pow((1 -t), 2) * b->y + 3 * (1-t) * pow(t, 2)* c->y + pow (t, 3)* d->y;
592 }
593 
594 
ami_plot_screen_is_palettemapped(void)595 bool ami_plot_screen_is_palettemapped(void)
596 {
597 	/* This may not be entirely correct - previously we returned the state of the current BitMap */
598 	return palette_mapped;
599 }
600 
601 
602 /**
603  * \brief Sets a clip rectangle for subsequent plot operations.
604  *
605  * \param ctx The current redraw context.
606  * \param clip The rectangle to limit all subsequent plot
607  *              operations within.
608  * \return NSERROR_OK on success else error code.
609  */
610 static nserror
ami_clip(const struct redraw_context * ctx,const struct rect * clip)611 ami_clip(const struct redraw_context *ctx, const struct rect *clip)
612 {
613 	struct gui_globals *glob = (struct gui_globals *)ctx->priv;
614 	struct Region *reg = NULL;
615 
616 	NSLOG(plot, DEEPDEBUG, "[ami_plotter] Entered ami_clip()");
617 
618 	if (glob->rp->Layer) {
619 		reg = NewRegion();
620 
621 		glob->rect.MinX = clip->x0;
622 		glob->rect.MinY = clip->y0;
623 		glob->rect.MaxX = clip->x1-1;
624 		glob->rect.MaxY = clip->y1-1;
625 
626 		OrRectRegion(reg,&glob->rect);
627 
628 		reg = InstallClipRegion(glob->rp->Layer,reg);
629 
630 		if(reg) {
631 			DisposeRegion(reg);
632 		}
633 	}
634 
635 	return NSERROR_OK;
636 }
637 
638 
639 /**
640  * Plots an arc
641  *
642  * plot an arc segment around (x,y), anticlockwise from angle1
643  *  to angle2. Angles are measured anticlockwise from
644  *  horizontal, in degrees.
645  *
646  * \param ctx The current redraw context.
647  * \param style Style controlling the arc plot.
648  * \param x The x coordinate of the arc.
649  * \param y The y coordinate of the arc.
650  * \param radius The radius of the arc.
651  * \param angle1 The start angle of the arc.
652  * \param angle2 The finish angle of the arc.
653  * \return NSERROR_OK on success else error code.
654  */
655 static nserror
ami_arc(const struct redraw_context * ctx,const plot_style_t * style,int x,int y,int radius,int angle1,int angle2)656 ami_arc(const struct redraw_context *ctx,
657 	const plot_style_t *style,
658 	int x, int y, int radius, int angle1, int angle2)
659 {
660 	NSLOG(plot, DEEPDEBUG, "[ami_plotter] Entered ami_arc()");
661 
662 	struct gui_globals *glob = (struct gui_globals *)ctx->priv;
663 
664 	if (angle2 < angle1) {
665 		angle2 += 360;
666 	}
667 
668 	ami_plot_setapen(glob, glob->rp, style->fill_colour);
669 	ami_arc_gfxlib(glob->rp, x, y, radius, angle1, angle2);
670 
671 	return NSERROR_OK;
672 }
673 
674 
675 /**
676  * Plots a circle
677  *
678  * Plot a circle centered on (x,y), which is optionally filled.
679  *
680  * \param ctx The current redraw context.
681  * \param style Style controlling the circle plot.
682  * \param x x coordinate of circle centre.
683  * \param y y coordinate of circle centre.
684  * \param radius circle radius.
685  * \return NSERROR_OK on success else error code.
686  */
687 static nserror
ami_disc(const struct redraw_context * ctx,const plot_style_t * style,int x,int y,int radius)688 ami_disc(const struct redraw_context *ctx,
689 		const plot_style_t *style,
690 		int x, int y, int radius)
691 {
692 	NSLOG(plot, DEEPDEBUG, "[ami_plotter] Entered ami_disc()");
693 
694 	struct gui_globals *glob = (struct gui_globals *)ctx->priv;
695 
696 	if (style->fill_type != PLOT_OP_TYPE_NONE) {
697 		ami_plot_setapen(glob, glob->rp, style->fill_colour);
698 		AreaCircle(glob->rp,x,y,radius);
699 		AreaEnd(glob->rp);
700 	}
701 
702 	if (style->stroke_type != PLOT_OP_TYPE_NONE) {
703 		ami_plot_setapen(glob, glob->rp, style->stroke_colour);
704 		DrawEllipse(glob->rp,x,y,radius,radius);
705 	}
706 
707 	return NSERROR_OK;
708 }
709 
710 
711 /**
712  * Plots a line
713  *
714  * plot a line from (x0,y0) to (x1,y1). Coordinates are at
715  *  centre of line width/thickness.
716  *
717  * \param ctx The current redraw context.
718  * \param style Style controlling the line plot.
719  * \param line A rectangle defining the line to be drawn
720  * \return NSERROR_OK on success else error code.
721  */
722 static nserror
ami_line(const struct redraw_context * ctx,const plot_style_t * style,const struct rect * line)723 ami_line(const struct redraw_context *ctx,
724 		const plot_style_t *style,
725 		const struct rect *line)
726 {
727 	NSLOG(plot, DEEPDEBUG, "[ami_plotter] Entered ami_line()");
728 
729 	struct gui_globals *glob = (struct gui_globals *)ctx->priv;
730 
731 	glob->rp->PenWidth = plot_style_fixed_to_int(style->stroke_width);
732 	glob->rp->PenHeight = plot_style_fixed_to_int(style->stroke_width);
733 
734 	switch (style->stroke_type) {
735 		case PLOT_OP_TYPE_SOLID: /**< Solid colour */
736 		default:
737 			glob->rp->LinePtrn = PATT_LINE;
738 		break;
739 
740 		case PLOT_OP_TYPE_DOT: /**< Doted plot */
741 			glob->rp->LinePtrn = PATT_DOT;
742 		break;
743 
744 		case PLOT_OP_TYPE_DASH: /**< dashed plot */
745 			glob->rp->LinePtrn = PATT_DASH;
746 		break;
747 	}
748 
749 	ami_plot_setapen(glob, glob->rp, style->stroke_colour);
750 	Move(glob->rp, line->x0, line->y0);
751 	Draw(glob->rp, line->x1, line->y1);
752 
753 	glob->rp->PenWidth = 1;
754 	glob->rp->PenHeight = 1;
755 	glob->rp->LinePtrn = PATT_LINE;
756 
757 	return NSERROR_OK;
758 }
759 
760 
761 /**
762  * Plots a rectangle.
763  *
764  * The rectangle can be filled an outline or both controlled
765  *  by the plot style The line can be solid, dotted or
766  *  dashed. Top left corner at (x0,y0) and rectangle has given
767  *  width and height.
768  *
769  * \param ctx The current redraw context.
770  * \param style Style controlling the rectangle plot.
771  * \param rect A rectangle defining the line to be drawn
772  * \return NSERROR_OK on success else error code.
773  */
774 static nserror
ami_rectangle(const struct redraw_context * ctx,const plot_style_t * style,const struct rect * rect)775 ami_rectangle(const struct redraw_context *ctx,
776 		     const plot_style_t *style,
777 		     const struct rect *rect)
778 {
779 	NSLOG(plot, DEEPDEBUG, "[ami_plotter] Entered ami_rectangle()");
780 
781 	struct gui_globals *glob = (struct gui_globals *)ctx->priv;
782 
783 	if (style->fill_type != PLOT_OP_TYPE_NONE) {
784 		ami_plot_setapen(glob, glob->rp, style->fill_colour);
785 		RectFill(glob->rp, rect->x0, rect->y0, rect->x1- 1 , rect->y1 - 1);
786 	}
787 
788 	if (style->stroke_type != PLOT_OP_TYPE_NONE) {
789 		glob->rp->PenWidth = plot_style_fixed_to_int(style->stroke_width);
790 		glob->rp->PenHeight = plot_style_fixed_to_int(style->stroke_width);
791 
792 		switch (style->stroke_type) {
793 			case PLOT_OP_TYPE_SOLID: /**< Solid colour */
794 			default:
795 				glob->rp->LinePtrn = PATT_LINE;
796 			break;
797 
798 			case PLOT_OP_TYPE_DOT: /**< Dotted plot */
799 				glob->rp->LinePtrn = PATT_DOT;
800 			break;
801 
802 			case PLOT_OP_TYPE_DASH: /**< dashed plot */
803 				glob->rp->LinePtrn = PATT_DASH;
804 			break;
805  		}
806 
807 		ami_plot_setapen(glob, glob->rp, style->stroke_colour);
808 		Move(glob->rp, rect->x0, rect->y0);
809 		Draw(glob->rp, rect->x1, rect->y0);
810 		Draw(glob->rp, rect->x1, rect->y1);
811 		Draw(glob->rp, rect->x0, rect->y1);
812 		Draw(glob->rp, rect->x0, rect->y0);
813 
814 		glob->rp->PenWidth = 1;
815 		glob->rp->PenHeight = 1;
816 		glob->rp->LinePtrn = PATT_LINE;
817 	}
818 
819 	return NSERROR_OK;
820 }
821 
822 
823 /**
824  * Plot a polygon
825  *
826  * Plots a filled polygon with straight lines between
827  * points. The lines around the edge of the ploygon are not
828  * plotted. The polygon is filled with the non-zero winding
829  * rule.
830  *
831  * \param ctx The current redraw context.
832  * \param style Style controlling the polygon plot.
833  * \param p verticies of polygon
834  * \param n number of verticies.
835  * \return NSERROR_OK on success else error code.
836  */
837 static nserror
ami_polygon(const struct redraw_context * ctx,const plot_style_t * style,const int * p,unsigned int n)838 ami_polygon(const struct redraw_context *ctx,
839 		   const plot_style_t *style,
840 		   const int *p,
841 		   unsigned int n)
842 {
843 	NSLOG(plot, DEEPDEBUG, "[ami_plotter] Entered ami_polygon()");
844 
845 	struct gui_globals *glob = (struct gui_globals *)ctx->priv;
846 
847 	ami_plot_setapen(glob, glob->rp, style->fill_colour);
848 
849 	if (AreaMove(glob->rp,p[0],p[1]) == -1) {
850 		NSLOG(netsurf, INFO, "AreaMove: vector list full");
851 	}
852 
853 	for (uint32 k = 1; k < n; k++) {
854 		if (AreaDraw(glob->rp,p[k*2],p[(k*2)+1]) == -1) {
855 			NSLOG(netsurf, INFO, "AreaDraw: vector list full");
856 		}
857 	}
858 
859 	if (AreaEnd(glob->rp) == -1) {
860 		NSLOG(netsurf, INFO, "AreaEnd: error");
861 	}
862 
863 	return NSERROR_OK;
864 }
865 
866 
867 /**
868  * Plots a path.
869  *
870  * Path plot consisting of cubic Bezier curves. Line and fill colour is
871  *  controlled by the plot style.
872  *
873  * \param ctx The current redraw context.
874  * \param pstyle Style controlling the path plot.
875  * \param p elements of path
876  * \param n nunber of elements on path
877  * \param transform A transform to apply to the path.
878  * \return NSERROR_OK on success else error code.
879  */
880 static nserror
ami_path(const struct redraw_context * ctx,const plot_style_t * pstyle,const float * p,unsigned int n,const float transform[6])881 ami_path(const struct redraw_context *ctx,
882 		const plot_style_t *pstyle,
883 		const float *p,
884 		unsigned int n,
885 		const float transform[6])
886 {
887 	unsigned int i;
888 	struct bez_point start_p = {0, 0}, cur_p = {0, 0}, p_a, p_b, p_c, p_r;
889 
890 	NSLOG(plot, DEEPDEBUG, "[ami_plotter] Entered ami_path()");
891 
892 	struct gui_globals *glob = (struct gui_globals *)ctx->priv;
893 
894 	if (n == 0) {
895 		return NSERROR_OK;
896 	}
897 
898 	if (p[0] != PLOTTER_PATH_MOVE) {
899 		NSLOG(netsurf, INFO, "Path does not start with move");
900 		return NSERROR_INVALID;
901 	}
902 
903 	if (pstyle->fill_colour != NS_TRANSPARENT) {
904 		ami_plot_setapen(glob, glob->rp, pstyle->fill_colour);
905 		if (pstyle->stroke_colour != NS_TRANSPARENT) {
906 			ami_plot_setopen(glob, glob->rp, pstyle->stroke_colour);
907 		}
908 	} else {
909 		if (pstyle->stroke_colour != NS_TRANSPARENT) {
910 			ami_plot_setapen(glob, glob->rp, pstyle->stroke_colour);
911 		} else {
912 			return NSERROR_OK; /* wholly transparent */
913 		}
914 	}
915 
916 	/* Construct path */
917 	for (i = 0; i < n; ) {
918 		if (p[i] == PLOTTER_PATH_MOVE) {
919 			if (pstyle->fill_colour != NS_TRANSPARENT) {
920 				if (AreaMove(glob->rp, p[i+1], p[i+2]) == -1) {
921 					NSLOG(netsurf, INFO,
922 					      "AreaMove: vector list full");
923 				}
924 			} else {
925 				Move(glob->rp, p[i+1], p[i+2]);
926 			}
927 			/* Keep track for future Bezier curves/closes etc */
928 			start_p.x = p[i+1];
929 			start_p.y = p[i+2];
930 			cur_p.x = start_p.x;
931 			cur_p.y = start_p.y;
932 			i += 3;
933 		} else if (p[i] == PLOTTER_PATH_CLOSE) {
934 			if (pstyle->fill_colour != NS_TRANSPARENT) {
935 				if (AreaEnd(glob->rp) == -1) {
936 					NSLOG(netsurf, INFO, "AreaEnd: error");
937 				}
938 			} else {
939 				Draw(glob->rp, start_p.x, start_p.y);
940 			}
941 			i++;
942 		} else if (p[i] == PLOTTER_PATH_LINE) {
943 			if (pstyle->fill_colour != NS_TRANSPARENT) {
944 				if (AreaDraw(glob->rp, p[i+1], p[i+2]) == -1) {
945 					NSLOG(netsurf, INFO,
946 					      "AreaDraw: vector list full");
947 				}
948 			} else {
949 				Draw(glob->rp, p[i+1], p[i+2]);
950 			}
951 			cur_p.x = p[i+1];
952 			cur_p.y = p[i+2];
953 			i += 3;
954 		} else if (p[i] == PLOTTER_PATH_BEZIER) {
955 			p_a.x = p[i+1];
956 			p_a.y = p[i+2];
957 			p_b.x = p[i+3];
958 			p_b.y = p[i+4];
959 			p_c.x = p[i+5];
960 			p_c.y = p[i+6];
961 
962 			for (float t = 0.0; t <= 1.0; t += 0.1) {
963 				ami_bezier(&cur_p, &p_a, &p_b, &p_c, t, &p_r);
964 				if (pstyle->fill_colour != NS_TRANSPARENT) {
965 					if (AreaDraw(glob->rp, p_r.x, p_r.y) == -1) {
966 						NSLOG(netsurf, INFO,
967 						      "AreaDraw: vector list full");
968 					}
969 				} else {
970 					Draw(glob->rp, p_r.x, p_r.y);
971 				}
972 			}
973 			cur_p.x = p_c.x;
974 			cur_p.y = p_c.y;
975 			i += 7;
976 		} else {
977 			NSLOG(netsurf, INFO, "bad path command %f", p[i]);
978 			/* End path for safety if using Area commands */
979 			if (pstyle->fill_colour != NS_TRANSPARENT) {
980 				AreaEnd(glob->rp);
981 				BNDRYOFF(glob->rp);
982 			}
983 			return NSERROR_INVALID;
984 		}
985 	}
986 	if (pstyle->fill_colour != NS_TRANSPARENT) {
987 		BNDRYOFF(glob->rp);
988 	}
989 
990 	return NSERROR_OK;
991 }
992 
993 
994 /**
995  * Plot a bitmap
996  *
997  * Tiled plot of a bitmap image. (x,y) gives the top left
998  * coordinate of an explicitly placed tile. From this tile the
999  * image can repeat in all four directions -- up, down, left
1000  * and right -- to the extents given by the current clip
1001  * rectangle.
1002  *
1003  * The bitmap_flags say whether to tile in the x and y
1004  * directions. If not tiling in x or y directions, the single
1005  * image is plotted. The width and height give the dimensions
1006  * the image is to be scaled to.
1007  *
1008  * \param ctx The current redraw context.
1009  * \param bitmap The bitmap to plot
1010  * \param x The x coordinate to plot the bitmap
1011  * \param y The y coordiante to plot the bitmap
1012  * \param width The width of area to plot the bitmap into
1013  * \param height The height of area to plot the bitmap into
1014  * \param bg the background colour to alpha blend into
1015  * \param flags the flags controlling the type of plot operation
1016  * \return NSERROR_OK on success else error code.
1017  */
1018 static nserror
ami_bitmap_tile(const struct redraw_context * ctx,struct bitmap * bitmap,int x,int y,int width,int height,colour bg,bitmap_flags_t flags)1019 ami_bitmap_tile(const struct redraw_context *ctx,
1020 		struct bitmap *bitmap,
1021 		int x, int y,
1022 		int width,
1023 		int height,
1024 		colour bg,
1025 		bitmap_flags_t flags)
1026 {
1027 	int xf,yf,xm,ym,oy,ox;
1028 	struct BitMap *tbm = NULL;
1029 	struct Hook *bfh = NULL;
1030 	struct bfbitmap bfbm;
1031 	bool repeat_x = (flags & BITMAPF_REPEAT_X);
1032 	bool repeat_y = (flags & BITMAPF_REPEAT_Y);
1033 
1034 	NSLOG(plot, DEEPDEBUG, "[ami_plotter] Entered ami_bitmap_tile()");
1035 
1036 	struct gui_globals *glob = (struct gui_globals *)ctx->priv;
1037 
1038 	if ((width == 0) || (height == 0)) {
1039 		return NSERROR_OK;
1040 	}
1041 
1042 	if (!(repeat_x || repeat_y)) {
1043 		return ami_bitmap(glob, x, y, width, height, bitmap);
1044 	}
1045 
1046 	/* If it is a one pixel transparent image, we are wasting our time */
1047 	if ((amiga_bitmap_get_opaque(bitmap) == false) &&
1048 	    (bitmap_get_width(bitmap) == 1) &&
1049 	    (bitmap_get_height(bitmap) == 1)) {
1050 		return NSERROR_OK;
1051 	}
1052 
1053 	tbm = ami_bitmap_get_native(bitmap, width, height, glob->palette_mapped, glob->rp->BitMap);
1054 	if (!tbm) {
1055 		return NSERROR_OK;
1056 	}
1057 
1058 	ox = x;
1059 	oy = y;
1060 
1061 	/* get left most tile position */
1062 	for (; ox > 0; ox -= width)
1063 
1064 	/* get top most tile position */
1065 	for (; oy > 0; oy -= height);
1066 
1067 	if (ox < 0) {
1068 		ox = -ox;
1069 	}
1070 	if (oy < 0) {
1071 		oy = -oy;
1072 	}
1073 	if (repeat_x) {
1074 		xf = glob->rect.MaxX;
1075 		xm = glob->rect.MinX;
1076 	} else {
1077 		xf = x + width;
1078 		xm = x;
1079 	}
1080 
1081 	if (repeat_y) {
1082 		yf = glob->rect.MaxY;
1083 		ym = glob->rect.MinY;
1084 	} else {
1085 		yf = y + height;
1086 		ym = y;
1087 	}
1088 #ifdef __amigaos4__
1089 	if(amiga_bitmap_get_opaque(bitmap)) {
1090 		bfh = CreateBackFillHook(BFHA_BitMap,tbm,
1091 							BFHA_Width,width,
1092 							BFHA_Height,height,
1093 							BFHA_OffsetX,ox,
1094 							BFHA_OffsetY,oy,
1095 							TAG_DONE);
1096 	} else
1097 #endif
1098 	{
1099 		bfbm.bm = tbm;
1100 		bfbm.width = width;
1101 		bfbm.height = height;
1102 		bfbm.offsetx = ox;
1103 		bfbm.offsety = oy;
1104 		bfbm.mask = ami_bitmap_get_mask(bitmap, width, height, tbm);
1105 		bfbm.palette_mapped = glob->palette_mapped;
1106 		bfh = calloc(1, sizeof(struct Hook));
1107 		bfh->h_Entry = (HOOKFUNC)ami_bitmap_tile_hook;
1108 		bfh->h_SubEntry = 0;
1109 		bfh->h_Data = &bfbm;
1110 	}
1111 
1112 	InstallLayerHook(glob->rp->Layer,bfh);
1113 	EraseRect(glob->rp,xm,ym,xf,yf);
1114 	InstallLayerHook(glob->rp->Layer,LAYERS_NOBACKFILL);
1115 
1116 #ifdef __amigaos4__
1117 	if (amiga_bitmap_get_opaque(bitmap)) {
1118 		DeleteBackFillHook(bfh);
1119 	} else
1120 #endif
1121 		free(bfh);
1122 
1123 	if ((ami_bitmap_is_nativebm(bitmap, tbm) == false)) {
1124 		/**\todo is this logic logical? */
1125 		ami_rtg_freebitmap(tbm);
1126 	}
1127 
1128 	return NSERROR_OK;
1129 }
1130 
1131 
1132 /**
1133  * Text plotting.
1134  *
1135  * \param ctx The current redraw context.
1136  * \param fstyle plot style for this text
1137  * \param x x coordinate
1138  * \param y y coordinate
1139  * \param text UTF-8 string to plot
1140  * \param length length of string, in bytes
1141  * \return NSERROR_OK on success else error code.
1142  */
1143 static nserror
ami_text(const struct redraw_context * ctx,const struct plot_font_style * fstyle,int x,int y,const char * text,size_t length)1144 ami_text(const struct redraw_context *ctx,
1145 		const struct plot_font_style *fstyle,
1146 		int x,
1147 		int y,
1148 		const char *text,
1149 		size_t length)
1150 {
1151 	NSLOG(plot, DEEPDEBUG, "[ami_plotter] Entered ami_text()");
1152 
1153 	struct gui_globals *glob = (struct gui_globals *)ctx->priv;
1154 
1155 	if (__builtin_expect(ami_nsfont == NULL, 0)) {
1156 		return NSERROR_OK;
1157 	}
1158 	ami_plot_setapen(glob, glob->rp, fstyle->foreground);
1159 	ami_nsfont->text(glob->rp, text, length, fstyle, x, y, nsoption_bool(font_antialiasing));
1160 
1161 	return NSERROR_OK;
1162 }
1163 
1164 
1165 const struct plotter_table amiplot = {
1166 	.rectangle = ami_rectangle,
1167 	.line = ami_line,
1168 	.polygon = ami_polygon,
1169 	.clip = ami_clip,
1170 	.text = ami_text,
1171 	.disc = ami_disc,
1172 	.arc = ami_arc,
1173 	.bitmap = ami_bitmap_tile,
1174 	.path = ami_path,
1175 	.option_knockout = true,
1176 };
1177 
1178