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