1 /*
2  * Copyright 2008, 2009, 2012, 2016 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 <stdlib.h>
22 #include <string.h>
23 #include <proto/exec.h>
24 #ifdef __amigaos4__
25 #include <graphics/blitattr.h>
26 #include <graphics/composite.h>
27 #endif
28 #include <graphics/gfxbase.h>
29 #include <proto/datatypes.h>
30 #include <datatypes/pictureclass.h>
31 #include <proto/dos.h>
32 #include <proto/intuition.h>
33 #include <proto/utility.h>
34 
35 #include <proto/guigfx.h>
36 #include <guigfx/guigfx.h>
37 #include <render/render.h>
38 #ifndef __amigaos4__
39 #include <inline/guigfx.h>
40 #endif
41 
42 #ifdef __amigaos4__
43 #include <exec/extmem.h>
44 #include <sys/param.h>
45 #endif
46 #include "assert.h"
47 
48 #include "utils/log.h"
49 #include "utils/nsoption.h"
50 #include "utils/nsurl.h"
51 #include "utils/messages.h"
52 #include "netsurf/bitmap.h"
53 #include "netsurf/content.h"
54 
55 #include "amiga/gui.h"
56 #include "amiga/bitmap.h"
57 #include "amiga/plotters.h"
58 #include "amiga/memory.h"
59 #include "amiga/misc.h"
60 #include "amiga/rtg.h"
61 #include "amiga/schedule.h"
62 
63 // disable use of "triangle mode" for scaling
64 #ifdef AMI_NS_TRIANGLE_SCALING
65 #undef AMI_NS_TRIANGLE_SCALING
66 #endif
67 
68 struct bitmap {
69 	int width;
70 	int height;
71 	UBYTE *pixdata;
72 	struct ExtMemIFace *iextmem;
73 	uint32 size;
74 	bool opaque;
75 	int native;
76 	struct BitMap *nativebm;
77 	int nativebmwidth;
78 	int nativebmheight;
79 	PLANEPTR native_mask;
80 	Object *dto;
81 	APTR drawhandle;
82 	struct nsurl *url;   /* temporary storage space */
83 	char *title; /* temporary storage space */
84 	ULONG *icondata; /* for appicons */
85 };
86 
87 enum {
88 	AMI_NSBM_NONE = 0,
89 	AMI_NSBM_TRUECOLOUR,
90 	AMI_NSBM_PALETTEMAPPED
91 };
92 
93 struct vertex {
94 	float x, y;
95 	float s, t, w;
96 };
97 
98 #define VTX(I,X,Y,S,T) vtx[I].x = X; vtx[I].y = Y; vtx[I].s = S; vtx[I].t = T; vtx[I].w = 1.0f;
99 #define VTX_RECT(SX,SY,SW,SH,DX,DY,DW,DH) \
100 		VTX(0, DX,      DY,      SX,      SY); \
101 		VTX(1, DX + DW, DY,      SX + SW, SY); \
102 		VTX(2, DX,      DY + DH, SX,      SY + SH); \
103 		VTX(3, DX + DW, DY,      SX + SW, SY); \
104 		VTX(4, DX,      DY + DH, SX,      SY + SH); \
105 		VTX(5, DX + DW, DY + DH, SX + SW, SY + SH);
106 
107 static APTR pool_bitmap = NULL;
108 static bool guigfx_warned = false;
109 
110 /* exported function documented in amiga/bitmap.h */
amiga_bitmap_create(int width,int height,unsigned int state)111 void *amiga_bitmap_create(int width, int height, unsigned int state)
112 {
113 	struct bitmap *bitmap;
114 
115 	if(pool_bitmap == NULL) pool_bitmap = ami_memory_itempool_create(sizeof(struct bitmap));
116 
117 	bitmap = ami_memory_itempool_alloc(pool_bitmap, sizeof(struct bitmap));
118 	if(bitmap == NULL) return NULL;
119 
120 	bitmap->size = width * height * 4;
121 
122 #ifdef __amigaos4__
123 	if(nsoption_bool(use_extmem) == true) {
124 		uint64 size64 = bitmap->size;
125 		bitmap->iextmem = AllocSysObjectTags(ASOT_EXTMEM,
126 								ASOEXTMEM_Size, &size64,
127 								ASOEXTMEM_AllocationPolicy, EXTMEMPOLICY_IMMEDIATE,
128 								TAG_END);
129 
130 		bitmap->pixdata = NULL;
131 		UBYTE *pixdata = amiga_bitmap_get_buffer(bitmap);
132 		memset(pixdata, 0xff, bitmap->size);
133 	} else
134 #endif
135 	{
136 		bitmap->pixdata = ami_memory_clear_alloc(bitmap->size, 0xff);
137 	}
138 
139 	bitmap->width = width;
140 	bitmap->height = height;
141 
142 	if(state & BITMAP_OPAQUE) bitmap->opaque = true;
143 		else bitmap->opaque = false;
144 
145 	bitmap->nativebm = NULL;
146 	bitmap->nativebmwidth = 0;
147 	bitmap->nativebmheight = 0;
148 	bitmap->native_mask = NULL;
149 	bitmap->drawhandle = NULL;
150 	bitmap->url = NULL;
151 	bitmap->title = NULL;
152 	bitmap->icondata = NULL;
153 	bitmap->native = AMI_NSBM_NONE;
154 
155 	return bitmap;
156 }
157 
amiga_bitmap_unmap_buffer(void * p)158 static void amiga_bitmap_unmap_buffer(void *p)
159 {
160 #ifdef __amigaos4__
161 	struct bitmap *bm = p;
162 
163 	if((nsoption_bool(use_extmem) == true) && (bm->pixdata != NULL)) {
164 		NSLOG(netsurf, INFO,
165 		      "Unmapping ExtMem object %p for bitmap %p",
166 		      bm->iextmem,
167 		      bm);
168 		bm->iextmem->Unmap(bm->pixdata, bm->size);
169 		bm->pixdata = NULL;
170 	}
171 #endif
172 }
173 
174 /* exported function documented in amiga/bitmap.h */
amiga_bitmap_get_buffer(void * bitmap)175 unsigned char *amiga_bitmap_get_buffer(void *bitmap)
176 {
177 	struct bitmap *bm = bitmap;
178 
179 #ifdef __amigaos4__
180 	if(nsoption_bool(use_extmem) == true) {
181 		if(bm->pixdata == NULL) {
182 			NSLOG(netsurf, INFO,
183 			      "Mapping ExtMem object %p for bitmap %p",
184 			      bm->iextmem,
185 			      bm);
186 			bm->pixdata = bm->iextmem->Map(NULL, bm->size, 0LL, 0);
187 		}
188 
189 		/* unmap the buffer after one second */
190 		ami_schedule(1000, amiga_bitmap_unmap_buffer, bm);
191 	}
192 #endif
193 
194 	return bm->pixdata;
195 }
196 
197 /* exported function documented in amiga/bitmap.h */
amiga_bitmap_get_rowstride(void * bitmap)198 size_t amiga_bitmap_get_rowstride(void *bitmap)
199 {
200 	struct bitmap *bm = bitmap;
201 
202 	if(bm)
203 	{
204 		return ((bm->width)*4);
205 	}
206 	else
207 	{
208 		return 0;
209 	}
210 }
211 
212 
213 /* exported function documented in amiga/bitmap.h */
amiga_bitmap_destroy(void * bitmap)214 void amiga_bitmap_destroy(void *bitmap)
215 {
216 	struct bitmap *bm = bitmap;
217 
218 	if(bm)
219 	{
220 		if((bm->nativebm)) { // && (bm->native == AMI_NSBM_TRUECOLOUR)) {
221 			ami_rtg_freebitmap(bm->nativebm);
222 		}
223 
224 		if(bm->native_mask) FreeRaster(bm->native_mask, bm->width, bm->height);
225 
226 #ifdef __amigaos4__
227 		if(nsoption_bool(use_extmem) == true) {
228 			ami_schedule(-1, amiga_bitmap_unmap_buffer, bm);
229 			amiga_bitmap_unmap_buffer(bm);
230 			FreeSysObject(ASOT_EXTMEM, bm->iextmem);
231 			bm->iextmem = NULL;
232 		} else
233 #endif
234 		{
235 			if(bm->drawhandle) ReleaseDrawHandle(bm->drawhandle);
236 			ami_memory_clear_free(bm->pixdata);
237 		}
238 
239 		if(bm->url) nsurl_unref(bm->url);
240 		if(bm->title) free(bm->title);
241 
242 		bm->pixdata = NULL;
243 		bm->nativebm = NULL;
244 		bm->native_mask = NULL;
245 		bm->drawhandle = NULL;
246 		bm->url = NULL;
247 		bm->title = NULL;
248 
249 		ami_memory_itempool_free(pool_bitmap, bm, sizeof(struct bitmap));
250 		bm = NULL;
251 	}
252 }
253 
254 
255 /* exported function documented in amiga/bitmap.h */
amiga_bitmap_save(void * bitmap,const char * path,unsigned flags)256 bool amiga_bitmap_save(void *bitmap, const char *path, unsigned flags)
257 {
258 	int err = 0;
259 	Object *dto = NULL;
260 
261 	if((dto = ami_datatype_object_from_bitmap(bitmap)))
262 	{
263 		if (flags & AMI_BITMAP_SCALE_ICON) {
264 			IDoMethod(dto, PDTM_SCALE, 16, 16, 0);
265 
266 			if((DoDTMethod(dto, 0, 0, DTM_PROCLAYOUT, 0, 1)) == 0) {
267 				return false;
268 			}
269 		}
270 
271 		err = SaveDTObjectA(dto, NULL, NULL, path, DTWM_IFF, FALSE, NULL);
272 		DisposeDTObject(dto);
273 	}
274 
275 	if(err == 0) return false;
276 		else return true;
277 }
278 
279 
280 /* exported function documented in amiga/bitmap.h */
amiga_bitmap_modified(void * bitmap)281 void amiga_bitmap_modified(void *bitmap)
282 {
283 	struct bitmap *bm = bitmap;
284 
285 #ifdef __amigaos4__
286 		/* unmap the buffer after 0.5s - we might need it imminently */
287 		ami_schedule(500, amiga_bitmap_unmap_buffer, bm);
288 #endif
289 
290 	if(bm->nativebm) ami_rtg_freebitmap(bm->nativebm);
291 	if(bm->drawhandle) ReleaseDrawHandle(bm->drawhandle);
292 	if(bm->native_mask) FreeRaster(bm->native_mask, bm->width, bm->height);
293 	bm->nativebm = NULL;
294 	bm->drawhandle = NULL;
295 	bm->native_mask = NULL;
296 	bm->native = AMI_NSBM_NONE;
297 }
298 
299 
300 /* exported function documented in amiga/bitmap.h */
amiga_bitmap_set_opaque(void * bitmap,bool opaque)301 void amiga_bitmap_set_opaque(void *bitmap, bool opaque)
302 {
303 	struct bitmap *bm = bitmap;
304 	assert(bitmap);
305 	bm->opaque = opaque;
306 }
307 
308 
309 /* exported function documented in amiga/bitmap.h */
amiga_bitmap_test_opaque(void * bitmap)310 bool amiga_bitmap_test_opaque(void *bitmap)
311 {
312 	struct bitmap *bm = bitmap;
313 	uint32 p = bm->width * bm->height;
314 	uint32 a = 0;
315 	uint32 *bmi = (uint32 *)amiga_bitmap_get_buffer(bm);
316 
317 	assert(bitmap);
318 
319 	for(a=0;a<p;a+=4)
320 	{
321 		if ((*bmi & 0x000000ffU) != 0x000000ffU) return false;
322 		bmi++;
323 	}
324 	return true;
325 }
326 
327 
328 /* exported function documented in amiga/bitmap.h */
amiga_bitmap_get_opaque(void * bitmap)329 bool amiga_bitmap_get_opaque(void *bitmap)
330 {
331 	struct bitmap *bm = bitmap;
332 	assert(bitmap);
333 	return bm->opaque;
334 }
335 
336 /**
337  * get width of a bitmap.
338  */
bitmap_get_width(void * bitmap)339 int bitmap_get_width(void *bitmap)
340 {
341 	struct bitmap *bm = bitmap;
342 
343 	if(bm)
344 	{
345 		return(bm->width);
346 	}
347 	else
348 	{
349 		return 0;
350 	}
351 }
352 
353 /**
354  * get height of a bitmap.
355  */
bitmap_get_height(void * bitmap)356 int bitmap_get_height(void *bitmap)
357 {
358 	struct bitmap *bm = bitmap;
359 
360 	if(bm)
361 	{
362 		return(bm->height);
363 	}
364 	else
365 	{
366 		return 0;
367 	}
368 }
369 
370 
371 /**
372  * Find the bytes per pixel of a bitmap
373  *
374  * \param  vbitmap  a bitmap, as returned by bitmap_create()
375  * \return bytes per pixel
376  */
bitmap_get_bpp(void * vbitmap)377 static size_t bitmap_get_bpp(void *vbitmap)
378 {
379 	struct bitmap *bitmap = (struct bitmap *)vbitmap;
380 	assert(bitmap);
381 	return 4;
382 }
383 
ami_bitmap_argb_to_rgba(struct bitmap * bm)384 static void ami_bitmap_argb_to_rgba(struct bitmap *bm)
385 {
386 	if(bm == NULL) return;
387 
388 	ULONG *data = (ULONG *)amiga_bitmap_get_buffer(bm);
389 	for(int i = 0; i < (bm->width * bm->height); i++) {
390 		data[i] = (data[i] << 8) | (data[i] >> 24);
391 	}
392 }
393 
ami_bitmap_rgba_to_argb(struct bitmap * bm)394 static void ami_bitmap_rgba_to_argb(struct bitmap *bm)
395 {
396 	if(bm == NULL) return;
397 
398 	ULONG *data = (ULONG *)amiga_bitmap_get_buffer(bm);
399 	for(int i = 0; i < (bm->width * bm->height); i++) {
400 		data[i] = (data[ i] >> 8) | (data[i] << 24);
401 	}
402 }
403 
404 #ifdef BITMAP_DUMP
bitmap_dump(struct bitmap * bitmap)405 void bitmap_dump(struct bitmap *bitmap)
406 {
407 	int x,y;
408 	ULONG *bm = (ULONG *)amiga_bitmap_get_buffer(bitmap);
409 
410 	printf("Width=%ld, Height=%ld, Opaque=%s\nnativebm=%lx, width=%ld, height=%ld\n",
411 		bitmap->width, bitmap->height, bitmap->opaque ? "true" : "false",
412 		bitmap->nativebm, bitmap->nativebmwidth, bitmap->nativebmheight);
413 
414 	for(y = 0; y < bitmap->height; y++) {
415 		for(x = 0; x < bitmap->width; x++) {
416 			printf("%lx ", bm[(y*bitmap->width) + x]);
417 		}
418 		printf("\n");
419 	}
420 }
421 #endif
422 
ami_datatype_object_from_bitmap(struct bitmap * bitmap)423 Object *ami_datatype_object_from_bitmap(struct bitmap *bitmap)
424 {
425 	Object *dto;
426 	struct BitMapHeader *bmhd;
427 
428 	if((dto = NewDTObject(NULL,
429 					DTA_SourceType,DTST_RAM,
430 					DTA_GroupID,GID_PICTURE,
431 					//DTA_BaseName,"ilbm",
432 					PDTA_DestMode,PMODE_V43,
433 					TAG_DONE)))
434 	{
435 		if(GetDTAttrs(dto,PDTA_BitMapHeader,&bmhd,TAG_DONE))
436 		{
437 			bmhd->bmh_Width = (UWORD)bitmap_get_width(bitmap);
438 			bmhd->bmh_Height = (UWORD)bitmap_get_height(bitmap);
439 			bmhd->bmh_Depth = (UBYTE)bitmap_get_bpp(bitmap) * 8;
440 			if(!amiga_bitmap_get_opaque(bitmap)) bmhd->bmh_Masking = mskHasAlpha;
441 		}
442 
443 		SetDTAttrs(dto,NULL,NULL,
444 					DTA_ObjName, bitmap->url ? nsurl_access(bitmap->url) : "",
445 					DTA_ObjAnnotation,bitmap->title,
446 					DTA_ObjAuthor,messages_get("NetSurf"),
447 					DTA_NominalHoriz,bitmap_get_width(bitmap),
448 					DTA_NominalVert,bitmap_get_height(bitmap),
449 					PDTA_SourceMode,PMODE_V43,
450 					TAG_DONE);
451 
452 		IDoMethod(dto, PDTM_WRITEPIXELARRAY, amiga_bitmap_get_buffer(bitmap),
453 					PBPAFMT_RGBA, amiga_bitmap_get_rowstride(bitmap), 0, 0,
454 					bitmap_get_width(bitmap), bitmap_get_height(bitmap));
455 	}
456 
457 	return dto;
458 }
459 
460 /* Quick way to get an object on disk into a struct bitmap */
ami_bitmap_from_datatype(char * filename)461 struct bitmap *ami_bitmap_from_datatype(char *filename)
462 {
463 	Object *dto;
464 	struct bitmap *bm = NULL;
465 
466 	if((dto = NewDTObject(filename,
467 					DTA_GroupID, GID_PICTURE,
468 					PDTA_DestMode, PMODE_V43,
469 					PDTA_PromoteMask, TRUE,
470 					TAG_DONE))) {
471 		struct BitMapHeader *bmh;
472 
473 		if(GetDTAttrs(dto, PDTA_BitMapHeader, &bmh, TAG_DONE))
474 		{
475 			bm = amiga_bitmap_create(bmh->bmh_Width, bmh->bmh_Height, 0);
476 
477 			IDoMethod(dto, PDTM_READPIXELARRAY, amiga_bitmap_get_buffer(bm),
478 				PBPAFMT_RGBA, amiga_bitmap_get_rowstride(bm), 0, 0,
479 				bmh->bmh_Width, bmh->bmh_Height);
480 
481 			amiga_bitmap_set_opaque(bm, amiga_bitmap_test_opaque(bm));
482 		}
483 		DisposeDTObject(dto);
484 	}
485 
486 	return bm;
487 }
488 
ami_bitmap_get_generic(struct bitmap * bitmap,int width,int height,struct BitMap * restrict friendbm,int type)489 static inline struct BitMap *ami_bitmap_get_generic(struct bitmap *bitmap,
490 			int width, int height, struct BitMap *restrict friendbm, int type)
491 {
492 	struct BitMap *restrict tbm = NULL;
493 	struct Screen *scrn = ami_gui_get_screen();
494 
495 	if(bitmap->nativebm)
496 	{
497 		if((bitmap->nativebmwidth == width) && (bitmap->nativebmheight == height)) {
498 			tbm = bitmap->nativebm;
499 			return tbm;
500 		} else if((bitmap->nativebmwidth == bitmap->width) &&
501 				(bitmap->nativebmheight == bitmap->height)) { // >= width/height ?
502 			tbm = bitmap->nativebm;
503 		} else {
504 			if(bitmap->nativebm) amiga_bitmap_modified(bitmap);
505 		}
506 	}
507 
508 	if(tbm == NULL) {
509 		if(type == AMI_NSBM_TRUECOLOUR) {
510 			tbm = ami_rtg_allocbitmap(bitmap->width, bitmap->height, 32, 0,
511 										friendbm, AMI_BITMAP_FORMAT);
512 			if(tbm == NULL) return NULL;
513 
514 			ami_rtg_writepixelarray(amiga_bitmap_get_buffer(bitmap),
515 										tbm, bitmap->width, bitmap->height,
516 										bitmap->width * 4, AMI_BITMAP_FORMAT);
517 		} else {
518 			tbm = ami_rtg_allocbitmap(width, height,
519 										8, 0, friendbm, AMI_BITMAP_FORMAT);
520 			if(tbm == NULL) return NULL;
521 
522 			if(GuiGFXBase != NULL) {
523 				struct RastPort rp;
524 				InitRastPort(&rp);
525 				rp.BitMap = tbm;
526 				ULONG dithermode = DITHERMODE_NONE;
527 
528 				if(nsoption_int(dither_quality) == 1) {
529 					dithermode = DITHERMODE_EDD;
530 				} else if(nsoption_int(dither_quality) == 2) {
531 					dithermode = DITHERMODE_FS;
532 				}
533 
534 				ami_bitmap_rgba_to_argb(bitmap);
535 				bitmap->drawhandle = ObtainDrawHandle(
536 					NULL,
537 					&rp,
538 					scrn->ViewPort.ColorMap,
539 					GGFX_DitherMode, dithermode,
540 					TAG_DONE);
541 				if(bitmap->drawhandle) {
542 					APTR ddh = CreateDirectDrawHandle(bitmap->drawhandle,
543 											bitmap->width, bitmap->height,
544 											width, height, NULL);
545 
546 					DirectDrawTrueColor(ddh, (ULONG *)amiga_bitmap_get_buffer(bitmap), 0, 0, TAG_DONE);
547 					DeleteDirectDrawHandle(ddh);
548 					ReleaseDrawHandle(bitmap->drawhandle);
549 					bitmap->drawhandle = NULL;
550 				}
551 				ami_bitmap_argb_to_rgba(bitmap);
552 			} else {
553 				if(guigfx_warned == false) {
554 					amiga_warn_user("BMConvErr", NULL);
555 					guigfx_warned = true;
556 				}
557 			}
558 		}
559 
560 		if(((type == AMI_NSBM_TRUECOLOUR) && (nsoption_int(cache_bitmaps) == 2)) ||
561 				((type == AMI_NSBM_PALETTEMAPPED) && (((bitmap->width == width) &&
562 				(bitmap->height == height) && (nsoption_int(cache_bitmaps) == 2)) ||
563 				(nsoption_int(cache_bitmaps) >= 1)))) {
564 			bitmap->nativebm = tbm;
565 			if(type == AMI_NSBM_TRUECOLOUR) {
566 				bitmap->nativebmwidth = bitmap->width;
567 				bitmap->nativebmheight = bitmap->height;
568 			} else {
569 				bitmap->nativebmwidth = width;
570 				bitmap->nativebmheight = height;
571 			}
572 			bitmap->native = type;
573 		}
574 
575 		if(type == AMI_NSBM_PALETTEMAPPED)
576 			return tbm;
577 	}
578 
579 	if((bitmap->width != width) || (bitmap->height != height)) {
580 		struct BitMap *restrict scaledbm;
581 		struct BitScaleArgs bsa;
582 		int depth = 32;
583 		if(type == AMI_NSBM_PALETTEMAPPED) depth = 8;
584 
585 		scaledbm = ami_rtg_allocbitmap(width, height, depth, 0,
586 									friendbm, AMI_BITMAP_FORMAT);
587 #ifdef __amigaos4__
588 		if(__builtin_expect(((GfxBase->LibNode.lib_Version >= 53) &&
589 			(type == AMI_NSBM_TRUECOLOUR)), 1)) {
590 			/* AutoDoc says v52, but this function isn't in OS4.0, so checking for v53 (OS4.1)
591 			 * Additionally, when we use friend BitMaps in non 32-bit modes it freezes the OS */
592 
593 			uint32 flags = 0;
594 			uint32 err = COMPERR_Success;
595 #ifdef AMI_NS_TRIANGLE_SCALING
596 			struct vertex vtx[6];
597 			VTX_RECT(0, 0, bitmap->width, bitmap->height, 0, 0, width, height);
598 
599 			flags = COMPFLAG_HardwareOnly;
600 			if(nsoption_bool(scale_quality) == true) flags |= COMPFLAG_SrcFilter;
601 
602 			err = CompositeTags(COMPOSITE_Src, tbm, scaledbm,
603 						COMPTAG_VertexArray, vtx,
604 						COMPTAG_VertexFormat, COMPVF_STW0_Present,
605 						COMPTAG_NumTriangles, 2,
606 						COMPTAG_Flags, flags,
607 						COMPTAG_FriendBitMap, scrn->RastPort.BitMap,
608 						TAG_DONE);
609 
610 			if (err != COMPERR_Success) {
611 				NSLOG(netsurf, INFO,
612 				      "Composite error %ld - falling back",
613 				      err);
614 				/* If it failed, do it again the way
615 				 *  which works in software
616 				 */
617 #else
618 			{
619 #endif
620 				flags = 0;
621 				if(nsoption_bool(scale_quality) == true) flags |= COMPFLAG_SrcFilter;
622 
623 				err = CompositeTags(COMPOSITE_Src, tbm, scaledbm,
624 						COMPTAG_ScaleX, COMP_FLOAT_TO_FIX((float)width/bitmap->width),
625 						COMPTAG_ScaleY, COMP_FLOAT_TO_FIX((float)height/bitmap->height),
626 						COMPTAG_Flags, flags,
627 						COMPTAG_FriendBitMap, scrn->RastPort.BitMap,
628 						TAG_DONE);
629 				/* If it still fails... it's non-fatal */
630 				NSLOG(netsurf, INFO,
631 				      "Fallback returned error %ld", err);
632 			}
633 		} else /* Do it the old-fashioned way.  This is pretty slow, even on OS4.1 */
634 #endif
635 		{
636 			bsa.bsa_SrcX = 0;
637 			bsa.bsa_SrcY = 0;
638 			bsa.bsa_SrcWidth = bitmap->width;
639 			bsa.bsa_SrcHeight = bitmap->height;
640 			bsa.bsa_DestX = 0;
641 			bsa.bsa_DestY = 0;
642 			bsa.bsa_XSrcFactor = bitmap->width;
643 			bsa.bsa_XDestFactor = width;
644 			bsa.bsa_YSrcFactor = bitmap->height;
645 			bsa.bsa_YDestFactor = height;
646 			bsa.bsa_SrcBitMap = tbm;
647 			bsa.bsa_DestBitMap = scaledbm;
648 			bsa.bsa_Flags = 0;
649 
650 			BitMapScale(&bsa);
651 		}
652 
653 		if(bitmap->nativebm != tbm) ami_rtg_freebitmap(bitmap->nativebm);
654 		ami_rtg_freebitmap(tbm);
655 		tbm = scaledbm;
656 		bitmap->nativebm = NULL;
657 		bitmap->native = AMI_NSBM_NONE;
658 
659 		if(nsoption_int(cache_bitmaps) >= 1)
660 		{
661 			bitmap->nativebm = tbm;
662 			bitmap->nativebmwidth = width;
663 			bitmap->nativebmheight = height;
664 			bitmap->native = type;
665 		}
666 	}
667 
668 	return tbm;
669 }
670 
671 
672 static inline struct BitMap *ami_bitmap_get_truecolour(struct bitmap *bitmap,
673 			int width, int height, struct BitMap *friendbm)
674 {
675 	if((bitmap->native != AMI_NSBM_NONE) && (bitmap->native != AMI_NSBM_TRUECOLOUR)) {
676 		amiga_bitmap_modified(bitmap);
677 	}
678 
679 	return ami_bitmap_get_generic(bitmap, width, height, friendbm, AMI_NSBM_TRUECOLOUR);
680 }
681 
682 PLANEPTR ami_bitmap_get_mask(struct bitmap *bitmap, int width,
683 			int height, struct BitMap *n_bm)
684 {
685 	uint32 *bmi = (uint32 *) amiga_bitmap_get_buffer(bitmap);
686 	UBYTE maskbit = 0;
687 	ULONG bm_width;
688 	int y, x, bpr;
689 
690 	if((height != bitmap->height) || (width != bitmap->width)) return NULL;
691 	if(amiga_bitmap_get_opaque(bitmap) == true) return NULL;
692 	if(bitmap->native_mask) return bitmap->native_mask;
693 
694 	bm_width = GetBitMapAttr(n_bm, BMA_WIDTH);
695 	bpr = RASSIZE(bm_width, 1);
696 	bitmap->native_mask = AllocRaster(bm_width, height);
697 	SetMem(bitmap->native_mask, 0, bpr * height);
698 
699 	for(y=0; y<height; y++) {
700 		for(x=0; x<width; x++) {
701 			if ((*bmi & 0x000000ffU) <= (ULONG)nsoption_int(mask_alpha)) maskbit = 0;
702 				else maskbit = 1;
703 			bmi++;
704 			bitmap->native_mask[(y*bpr) + (x/8)] |=
705 				maskbit << (7 - (x % 8));
706 		}
707 	}
708 
709 	return bitmap->native_mask;
710 }
711 
712 static inline struct BitMap *ami_bitmap_get_palettemapped(struct bitmap *bitmap,
713 					int width, int height, struct BitMap *friendbm)
714 {
715 	if((bitmap->native != AMI_NSBM_NONE) && (bitmap->native != AMI_NSBM_PALETTEMAPPED)) {
716 		amiga_bitmap_modified(bitmap);
717 	}
718 
719 	return ami_bitmap_get_generic(bitmap, width, height, friendbm, AMI_NSBM_PALETTEMAPPED);
720 }
721 
722 struct BitMap *ami_bitmap_get_native(struct bitmap *bitmap,
723 				int width, int height, bool palette_mapped, struct BitMap *friendbm)
724 {
725 	if(bitmap == NULL) return NULL;
726 
727 	if(__builtin_expect(palette_mapped == true, 0)) {
728 		return ami_bitmap_get_palettemapped(bitmap, width, height, friendbm);
729 	} else {
730 		return ami_bitmap_get_truecolour(bitmap, width, height, friendbm);
731 	}
732 }
733 
734 void ami_bitmap_fini(void)
735 {
736 	if(pool_bitmap) ami_memory_itempool_delete(pool_bitmap);
737 	pool_bitmap = NULL;
738 }
739 
740 static nserror bitmap_render(struct bitmap *bitmap, struct hlcache_handle *content)
741 {
742 #ifdef __amigaos4__
743 	NSLOG(netsurf, INFO, "Entering bitmap_render");
744 
745 	int plot_width;
746 	int plot_height;
747 	struct gui_globals *bm_globals;
748 
749 	plot_width = MIN(content_get_width(content), bitmap->width);
750 	plot_height = ((plot_width * bitmap->height) + (bitmap->width / 2)) /
751 			bitmap->width;
752 
753 	bm_globals = ami_plot_ra_alloc(bitmap->width, bitmap->height, true, false);
754 	ami_clearclipreg(bm_globals);
755 
756 	struct redraw_context ctx = {
757 		.interactive = false,
758 		.background_images = true,
759 		.plot = &amiplot,
760 		.priv = bm_globals
761 	};
762 
763 	content_scaled_redraw(content, plot_width, plot_height, &ctx);
764 
765 	BltBitMapTags(	BLITA_SrcX, 0,
766 					BLITA_SrcY, 0,
767 					BLITA_Width, bitmap->width,
768 					BLITA_Height, bitmap->height,
769 					BLITA_Source, ami_plot_ra_get_bitmap(bm_globals),
770 					BLITA_SrcType, BLITT_BITMAP,
771 					BLITA_Dest, amiga_bitmap_get_buffer(bitmap),
772 					BLITA_DestType, BLITT_ARGB32,
773 					BLITA_DestBytesPerRow, 4 * bitmap->width,
774 					BLITA_DestX, 0,
775 					BLITA_DestY, 0,
776 					TAG_DONE);
777 
778 	ami_bitmap_argb_to_rgba(bitmap);
779 
780 	/**\todo In theory we should be able to move the bitmap to our native area
781 		to try to avoid re-conversion (at the expense of memory) */
782 
783 	ami_plot_ra_free(bm_globals);
784 	amiga_bitmap_set_opaque(bitmap, true);
785 #else
786 #warning FIXME for OS3 (in current state none of bitmap_render can work!)
787 #endif
788 
789 	return NSERROR_OK;
790 }
791 
792 void ami_bitmap_set_url(struct bitmap *bm, struct nsurl *url)
793 {
794 	if(bm->url != NULL) return;
795 	bm->url = nsurl_ref(url);
796 }
797 
798 void ami_bitmap_set_title(struct bitmap *bm, const char *title)
799 {
800 	if(bm->title != NULL) return;
801 	bm->title = strdup(title);
802 }
803 
804 void ami_bitmap_set_icondata(struct bitmap *bm, ULONG *icondata)
805 {
806 	bm->icondata = icondata;
807 }
808 
809 void ami_bitmap_free_icondata(struct bitmap *bm)
810 {
811 	if(bm->icondata) free(bm->icondata);
812 	bm->icondata = NULL;
813 }
814 
815 bool ami_bitmap_is_nativebm(struct bitmap *bm, struct BitMap *nbm)
816 {
817 	if(bm->nativebm == nbm) return true;
818 		else return false;
819 }
820 
821 
822 static struct gui_bitmap_table bitmap_table = {
823 	.create = amiga_bitmap_create,
824 	.destroy = amiga_bitmap_destroy,
825 	.set_opaque = amiga_bitmap_set_opaque,
826 	.get_opaque = amiga_bitmap_get_opaque,
827 	.test_opaque = amiga_bitmap_test_opaque,
828 	.get_buffer = amiga_bitmap_get_buffer,
829 	.get_rowstride = amiga_bitmap_get_rowstride,
830 	.get_width = bitmap_get_width,
831 	.get_height = bitmap_get_height,
832 	.get_bpp = bitmap_get_bpp,
833 	.save = amiga_bitmap_save,
834 	.modified = amiga_bitmap_modified,
835 	.render = bitmap_render,
836 };
837 
838 struct gui_bitmap_table *amiga_bitmap_table = &bitmap_table;
839