/*
 * Copyright 2010 Ole Loots <ole@monochrom.net>
 *
 * This file is part of NetSurf, http://www.netsurf-browser.org/
 *
 * NetSurf is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * NetSurf is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <assert.h>
#include <sys/types.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#include <stdbool.h>
#include <mt_gem.h>
#include <mint/osbind.h>

#include "utils/log.h"
#include "utils/utf8.h"
#include "utils/utils.h"
#include "netsurf/bitmap.h"
#include "netsurf/plotters.h"
#include "netsurf/mouse.h"

#include "atari/gui.h"
#include "atari/osspec.h"
#include "atari/misc.h"
#include "atari/bitmap.h"
#include "utils/nsoption.h"
#include "atari/plot/eddi.h"
#include "atari/plot/fontplot.h"
#include "atari/plot/plot.h"

void vq_scrninfo(VdiHdl handle, short *work_out);

struct s_view {
    short x;                /**< drawing (screen) offset x                */
    short y;                /**< drawing (screen) offset y                */
    short w;                /**< width of buffer, not in sync with vis_w  */
    short h;                /**< height of buffer, not in sync with vis_w */
    short vis_x;            /**< visible rectangle of the screen buffer   */
    short vis_y;            /**< coords are relative to plot location     */
    short vis_w;            /**< clipped to screen dimensions             */
    short vis_h;            /**< visible width                            */
    struct rect abs_clipping; /**< The toplevel clipping rectangle        */
    struct rect clipping;   /**< actual clipping rectangle                */
    float scale;
};

/**
 * Garbage collection of the snapshot routine
 *
 * this should be called after you are done with the data returned by
 *  snapshot_create don't access the screenshot after you called this
 *  function
 */
static void snapshot_suspend(void);

/**
 * destroy memory used by screenshot
 */
static void snapshot_destroy(void);

#ifdef WITH_8BPP_SUPPORT
static unsigned short sys_pal[256][3]; /*RGB*/
static unsigned short pal[256][3];     /*RGB*/
static char rgb_lookup[256][4];
short web_std_colors[6] = {0, 51, 102, 153, 204, 255};

unsigned short vdi_web_pal[216][3] = {
    {0x000,0x000,0x000}, {0x0c8,0x000,0x000}, {0x190,0x000,0x000},
    {0x258,0x000,0x000}, {0x320,0x000,0x000}, {0x3e8,0x000,0x000},
    {0x000,0x0c8,0x000}, {0x0c8,0x0c8,0x000}, {0x190,0x0c8,0x000},
    {0x258,0x0c8,0x000}, {0x320,0x0c8,0x000}, {0x3e8,0x0c8,0x000},
    {0x000,0x190,0x000}, {0x0c8,0x190,0x000}, {0x190,0x190,0x000},
    {0x258,0x190,0x000}, {0x320,0x190,0x000}, {0x3e8,0x190,0x000},
    {0x000,0x258,0x000}, {0x0c8,0x258,0x000}, {0x190,0x258,0x000},
    {0x258,0x258,0x000}, {0x320,0x258,0x000}, {0x3e8,0x258,0x000},
    {0x000,0x320,0x000}, {0x0c8,0x320,0x000}, {0x190,0x320,0x000},
    {0x258,0x320,0x000}, {0x320,0x320,0x000}, {0x3e8,0x320,0x000},
    {0x000,0x3e8,0x000}, {0x0c8,0x3e8,0x000}, {0x190,0x3e8,0x000},
    {0x258,0x3e8,0x000}, {0x320,0x3e8,0x000}, {0x3e8,0x3e8,0x000},
    {0x000,0x000,0x0c8}, {0x0c8,0x000,0x0c8}, {0x190,0x000,0x0c8},
    {0x258,0x000,0x0c8}, {0x320,0x000,0x0c8}, {0x3e8,0x000,0x0c8},
    {0x000,0x0c8,0x0c8}, {0x0c8,0x0c8,0x0c8}, {0x190,0x0c8,0x0c8},
    {0x258,0x0c8,0x0c8}, {0x320,0x0c8,0x0c8}, {0x3e8,0x0c8,0x0c8},
    {0x000,0x190,0x0c8}, {0x0c8,0x190,0x0c8}, {0x190,0x190,0x0c8},
    {0x258,0x190,0x0c8}, {0x320,0x190,0x0c8}, {0x3e8,0x190,0x0c8},
    {0x000,0x258,0x0c8}, {0x0c8,0x258,0x0c8}, {0x190,0x258,0x0c8},
    {0x258,0x258,0x0c8}, {0x320,0x258,0x0c8}, {0x3e8,0x258,0x0c8},
    {0x000,0x320,0x0c8}, {0x0c8,0x320,0x0c8}, {0x190,0x320,0x0c8},
    {0x258,0x320,0x0c8}, {0x320,0x320,0x0c8}, {0x3e8,0x320,0x0c8},
    {0x000,0x3e8,0x0c8}, {0x0c8,0x3e8,0x0c8}, {0x190,0x3e8,0x0c8},
    {0x258,0x3e8,0x0c8}, {0x320,0x3e8,0x0c8}, {0x3e8,0x3e8,0x0c8},
    {0x000,0x000,0x190}, {0x0c8,0x000,0x190}, {0x190,0x000,0x190},
    {0x258,0x000,0x190}, {0x320,0x000,0x190}, {0x3e8,0x000,0x190},
    {0x000,0x0c8,0x190}, {0x0c8,0x0c8,0x190}, {0x190,0x0c8,0x190},
    {0x258,0x0c8,0x190}, {0x320,0x0c8,0x190}, {0x3e8,0x0c8,0x190},
    {0x000,0x190,0x190}, {0x0c8,0x190,0x190}, {0x190,0x190,0x190},
    {0x258,0x190,0x190}, {0x320,0x190,0x190}, {0x3e8,0x190,0x190},
    {0x000,0x258,0x190}, {0x0c8,0x258,0x190}, {0x190,0x258,0x190},
    {0x258,0x258,0x190}, {0x320,0x258,0x190}, {0x3e8,0x258,0x190},
    {0x000,0x320,0x190}, {0x0c8,0x320,0x190}, {0x190,0x320,0x190},
    {0x258,0x320,0x190}, {0x320,0x320,0x190}, {0x3e8,0x320,0x190},
    {0x000,0x3e8,0x190}, {0x0c8,0x3e8,0x190}, {0x190,0x3e8,0x190},
    {0x258,0x3e8,0x190}, {0x320,0x3e8,0x190}, {0x3e8,0x3e8,0x190},
    {0x000,0x000,0x258}, {0x0c8,0x000,0x258}, {0x190,0x000,0x258},
    {0x258,0x000,0x258}, {0x320,0x000,0x258}, {0x3e8,0x000,0x258},
    {0x000,0x0c8,0x258}, {0x0c8,0x0c8,0x258}, {0x190,0x0c8,0x258},
    {0x258,0x0c8,0x258}, {0x320,0x0c8,0x258}, {0x3e8,0x0c8,0x258},
    {0x000,0x190,0x258}, {0x0c8,0x190,0x258}, {0x190,0x190,0x258},
    {0x258,0x190,0x258}, {0x320,0x190,0x258}, {0x3e8,0x190,0x258},
    {0x000,0x258,0x258}, {0x0c8,0x258,0x258}, {0x190,0x258,0x258},
    {0x258,0x258,0x258}, {0x320,0x258,0x258}, {0x3e8,0x258,0x258},
    {0x000,0x320,0x258}, {0x0c8,0x320,0x258}, {0x190,0x320,0x258},
    {0x258,0x320,0x258}, {0x320,0x320,0x258}, {0x3e8,0x320,0x258},
    {0x000,0x3e8,0x258}, {0x0c8,0x3e8,0x258}, {0x190,0x3e8,0x258},
    {0x258,0x3e8,0x258}, {0x320,0x3e8,0x258}, {0x3e8,0x3e8,0x258},
    {0x000,0x000,0x320}, {0x0c8,0x000,0x320}, {0x190,0x000,0x320},
    {0x258,0x000,0x320}, {0x320,0x000,0x320}, {0x3e8,0x000,0x320},
    {0x000,0x0c8,0x320}, {0x0c8,0x0c8,0x320}, {0x190,0x0c8,0x320},
    {0x258,0x0c8,0x320}, {0x320,0x0c8,0x320}, {0x3e8,0x0c8,0x320},
    {0x000,0x190,0x320}, {0x0c8,0x190,0x320}, {0x190,0x190,0x320},
    {0x258,0x190,0x320}, {0x320,0x190,0x320}, {0x3e8,0x190,0x320},
    {0x000,0x258,0x320}, {0x0c8,0x258,0x320}, {0x190,0x258,0x320},
    {0x258,0x258,0x320}, {0x320,0x258,0x320}, {0x3e8,0x258,0x320},
    {0x000,0x320,0x320}, {0x0c8,0x320,0x320}, {0x190,0x320,0x320},
    {0x258,0x320,0x320}, {0x320,0x320,0x320}, {0x3e8,0x320,0x320},
    {0x000,0x3e8,0x320}, {0x0c8,0x3e8,0x320}, {0x190,0x3e8,0x320},
    {0x258,0x3e8,0x320}, {0x320,0x3e8,0x320}, {0x3e8,0x3e8,0x320},
    {0x000,0x000,0x3e8}, {0x0c8,0x000,0x3e8}, {0x190,0x000,0x3e8},
    {0x258,0x000,0x3e8}, {0x320,0x000,0x3e8}, {0x3e8,0x000,0x3e8},
    {0x000,0x0c8,0x3e8}, {0x0c8,0x0c8,0x3e8}, {0x190,0x0c8,0x3e8},
    {0x258,0x0c8,0x3e8}, {0x320,0x0c8,0x3e8}, {0x3e8,0x0c8,0x3e8},
    {0x000,0x190,0x3e8}, {0x0c8,0x190,0x3e8}, {0x190,0x190,0x3e8},
    {0x258,0x190,0x3e8}, {0x320,0x190,0x3e8}, {0x3e8,0x190,0x3e8},
    {0x000,0x258,0x3e8}, {0x0c8,0x258,0x3e8}, {0x190,0x258,0x3e8},
    {0x258,0x258,0x3e8}, {0x320,0x258,0x3e8}, {0x3e8,0x258,0x3e8},
    {0x000,0x320,0x3e8}, {0x0c8,0x320,0x3e8}, {0x190,0x320,0x3e8},
    {0x258,0x320,0x3e8}, {0x320,0x320,0x3e8}, {0x3e8,0x320,0x3e8},
    {0x000,0x3e8,0x3e8}, {0x0c8,0x3e8,0x3e8}, {0x190,0x3e8,0x3e8},
    {0x258,0x3e8,0x3e8}, {0x320,0x3e8,0x3e8}, {0x3e8,0x3e8,0x3e8}
};
#endif

/* Error code translations: */
static const char * plot_error_codes[] = {
    "None",
    "ERR_BUFFERSIZE_EXCEEDS_SCREEN",
    "ERR_NO_MEM",
    "ERR_PLOTTER_NOT_AVAILABLE"
};

FONT_PLOTTER fplotter = NULL;

extern short vdih;

/* temp buffer for bitmap conversion: */
static void * buf_packed;
static int size_buf_packed;

/* temp buffer for bitmap conversion: */
void * buf_planar;
int size_buf_planar;

/* buffer for plot operations that require device format, */
/* currently used for transparent mfdb blits and snapshots: */
static MFDB buf_scr;
static int size_buf_scr;

/* buffer for std form, used during 8bpp snapshot */
MFDB buf_std;
int size_buf_std;

struct bitmap * buf_scr_compat;

/* intermediate bitmap format */
static HermesFormat vfmt;

/* no screen format here, hermes may not suitable for it */

/* netsurf source bitmap format */
static HermesFormat nsfmt;

struct s_vdi_sysinfo vdi_sysinfo;
/* bit depth of framebuffers: */
static int atari_plot_bpp_virt;
static struct s_view view;

//static HermesHandle hermes_pal_h; /* hermes palette handle */
static HermesHandle hermes_cnv_h; /* hermes converter instance handle */
static HermesHandle hermes_res_h;

//static short prev_vdi_clip[4];
static struct bitmap snapshot;

VdiHdl atari_plot_vdi_handle = -1;
unsigned long atari_plot_flags;
unsigned long atari_font_flags;

typedef bool (*bitmap_convert_fnc)(struct bitmap * img, int x, int y, GRECT * clip, uint32_t bg, uint32_t flags, MFDB *out  );
static bitmap_convert_fnc bitmap_convert;

/* exported interface documented in atari/plot.h */
const char* plot_err_str(int i)
{
    return (plot_error_codes[abs(i)]);
}


/**
 * Set line drawing color by passing netsurf XBGR "colour" type.
 *
 * \param vdih The vdi handle
 * \param cin  The netsurf colour value
 */
inline static void vsl_rgbcolor(short vdih, colour cin)
{
#ifdef WITH_8BPP_SUPPORT
    if( vdi_sysinfo.scr_bpp > 8 ) {
#endif
	RGB1000 c; /* a struct with three (RGB) shorts */
	rgb_to_vdi1000( (unsigned char*)&cin, &c);
	vs_color(vdih, OFFSET_CUSTOM_COLOR, (short *)&c);
	vsl_color(vdih, OFFSET_CUSTOM_COLOR);
#ifdef WITH_8BPP_SUPPORT
    } else {
	if( vdi_sysinfo.scr_bpp >= 4 ){
	    vsl_color(vdih, RGB_TO_VDI(cin));
	}
	else
	    vsl_color(vdih, BLACK);
    }
#endif
}


/**
 * Set fill color by passing netsurf XBGR "colour" type.
 *
 * \param vdih The vdi handle
 * \param cin  The netsurf colour value
 */
inline static void vsf_rgbcolor(short vdih, colour cin)
{
#ifdef WITH_8BPP_SUPPORT
    if( vdi_sysinfo.scr_bpp > 8 ) {
#endif
	RGB1000 c; /* a struct with three (RGB) shorts */
	rgb_to_vdi1000( (unsigned char*)&cin, &c);
	vs_color( vdih, OFFSET_CUSTOM_COLOR, (short *)&c);
	vsf_color( vdih, OFFSET_CUSTOM_COLOR );
#ifdef WITH_8BPP_SUPPORT
    } else {
	if( vdi_sysinfo.scr_bpp >= 4 ){
	    vsf_color( vdih, RGB_TO_VDI(cin) );
	}
	else
	    vsf_color( vdih, WHITE );
    }
#endif
}


/**
 * Get current visible coords
 */
inline static void plot_get_visible_grect(GRECT * out)
{
    out->g_x = view.vis_x;
    out->g_y = view.vis_y;
    out->g_w = view.vis_w;
    out->g_h = view.vis_h;
}


/* calculate visible area of framebuffer in coords relative to framebuffer */
/* position                                                                */
/*  result:                                                                */
/* this function should calculates an rectangle relative to the plot origin*/
/*  and size.                                                              */
/*      If the ploter coords do not fall within the screen region,         */
/*      all values of the region are set to zero.                          */
inline static void update_visible_rect(void)
{
    GRECT screen;           // dimensions of the screen
    GRECT frame;            // dimensions of the drawing area
    GRECT common;           // dimensions of intersection of both

    screen.g_x = 0;
    screen.g_y = 0;
    screen.g_w = vdi_sysinfo.scr_w;
    screen.g_h = vdi_sysinfo.scr_h;

    common.g_x = frame.g_x = view.x;
    common.g_y = frame.g_y = view.y;
    common.g_w = frame.g_w = view.w;
    common.g_h = frame.g_h = view.h;

    if (rc_intersect(&screen, &common)) {
	view.vis_w = common.g_w;
	view.vis_h = common.g_h;
	if (view.x < screen.g_x)
	    view.vis_x = frame.g_w - common.g_w;
	else
	    view.vis_x = 0;
	if (view.y <screen.g_y)
	    view.vis_y = frame.g_h - common.g_h;
	else
	    view.vis_y = 0;
    } else {
	view.vis_w = view.vis_h = 0;
	view.vis_x = view.vis_y = 0;
    }
}


/**
 * Returns the visible parts of the box
 *
 * The returned values are relative coords within framebuffer,
 * relative to screen coords (normally starting at 0,0 )
 */
inline static bool fbrect_to_screen(GRECT box, GRECT * ret)
{
    GRECT out, vis, screen;

    screen.g_x = 0;
    screen.g_y = 0;
    screen.g_w = vdi_sysinfo.scr_w;
    screen.g_h = vdi_sysinfo.scr_h;

    /* get visible region: */
    vis.g_x = view.x;
    vis.g_y = view.y;
    vis.g_w = view.w;
    vis.g_h = view.h;

    if ( !rc_intersect( &screen, &vis ) ) {
	return( false );
    }
    vis.g_x = view.w - vis.g_w;
    vis.g_y = view.h - vis.g_h;

    /* clip box to visible region: */
    if( !rc_intersect(&vis, &box) ) {
	return( false );
    }
    out.g_x = box.g_x + view.x;
    out.g_y = box.g_y + view.y;
    out.g_w = box.g_w;
    out.g_h = box.g_h;
    *ret = out;
    return ( true );
}


/**
 * copy an rectangle from the plot buffer to screen
 *
 * because this is an on-screen plotter, this is an screen to screen copy.
 */
bool plot_copy_rect(GRECT src, GRECT dst)
{
    MFDB devmf;
    MFDB scrmf;
    short pxy[8];
    GRECT vis;

    /* clip to visible rect, only needed for onscreen renderer: */
    plot_get_visible_grect(&vis );

    if( !rc_intersect(&vis, &src) )
	return(true);
    if( !rc_intersect(&vis, &dst) )
	return(true);

    src.g_x = view.x + src.g_x;
    src.g_y = view.y + src.g_y;
    dst.g_x = view.x + dst.g_x;
    dst.g_y = view.y + dst.g_y;

    devmf.fd_addr = NULL;
    devmf.fd_w = src.g_w;
    devmf.fd_h = src.g_h;
    devmf.fd_wdwidth = 0;
    devmf.fd_stand = 0;
    devmf.fd_nplanes = 0;
    devmf.fd_r1 = devmf.fd_r2 = devmf.fd_r3 = 0;

    scrmf.fd_addr = NULL;
    scrmf.fd_w = dst.g_w;
    scrmf.fd_h = dst.g_h;
    scrmf.fd_wdwidth = 0 ;
    scrmf.fd_stand = 0;
    scrmf.fd_nplanes = 0;
    scrmf.fd_r1 = scrmf.fd_r2 = scrmf.fd_r3 = 0;

    pxy[0] = src.g_x;
    pxy[1] = src.g_y;
    pxy[2] = pxy[0] + src.g_w-1;
    pxy[3] = pxy[1] + src.g_h-1;
    pxy[4] = dst.g_x;
    pxy[5] = dst.g_y;
    pxy[6] = pxy[4] + dst.g_w-1;
    pxy[7] = pxy[5] + dst.g_h-1;
    plot_lock();
    vro_cpyfm( atari_plot_vdi_handle, S_ONLY, (short*)&pxy, &devmf,  &scrmf);
    plot_unlock();

    return(true);
}


/**
 * Fill the screen info structure.
 *
 * \param vdih The handle
 * \param[out] info The infor structure to fill.
 */
static void read_vdi_sysinfo(short vdih, struct s_vdi_sysinfo * info)
{
    /** \todo this long is being cast to a pointer */
    unsigned long cookie_EdDI=0;
    short out[300];
    memset( info, 0, sizeof(struct s_vdi_sysinfo) );

    info->vdi_handle = vdih;
    if ( tos_getcookie(C_EdDI, (long *)&cookie_EdDI) == C_NOTFOUND ) {
	info->EdDiVersion = 0;
    } else {
	info->EdDiVersion = EdDI_version( (void *)cookie_EdDI );
    }

    memset( &out, 0, sizeof(short)*300 );
    vq_extnd( vdih, 0, (short*)&out );
    info->scr_w = out[0]+1;
    info->scr_h = out[1]+1;
    if( out[39] == 2 ) {
	info->scr_bpp = 1;
	info->colors = out[39];
    } else {
	info->colors = out[39];
    }

    memset( &out, 0, sizeof(short)*300 );
    vq_extnd( vdih, 1, (short*)&out );
    info->scr_bpp = out[4];
    info->maxpolycoords = out[14];
    info->maxintin = out[15];
    if( out[30] & 1 ) {
	info->rasterscale = true;
    } else {
	info->rasterscale = false;
    }

    switch( info->scr_bpp ) {
    case 8:
	info->pixelsize=1;
	break;
    case 15:
    case 16:
	info->pixelsize=2;
	break;
    case 24:
	info->pixelsize=3;
	break;
    case 32:
	info->pixelsize=4;
	break;
    case 64:
	info->pixelsize=8;
	break;
    default:
	info->pixelsize=1;
	break;

    }
    info->pitch = info->scr_w * info->pixelsize;
    info->vdiformat = ( (info->scr_bpp <= 8) ? VDI_FORMAT_INTER : VDI_FORMAT_PACK);
    info->screensize = ( info->scr_w * info->pixelsize )  * info->scr_h;

    if( info->EdDiVersion >= EDDI_10 ) {
	memset( &out, 0, sizeof(short)*300 );
	vq_scrninfo(vdih, (short*)&out);
	info->vdiformat = out[0];
	info->clut = out[1];
	info->scr_bpp = out[2];
	info->hicolors =  *((unsigned long*) &out[3]);
	if( info->EdDiVersion >= EDDI_11 ) {
	    info->pitch = out[5];
	    info->screen = (void *) *((unsigned long *) &out[6]);
	}

	switch( info->clut ) {

	case VDI_CLUT_HARDWARE:
	    break;

	case VDI_CLUT_SOFTWARE:
	{
	    int component; /* red, green, blue, alpha, overlay */
	    int num_bit;
	    unsigned short *tmp_p;

	    /* We can build masks with info here */
	    tmp_p = (unsigned short *) &out[16];
	    for (component=0; component<5; component++) {
		for (num_bit=0; num_bit<16; num_bit++) {
		    unsigned short val;

		    val = *tmp_p++;

		    if (val == 0xffff) {
			continue;
		    }

		    switch(component) {
		    case 0:
			info->mask_r |= 1<< val;
			break;
		    case 1:
			info->mask_g |= 1<< val;
			break;
		    case 2:
			info->mask_b |= 1<< val;
			break;
		    case 3:
			info->mask_a |= 1<< val;
			break;
		    }
		}
	    }
	}

	/* Remove lower green bits for Intel endian screen */
	if ((info->mask_g == ((7<<13)|3)) ||
	    (info->mask_g == ((7<<13)|7))) {
	    info->mask_g &= ~(7<<13);
	}
	break;

	case VDI_CLUT_NONE:
	    break;
	}
    }
}


/**
 *  Convert an RGB color to an VDI Color
 */
inline void rgb_to_vdi1000(unsigned char * in, RGB1000 *out)
{
    double r = ((double)in[3]/255); /* prozentsatz red   */
    double g = ((double)in[2]/255); /* prozentsatz green */
    double b = ((double)in[1]/255); /* prozentsatz blue  */
    out->red = 1000 * r + 0.5;
    out->green = 1000 * g + 0.5;
    out->blue = 1000 * b + 0.5;
    return;
}


inline void vdi1000_to_rgb(unsigned short * in, unsigned char * out)
{
    double r = ((double)in[0]/1000); /* prozentsatz red   */
    double g = ((double)in[1]/1000); /* prozentsatz green */
    double b = ((double)in[2]/1000); /* prozentsatz blue  */
    out[2] = 255 * r + 0.5;
    out[1] = 255 * g + 0.5;
    out[0] = 255 * b + 0.5;
    return;
}


#ifdef WITH_8BPP_SUPPORT
/**
 * Set pixel within an 8 bit VDI standard bitmap.
 */
inline static void
set_stdpx( MFDB * dst, int wdplanesz, int x, int y, unsigned char val )
{
    short * buf;
    short whichbit = (1<<(15-(x%16)));

    buf = dst->fd_addr;
    buf += ((dst->fd_wdwidth*(y))+(x>>4));

    *buf = (val&1) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));

    buf += wdplanesz;
    *buf = (val&(1<<1)) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));

    buf += wdplanesz;
    *buf = (val&(1<<2)) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));

    buf += wdplanesz;
    *buf = (val&(1<<3)) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));

    buf += wdplanesz;
    *buf = (val&(1<<4)) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));

    buf += wdplanesz;
    *buf = (val&(1<<5)) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));

    buf += wdplanesz;
    *buf = (val&(1<<6)) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));

    buf += wdplanesz;
    *buf = (val&(1<<7)) ? ((*buf)|(whichbit)) : ((*buf)&~(whichbit));
}


/**
 * Read pixel from an 8 bit VDI standard bitmap.
 */
inline static unsigned char get_stdpx(MFDB * dst, int wdplanesz, int x, int y)
{
    unsigned char ret=0;
    short * buf;
    short whichbit = (1<<(15-(x%16)));

    buf = dst->fd_addr;
    buf += ((dst->fd_wdwidth*(y))+(x>>4));

    if( *buf & whichbit )
	ret |= 1;

    buf += wdplanesz;
    if( *buf & whichbit )
	ret |= 2;

    buf += wdplanesz;
    if( *buf & whichbit )
	ret |= 4;

    buf += wdplanesz;
    if( *buf & whichbit )
	ret |= 8;

    buf += wdplanesz;
    if( *buf & whichbit )
	ret |= 16;

    buf += wdplanesz;
    if( *buf & whichbit )
	ret |= 32;

    buf += wdplanesz;
    if( *buf & whichbit )
	ret |= 64;

    buf += wdplanesz;
    if( *buf & whichbit )
	ret |= 128;

    return( ret );
}

/**
 * Convert an RGB color into an index into the 216 colors web pallette
 */
inline short rgb_to_666_index(unsigned char r, unsigned char g, unsigned char b)
{
    short i;
    unsigned char rgb[3] = {r,g,b};
    unsigned char tval[3];

    int diff_a, diff_b, diff_c;
    diff_a = abs(r-g);
    diff_b = abs(r-b);
    diff_c = abs(r-b);
    if( diff_a < 2 && diff_b < 2 && diff_c < 2 ) {
	if( (r!=0XFF) && (g!=0XFF) && (b!=0XFF)  ) {
	    if( ((r&0xF0)>>4) != 0 )
		//printf("conv gray: %x -> %d\n", ((r&0xF0)>>4) , (OFFSET_CUST_PAL) + ((r&0xF0)>>4) );
		return( (OFFSET_CUST_PAL - OFFSET_WEB_PAL) + ((r&0xF0)>>4) );
	}
    }

    /* convert each 8bit color to 6bit web color: */
    for( i=0; i<3; i++) {
	if(0 == rgb[i] % web_std_colors[1] ) {
	    tval[i] = rgb[i] / web_std_colors[1];
	} else {
	    int pos = ((short)rgb[i] / web_std_colors[1]);
	    if( abs(rgb[i] - web_std_colors[pos]) > abs(rgb[i] - web_std_colors[pos+1]) )
		tval[i] = pos+1;
	    else
		tval[i] = pos;
	}
    }
    return(tval[2]*36+tval[1]*6+tval[0]);
}
#endif


static void dump_vdi_info(short vdih)
{
    struct s_vdi_sysinfo temp;
    read_vdi_sysinfo( vdih, &temp );
    printf("struct s_vdi_sysinfo {\n");
    printf("        short vdi_handle: %d\n", temp.vdi_handle);
    printf("        short scr_w: %d \n", temp.scr_w);
    printf("        short scr_h: %d\n", temp.scr_h);
    printf("        short scr_bpp: %d\n", temp.scr_bpp);
    printf("        int colors: %d\n", temp.colors);
    printf("        ulong hicolors: %lu\n", temp.hicolors);
    printf("        short pixelsize: %d\n", temp.pixelsize);
    printf("        unsigned short pitch: %d\n", temp.pitch);
    printf("        unsigned short vdiformat: %d\n", temp.vdiformat);
    printf("        unsigned short clut: %d\n", temp.clut);
    printf("        void * screen: 0x0%p\n", temp.screen);
    printf("        unsigned long  screensize: %lu\n", temp.screensize);
    printf("        unsigned long  mask_r: 0x0%08lx\n", temp.mask_r);
    printf("        unsigned long  mask_g: 0x0%08lx\n", temp.mask_g);
    printf("        unsigned long  mask_b: 0x0%08lx\n", temp.mask_b);
    printf("        unsigned long  mask_a: 0x0%08lx\n", temp.mask_a);
    printf("        short maxintin: %d\n", temp.maxintin);
    printf("        short maxpolycoords: %d\n", temp.maxpolycoords);
    printf("        unsigned long EdDiVersion: 0x0%03lx\n", temp.EdDiVersion);
    printf("        unsigned short rasterscale: 0x%2x\n", temp.rasterscale);
    printf("};\n");
}


/**
 * Create an snapshot of the screen image in device format.
 */
static MFDB * snapshot_create_native_mfdb(int x, int y, int w, int h)
{
    MFDB scr;
    short pxy[8];

    /* allocate memory for the snapshot */
    {
	int scr_stride = MFDB_STRIDE( w );
	int scr_size = ( ((scr_stride >> 3) * h) * vdi_sysinfo.scr_bpp );
	if(size_buf_scr == 0 ){
	    /* init screen mfdb */
	    buf_scr.fd_addr = malloc( scr_size );
	    size_buf_scr = scr_size;
	} else {
	    if( scr_size >size_buf_scr ) {
		buf_scr.fd_addr = realloc(
		    buf_scr.fd_addr, scr_size
		    );
		size_buf_scr = scr_size;
	    }
	}
	if(buf_scr.fd_addr == NULL ) {
	    size_buf_scr = 0;
	    return( NULL );
	}
	buf_scr.fd_nplanes = vdi_sysinfo.scr_bpp;
	buf_scr.fd_w = scr_stride;
	buf_scr.fd_h = h;
	buf_scr.fd_wdwidth = scr_stride >> 4;
	assert(buf_scr.fd_addr != NULL );
    }
    init_mfdb( 0, w, h, 0, &scr );
    pxy[0] = x;
    pxy[1] = y;
    pxy[2] = pxy[0] + w-1;
    pxy[3] = pxy[1] + h-1;
    pxy[4] = 0;
    pxy[5] = 0;
    pxy[6] = w-1;
    pxy[7] = h-1;
    vro_cpyfm(
	atari_plot_vdi_handle, S_ONLY, (short*)&pxy,
	&scr,  &buf_scr
	);

    return( &buf_scr );
}


/**
 * Create an snapshot of the screen in netsurf ABGR format
 *
 * This creates an snapshot in RGBA format (NetSurf's native format)
 *
 * Capture the screen at x,y location
 *
 * \param x absolute screen coords
 * \param y absolute screen coords
 * \param w width
 * \param h height
 */
static struct bitmap *snapshot_create(int x, int y, int w, int h)
{
    int err;
    MFDB * native;
    // uint32_t start = clock();

    // FIXME:   This can be optimized a lot.
    //          1. do not copy the snapshot to the bitmap buffer
    //              when the format of screen and bitmap equals.
    //              just point the bitmap to the native mfdb.
    //          2. if we have eddi 1.1, we could optimize that further
    //              make snapshot_create_native_mfdb just returning a pointer
    //              to the screen.

    native = snapshot_create_native_mfdb(x, y, w, h );

    if(vfmt.bits == 32 )
	goto no_copy;

    /* allocate buffer for result bitmap: */
    if(buf_scr_compat == NULL ) {
	buf_scr_compat = atari_bitmap_create(w, h, 0);
    } else {
	buf_scr_compat = atari_bitmap_realloc( w, h,
					       buf_scr_compat->bpp,
					       w *buf_scr_compat->bpp,
					       BITMAP_GROW,
					       buf_scr_compat );
    }

    /* convert screen buffer to ns format: */
    err = Hermes_ConverterRequest( hermes_cnv_h,
				   &vfmt,
				   &nsfmt
	);
    assert( err != 0 );
    err = Hermes_ConverterCopy( hermes_cnv_h,
				native->fd_addr,
				0, /* x src coord of top left in pixel coords */
				0, /* y src coord of top left in pixel coords */
				w, h,
				native->fd_w * vdi_sysinfo.pixelsize, /* stride as bytes */
				buf_scr_compat->pixdata,
				0, /* x dst coord of top left in pixel coords */
				0, /* y dst coord of top left in pixel coords */
				w, h,
				atari_bitmap_get_rowstride(buf_scr_compat) /* stride as bytes */
	);
    assert( err != 0 );
    return( (struct bitmap * )buf_scr_compat );

no_copy:

    snapshot.width = w;
    snapshot.height = h;
    snapshot.pixdata = native->fd_addr;
    snapshot.native = *native;
    snapshot.rowstride = MFDB_STRIDE( w )*4;

    uint32_t row, col;
    for (row = 0; row<(uint32_t)h; row++) {
	// fd_w matches stride!
	uint32_t *rowptr = ((uint32_t*)native->fd_addr + ((row*native->fd_w)));
	for (col=0; col<(uint32_t)w; col++) {
	    *(rowptr+col) = (*(rowptr+col)<<8);
	}
    }
    return( &snapshot );
}


/**
 * Notify the snapshot interface that the last snapshot is no longer in use.
 */
static void snapshot_suspend(void)
{
    if(size_buf_scr > CONV_KEEP_LIMIT  ) {
	buf_scr.fd_addr = realloc(
	    buf_scr.fd_addr, CONV_KEEP_LIMIT
	    );
	if(buf_scr.fd_addr != NULL ) {
	    size_buf_scr = CONV_KEEP_LIMIT;
	} else {
	    size_buf_scr = 0;
	}
    }

#ifdef WITH_8BPP_SUPPORT
    if(size_buf_std > CONV_KEEP_LIMIT  ) {
	buf_std.fd_addr = realloc(
	    buf_std.fd_addr, CONV_KEEP_LIMIT
	    );
	if(buf_std.fd_addr != NULL ) {
	    size_buf_std = CONV_KEEP_LIMIT;
	} else {
	    size_buf_std = 0;
	}
    }
#endif

    if(buf_scr_compat != NULL ) {
	size_t bs = atari_bitmap_buffer_size(buf_scr_compat );
	if( bs > CONV_KEEP_LIMIT ) {
	    int w = 0;
	    int h = 1;
	    w = (CONV_KEEP_LIMIT /buf_scr_compat->bpp);
	    assert( CONV_KEEP_LIMIT == w*buf_scr_compat->bpp );
	    buf_scr_compat = atari_bitmap_realloc( w, h,
						   buf_scr_compat->bpp,
						   CONV_KEEP_LIMIT, BITMAP_SHRINK,buf_scr_compat
		);
	}
    }
}


/**
 * Shut down the snapshot interface.
 */
static void snapshot_destroy(void)
{

    free(buf_scr.fd_addr);
    if( buf_scr_compat != NULL) {
	atari_bitmap_destroy(buf_scr_compat);
    }

    buf_scr.fd_addr = NULL;
    buf_scr_compat = NULL;

#ifdef WITH_8BPP_SUPPORT
    free(buf_std.fd_addr);
    buf_std.fd_addr = NULL;
#endif
}


inline static uint32_t ablend(uint32_t pixel, uint32_t scrpixel)
{
    int opacity = pixel & 0xFF;
    int transp = 0x100 - opacity;
    uint32_t rb, g;
    pixel >>= 8;
    scrpixel >>= 8;
    rb = ((pixel & 0xFF00FF) * opacity +
	  (scrpixel & 0xFF00FF) * transp) >> 8;
    g  = ((pixel & 0x00FF00) * opacity +
	  (scrpixel & 0x00FF00) * transp) >> 8;

    return ((rb & 0xFF00FF) | (g & 0xFF00)) << 8;
}


/**
 * Alpha blends an image, using one pixel as the background.
 *
 * The bitmap receives the result.
 */
inline static bool ablend_pixel(struct bitmap * img, uint32_t bg, GRECT * clip)
{
    uint32_t * imgrow;
    int img_x, img_y, img_stride;

    img_stride= atari_bitmap_get_rowstride(img);

    for( img_y = 0; img_y < clip->g_h; img_y++) {
	imgrow = (uint32_t *)(img->pixdata + (img_stride * img_y));
	for( img_x = 0; img_x < clip->g_w; img_x++ ) {
	    imgrow[img_x] = ablend( imgrow[img_x], bg );
	}
    }
    return(true);
}


/**
 * Aplha blends the foreground image onto thebackground images.
 *
 * The background receives the blended image pixels.
 */
inline static bool
ablend_bitmap(struct bitmap *img,
	      struct bitmap *bg,
	      GRECT *img_clip,
	      GRECT * bg_clip )
{
    uint32_t * imgrow;
    uint32_t * screenrow;
    int img_x, img_y, bg_x, bg_y, img_stride, bg_stride;

    bg_clip = bg_clip;
    img_stride = atari_bitmap_get_rowstride(img);
    bg_stride = atari_bitmap_get_rowstride(bg);

    for( img_y = img_clip->g_y, bg_y = 0; bg_y < img_clip->g_h; bg_y++, img_y++) {
	imgrow = (uint32_t *)(img->pixdata + (img_stride * img_y));
	screenrow = (uint32_t *)(bg->pixdata + (bg_stride * bg_y));
	for( img_x = img_clip->g_x, bg_x = 0; bg_x < img_clip->g_w; bg_x++, img_x++ ) {

	    // when the pixel isn't fully transparent,...:
	    if( (imgrow[img_x] & 0x0FF) != 0 ){
		screenrow[bg_x] = ablend( imgrow[img_x], screenrow[bg_x]);
	    }

	    // FIXME, maybe this loop would be faster??:
	    // ---
	    //if( (imgrow[img_x] & 0x0FF) != 0xFF ){
	    //  imgrow[bg_x] = ablend( imgrow[img_x], screenrow[bg_x]);
	    //}

	    // or maybe even this???
	    // ---
	    //if(  (imgrow[img_x] & 0x0FF) == 0xFF ){
	    //  screenrow[bg_x] = imgrow[img_x];
	    //} else if( (imgrow[img_x] & 0x0FF) != 0x00 ) {
	    //  screenrow[bg_x] = ablend( imgrow[img_x], screenrow[bg_x]);
	    //}
	}
    }
    return(false);
}


#ifdef WITH_8BPP_SUPPORT

/**
 * Create an snapshot of the screen image in VDI standard format (8 bit).
 */
static MFDB * snapshot_create_std_mfdb(int x, int y, int w, int h)
{
    /* allocate memory for the snapshot */
    {
	int scr_stride = MFDB_STRIDE( w );
	int scr_size = ( ((scr_stride >> 3) * h) * vdi_sysinfo.scr_bpp );
	if(size_buf_std == 0 ){
	    /* init screen mfdb */
	    buf_std.fd_addr = malloc( scr_size );
	    size_buf_std = scr_size;
	} else {
	    if( scr_size >size_buf_std ) {
		buf_std.fd_addr = realloc(
		    buf_std.fd_addr, scr_size
		    );
		size_buf_std = scr_size;
	    }
	}
	if(buf_std.fd_addr == NULL ) {
	    size_buf_std = 0;
	    return( NULL );
	}
	buf_std.fd_nplanes = 8;
	buf_std.fd_w = scr_stride;
	buf_std.fd_h = h;
	buf_std.fd_stand = 1;
	buf_std.fd_wdwidth = scr_stride >> 4;
	assert(buf_std.fd_addr != NULL );
    }
    MFDB * native = snapshot_create_native_mfdb(x,y,w,h );
    assert( native );

    vr_trnfm(atari_plot_vdi_handle, native, &buf_std);
    return( &buf_std );
}


/**
 * Convert an bitmap to an 8 bit device dependant MFDB
 * \param img the bitmap (only tested with 32bit bitmaps)
 * \param x screen coord of the background
 * \param y screen coord of the background
 * \param clip the region of the image that get's converted
 * \param bg the background used for cheap transparency
 * \param flags
 * \param out receives the converted bitmap (still owned by the plot API)
 *
 */
static bool
bitmap_convert_8(struct bitmap *img,
		 int x,
		 int y,
		 GRECT *clip,
		 uint32_t bg,
		 uint32_t flags,
		 MFDB *out)
{
    MFDB native;
    MFDB stdform;
    int dststride;                      /* stride of dest. image */
    int dstsize;                        /* size of dest. in byte */
    int bw, bh;
    struct bitmap * scrbuf = NULL;
    bool cache =  ( flags & BITMAPF_BUFFER_NATIVE );
    bool opaque = atari_bitmap_get_opaque( img );

    if( opaque == false ){
	if( ( (atari_plot_flags & PLOT_FLAG_TRANS) == 0)
	    &&
	    ((flags & (BITMAPF_MONOGLYPH|BITMAPF_BUFFER_NATIVE))==0) ){
	    opaque = true;
	}
    }

    assert( clip->g_h > 0 );
    assert( clip->g_w > 0 );

    bw = atari_bitmap_get_width( img );
    bh = atari_bitmap_get_height( img );

    // The converted bitmap can be saved for subsequent blits, when
    // the bitmap is fully opaque

    if( (opaque == true) || (flags & BITMAPF_BUFFER_NATIVE ) ){
	if( img->converted == true ){
	    *out = img->native;
	    return( 0 );
	}
	if( ( flags & BITMAPF_MONOGLYPH ) == 0 ){
	    cache = true;
	}
    }
    if( ( flags & BITMAPF_MONOGLYPH ) != 0 ){
	assert(cache == false);
    }

    /* (re)allocate buffer for out image: */
    /* altough the buffer is named "buf_packed" on 8bit systems */
    /* it's not... */
    if( cache == false ){
	// the size of the output will match the size of the clipping:
	dststride = MFDB_STRIDE( clip->g_w );
	dstsize = ( ((dststride >> 3) * clip->g_h) * atari_plot_bpp_virt);
	if (dstsize > size_buf_packed) {
	    int blocks = (dstsize / (CONV_BLOCK_SIZE-1))+1;
	    void *buf;
	    if (buf_packed == NULL) {
		buf = malloc( blocks * CONV_BLOCK_SIZE);
	    } else {
		buf = realloc(buf_packed, blocks * CONV_BLOCK_SIZE);
	    }
	    if (buf == NULL) {
		return( 0-ERR_NO_MEM );
	    }
	    buf_packed = buf;
	    size_buf_packed = blocks * CONV_BLOCK_SIZE;
	}
	native.fd_addr = buf_packed;
    }
    else {
	// the output image will be completly saved, so size of the output
	// image will match the input image size.
	dststride = MFDB_STRIDE( bw );
	dstsize = ( ((dststride >> 3) * bh) * atari_plot_bpp_virt);
	assert( out->fd_addr == NULL );
	native.fd_addr = malloc( dstsize );
	if (native.fd_addr == NULL){
	    if (scrbuf != NULL)
		atari_bitmap_destroy(scrbuf);
	    return( 0-ERR_NO_MEM );
	}
    }


    /*
      on 8 bit systems we must convert the TC (ABGR) image
      to vdi standard format. ( only tested for 256 colors )
      and then convert it to native format with v_trnfm()
    */
    // realloc mem for stdform
    if( opaque == false ){
	// point image to snapshot buffer, otherwise allocate mem
	MFDB * bg = snapshot_create_std_mfdb(x, y, clip->g_w, clip->g_h);
	stdform.fd_addr = bg->fd_addr;
	bh = clip->g_h;
    } else {
	if (dstsize > size_buf_planar) {
	    int blocks = (dstsize / (CONV_BLOCK_SIZE-1))+1;
	    void *buf;
	    if (buf_planar == NULL) {
		buf = malloc(blocks * CONV_BLOCK_SIZE);
	    } else {
		buf = realloc(buf_planar, blocks * CONV_BLOCK_SIZE);
	    }
	    if (buf == NULL ) {
		if (cache) {
		    free(native.fd_addr);
		}
		return( 0-ERR_NO_MEM );
	    }
	    buf_planar = buf;
	    size_buf_planar = blocks * CONV_BLOCK_SIZE;
	}
	stdform.fd_addr = buf_planar;
    }
    stdform.fd_w = dststride;
    stdform.fd_h = bh;
    stdform.fd_wdwidth = dststride >> 4;
    stdform.fd_stand = 1;
    stdform.fd_nplanes = (short)atari_plot_bpp_virt;
    stdform.fd_r1 = stdform.fd_r2 = stdform.fd_r3 = 0;

    int img_stride = atari_bitmap_get_rowstride(img);
    uint32_t prev_pixel = 0x12345678; //TODO: check for collision in first pixel
    unsigned long col = 0;
    unsigned char val = 0;
    uint32_t * row;
    uint32_t pixel;
    int wdplanesize = stdform.fd_wdwidth*stdform.fd_h;

    if( opaque == false ){
	// apply transparency and convert to vdi std format
	unsigned long bgcol = 0;
	unsigned char prev_col = 0;
	for( y=0; y<clip->g_h; y++ ){
	    row = (uint32_t *)(img->pixdata + (img_stride * (y+clip->g_y)));
	    for( x=0; x<clip->g_w; x++ ){
		pixel = row[x+clip->g_x];
		if( (pixel&0xFF) == 0 ){
		    continue;
		}
		if( (pixel&0xFF) < 0xF0 ){
		    col = get_stdpx( &stdform, wdplanesize,x,y );
		    if( (col != prev_col) || (y == 0) )
			bgcol = (((rgb_lookup[col][2] << 16) | (rgb_lookup[col][1] << 8) | (rgb_lookup[col][0]))<<8);
		    if( prev_col != col || prev_pixel != pixel ){
			prev_col = col;
			pixel = ablend( pixel, bgcol );
			prev_pixel = pixel;
			pixel = pixel >> 8;
			/* convert pixel value to vdi color index: */
			col = ( ((pixel&0xFF)<<16)
				| (pixel&0xFF00)
				| ((pixel&0xFF0000)>>16) );
			val = RGB_TO_VDI( col );
		    }
		    set_stdpx( &stdform, wdplanesize, x, y, val );
		} else {
		    if( pixel != prev_pixel ){
			/* convert pixel value to vdi color index: */
			pixel = pixel >> 8;
			col = ( ((pixel&0xFF)<<16)
				| (pixel&0xFF00)
				| ((pixel&0xFF0000)>>16) );
			val = RGB_TO_VDI( col );
			prev_pixel = pixel;
		    }
		    set_stdpx( &stdform, wdplanesize, x, y, val );
		}
	    }
	}
	// adjust output position:
	clip->g_x = 0;
	clip->g_y = 0;
    } else {
	// convert the whole image data to vdi std format.
	for( y=0; y < bh; y++ ){
	    row = (uint32_t *)(img->pixdata + (img_stride * y));
	    for( x=0; x < bw; x++ ){
		pixel = row[x];
		if( pixel != prev_pixel ){
		    /* convert pixel value to vdi color index: */
		    pixel = pixel >> 8;
		    col = ( ((pixel&0xFF)<<16)
			    | (pixel&0xFF00)
			    | ((pixel&0xFF0000)>>16) );
		    val = RGB_TO_VDI( col );
		    prev_pixel = pixel;
		}
		set_stdpx( &stdform, wdplanesize, x, y, val );
	    }
	}
    }

    // convert into native format:
    native.fd_w = stdform.fd_w;
    native.fd_h = stdform.fd_h;
    native.fd_wdwidth = stdform.fd_wdwidth;
    native.fd_stand = 0;
    native.fd_nplanes = (short)atari_plot_bpp_virt;
    native.fd_r1 = native.fd_r2 = native.fd_r3 = 0;
    vr_trnfm(atari_plot_vdi_handle, &stdform, &native );
    *out = native;
    if( cache == true ){
	img->native = native;
	img->converted = true;
    }

    return(0);
}
#endif


/**
 * Convert bitmap to the native screen format
 *
 * \param img the bitmap
 * \param x coordinate where the bitmap REGION (described in clip)
 *           shall be drawn (screen coords)
 * \param y coordinate where the bitmap REGION (described in clip)
 *           shall be drawn (screen coords)
 * \param clip which area of the bitmap shall be drawn
 * \param bg background color
 * \param flags blit flags
 * \param out the result MFDB
 */
static bool
bitmap_convert_tc(struct bitmap *img,
		  int x,
		  int y,
		  GRECT *clip,
		  uint32_t bg,
		  uint32_t flags,
		  MFDB *out)
{
    int dststride; /* stride of dest. image */
    int dstsize; /* size of dest. in byte */
    int err;
    int bw, bh;
    struct bitmap * scrbuf = NULL;
    struct bitmap * source = NULL;
    bool cache =  ( flags & BITMAPF_BUFFER_NATIVE );
    bool opaque = atari_bitmap_get_opaque( img );

    if (opaque == false ) {
	if( ( (atari_plot_flags & PLOT_FLAG_TRANS) == 0)
	    &&
	    ((flags & (BITMAPF_MONOGLYPH|BITMAPF_BUFFER_NATIVE))==0) ){
	    opaque = true;
	}
    }

    assert( clip->g_h > 0 );
    assert( clip->g_w > 0 );

    bw = atari_bitmap_get_width( img );
    bh = atari_bitmap_get_height( img );

    // The converted bitmap can be saved for subsequent blits, WHEN:
    // A.) the bitmap is fully opaque OR
    // B.) the bitmap is completly inside the window
    // the latter one is important for alpha blits,
    // because we must get the window background to apply transparency
    // If the image is not completly within the window,
    // we can't get the whole background for the image.
    // this only works if the image isn't used at several different places.
    // In fact in case of alpha bitmap caching it is only used for the
    // toolbar buttons right now.

    if( (opaque == true) || (flags & BITMAPF_BUFFER_NATIVE ) ){
	if( img->converted == true ){
	    *out = img->native;
	    return( 0 );
	}
	if( ( flags & BITMAPF_MONOGLYPH ) == 0 ){
	    cache = true;
	}
    }

    /* rem. if eddi xy is installed, we could directly access the screen! */
    /* apply transparency to the image: */
    if (( opaque == false )) {
	/* copy the screen to an temp buffer: */
	if ((flags & BITMAPF_BUFFER_NATIVE) == 0) {
	    scrbuf = snapshot_create(x, y, clip->g_w, clip->g_h);
	    if( scrbuf != NULL ) {

		assert( clip->g_w <= bw );
		assert( clip->g_h <= bh );

		// copy blended pixels to the screen buffer:
		ablend_bitmap( img, scrbuf, clip, NULL );
		/* adjust size which gets converted: */
		bw = clip->g_w;
		bh = clip->g_h;
		/* adjust output position: */
		clip->g_x = 0;
		clip->g_y = 0;
		/* set the source of conversion: */
		source = scrbuf;
	    }
	} else {
	    /*
	      The whole bitmap can be transformed to an mfdb
	      (and get's cached)
	    */
	    GRECT region = { 0, 0, bw, bh };
	    ablend_pixel( img, bg, &region );
	    source = img;
	}
    } else {
	source = img;
    }
    /* (re)allocate buffer for converted image: */
    dststride = MFDB_STRIDE(bw);
    dstsize = ( ((dststride >> 3) * bh) * atari_plot_bpp_virt );
    if (cache == false) {
	/* ensure cache buffer is large enough */
	if (dstsize > size_buf_packed) {
	    int blocks = (dstsize / (CONV_BLOCK_SIZE-1))+1;
	    void *buf;
	    if (buf_packed == NULL) {
		buf = malloc(blocks * CONV_BLOCK_SIZE);
	    } else {
		buf = realloc(buf_packed, blocks * CONV_BLOCK_SIZE);
	    }
	    if (buf == NULL ) {
		if (scrbuf != NULL) {
		    atari_bitmap_destroy(scrbuf);
		}
		return( 0-ERR_NO_MEM );
	    }
	    buf_packed = buf;
	    size_buf_packed = blocks * CONV_BLOCK_SIZE;
	}
	out->fd_addr = buf_packed;
    } else {
	assert( out->fd_addr == NULL );
	out->fd_addr = (void*)malloc( dstsize );
	if( out->fd_addr == NULL ){
	    if( scrbuf != NULL )
		atari_bitmap_destroy( scrbuf );
	    return( 0-ERR_NO_MEM );
	}
    }

    out->fd_w = dststride;
    out->fd_h = bh;
    out->fd_wdwidth = dststride >> 4;
    out->fd_stand = 0;
    out->fd_nplanes = (short)atari_plot_bpp_virt;
    out->fd_r1 = out->fd_r2 = out->fd_r3 = 0;

    err = Hermes_ConverterRequest(
	hermes_cnv_h,
	&nsfmt,
	&vfmt
	);
    assert( err != 0 );

    // FIXME: here we can use the same optimization which is used for
    // the snapshot creation.

    /* convert image to virtual format: */
    err = Hermes_ConverterCopy( hermes_cnv_h,
				source->pixdata,
				0, /* x src coord of top left in pixel coords */
				0, /* y src coord of top left in pixel coords */
				bw, bh,
				source->rowstride,  /* stride as bytes */
				out->fd_addr,
				0, /* x dst coord of top left in pixel coords */
				0,/* y dst coord of top left in pixel coords */
				bw, bh,
				(dststride >> 3) *  atari_plot_bpp_virt             /* stride as bytes */
	);
    assert( err != 0 );

    if( cache == true ){
	img->native = *out;
	img->converted = true;
    }
    return( 0 );

}


inline static void convert_bitmap_done(void)
{
    if (size_buf_packed > CONV_KEEP_LIMIT) {
	void *buf;
	/* free the mem if it was an large allocation ... */
	buf = realloc(buf_packed, CONV_KEEP_LIMIT);
	if (buf != NULL) {
	    buf_packed = buf;
	    size_buf_packed = CONV_KEEP_LIMIT;
	}
    }
}


bool plot_blit_bitmap(struct bitmap * bmp, int x, int y,
		      unsigned long bg, unsigned long flags )
{
    MFDB src_mf;
    MFDB scrmf;
    short pxy[8];
    GRECT off, clip, vis;
    int screen_x, screen_y;

    src_mf.fd_addr = NULL;
    scrmf.fd_addr = NULL;

    off.g_x = x;
    off.g_y = y;
    off.g_h = bmp->height;
    off.g_w = bmp->width;

    // clip plotter clip rectangle:
    clip.g_x = view.clipping.x0;
    clip.g_y = view.clipping.y0;
    clip.g_w = view.clipping.x1 - view.clipping.x0;
    clip.g_h = view.clipping.y1 - view.clipping.y0;

    if( !rc_intersect( &clip, &off) ) {
	return(true);
    }

    // clip the visible rectangle of the plot area
    // this is the area of the plotter which falls into
    // screen region:
    plot_get_visible_grect(&vis);
    if( !rc_intersect( &vis, &off) ) {
	return(true);
    }

    screen_x = view.x + off.g_x;
    screen_y = view.y + off.g_y;

    // convert the clipping relative to bitmap:
    off.g_x = off.g_x - x;
    off.g_y = off.g_y - y;
    assert( (off.g_x >= 0) && (off.g_y >= 0) );

    /* Convert the Bitmap to native screen format - ready for output.   */
    /* This includes blending transparent pixels:                       */
    if (bitmap_convert(bmp, screen_x, screen_y, &off, bg, flags, &src_mf)
	!= 0 ) {
	return(true);
    }

    // setup the src region:
    pxy[0] = off.g_x;
    pxy[1] = off.g_y;
    pxy[2] = off.g_x + off.g_w-1;
    pxy[3] = off.g_y + off.g_h-1;

    // setup the target region:
    pxy[4] = screen_x;
    pxy[5] = screen_y;
    pxy[6] = screen_x + off.g_w-1;
    pxy[7] = screen_y + off.g_h-1;

    vro_cpyfm(atari_plot_vdi_handle, S_ONLY, (short*)&pxy, &src_mf,  &scrmf);
    convert_bitmap_done();
    snapshot_suspend();
    return(true);
}


bool plot_blit_mfdb(GRECT * loc, MFDB * insrc, short fgcolor,
		    uint32_t flags)
{
    MFDB screen;
//  MFDB tran;
    MFDB * src;
    short pxy[8];
    short c[2] = {fgcolor, 0};
    GRECT off;

    plot_get_clip_grect(&off);
    if( rc_intersect(loc, &off) == 0 ){
	return( 1 );
    }

    init_mfdb( 0, loc->g_w, loc->g_h, 0, &screen );
//
//  if( insrc->fd_stand){
//      printf("st\n");
//      int size = init_mfdb( insrc->fd_nplanes, loc->g_w, loc->g_h,
//          MFDB_FLAG_NOALLOC,
//          &tran
//      );
//      if( size_buf_scr == 0 ){
//          buf_scr.fd_addr = malloc( size );
//          size_buf_scr = size;
//      } else {
//          if( size > size_buf_scr ) {
//              buf_scr.fd_addr = realloc(
//                  buf_scr.fd_addr, size
//              );
//              size_buf_scr = size;
//          }
//      }
//      tran.fd_addr = buf_scr.fd_addr;
//      vr_trnfm(atari_plot_vdi_handle, insrc, &tran );
//      src = &tran;
//  } else {
    src = insrc;
//  }

    pxy[0] = off.g_x - loc->g_x;
    pxy[1] = off.g_y - loc->g_y;
    pxy[2] = pxy[0] + off.g_w - 1;
    pxy[3] = pxy[1] + off.g_h - 1;
    pxy[4] = view.x + off.g_x;
    pxy[5] = view.y + off.g_y;
    pxy[6] = pxy[4] + off.g_w-1;
    pxy[7] = pxy[5] + off.g_h-1;


    if( flags & PLOT_FLAG_TRANS && src->fd_nplanes == 1){
	vrt_cpyfm(atari_plot_vdi_handle, MD_REPLACE/*MD_TRANS*/, (short*)pxy, src, &screen, (short*)&c);
    } else {
	/* this method only plots transparent bitmaps, right now... */
    }
    return( 1 );
}


/* exported interface documented in atari/plot.h */
int plot_init(const struct redraw_context *ctx, char *fdrvrname)
{
    GRECT loc_pos = { 0, 0, 360, 400 };
    int err=0;

    if( nsoption_int(atari_dither) == 1)
	atari_plot_flags |= PLOT_FLAG_DITHER;
    if( nsoption_int(atari_transparency) == 1 )
	atari_plot_flags |= PLOT_FLAG_TRANS;
    if( nsoption_int(atari_font_monochrom) == 1 )
	atari_font_flags |= FONTPLOT_FLAG_MONOGLYPH;

    if (atari_plot_vdi_handle == -1) {

	short dummy;
	short work_in[12] = {Getrez()+2,1,1,1,1,1,1,1,1,1,2,1};
	short work_out[57];
	atari_plot_vdi_handle=graf_handle(&dummy, &dummy, &dummy, &dummy);
	v_opnvwk(work_in, &atari_plot_vdi_handle, work_out);
	NSLOG(netsurf, INFO, "Plot VDI handle: %d", atari_plot_vdi_handle);
    }
    read_vdi_sysinfo(atari_plot_vdi_handle, &vdi_sysinfo);
    if(verbose_log) {
	dump_vdi_info(atari_plot_vdi_handle) ;
	dump_font_drivers();
    }

    fplotter = new_font_plotter(atari_plot_vdi_handle, fdrvrname,
				atari_font_flags, &err);
    if (err) {
	const char * desc = plot_err_str(err);
	NSLOG(netsurf, INFO, "Unable to load font plotter %s -> %s",
              fdrvrname, desc);
	die("font plotter");
    }

    memset(&view, 0, sizeof(struct s_view));
    atari_plot_bpp_virt = vdi_sysinfo.scr_bpp;
    view.x = loc_pos.g_x;
    view.y = loc_pos.g_y;
    view.w = loc_pos.g_w;
    view.h = loc_pos.g_h;
    size_buf_packed = 0;
    size_buf_planar = 0;
    buf_packed = NULL;
    buf_planar = NULL;
    if( vdi_sysinfo.vdiformat == VDI_FORMAT_PACK  ) {
	atari_plot_bpp_virt = vdi_sysinfo.scr_bpp;
    } else {
	atari_plot_bpp_virt = 8;
    }

    plot_set_scale(1.0);
    update_visible_rect();

    struct rect clip;
    clip.x0 = 0;
    clip.y0 = 0;
    clip.x1 = view.w;
    clip.y1 = view.h;
    ctx->plot->clip(ctx, &clip);

    assert(Hermes_Init());

#ifdef WITH_8BPP_SUPPORT
    bitmap_convert = (vdi_sysinfo.scr_bpp > 8) ? bitmap_convert_tc : bitmap_convert_8;

    /* Setup color lookup tables and palette */
    unsigned char rgbcol[4];
    if( vdi_sysinfo.scr_bpp <= 8 ){
	unsigned char graytone=0;
	int i;
	for( i=0; i<=255; i++ ) {

	    // get the current color and save it for restore:
	    vq_color(atari_plot_vdi_handle, i, 1, (unsigned short*)&sys_pal[i][0] );
	    if( i<OFFSET_WEB_PAL ) {
		pal[i][0] = sys_pal[i][0];
		pal[i][1] = sys_pal[i][1];
		pal[i][2] = sys_pal[i][2];
	    } else if( vdi_sysinfo.scr_bpp >= 8 ) {
		if ( i < OFFSET_CUST_PAL ){
		    pal[i][0] = vdi_web_pal[i-OFFSET_WEB_PAL][0];
		    pal[i][1] = vdi_web_pal[i-OFFSET_WEB_PAL][1];
		    pal[i][2] = vdi_web_pal[i-OFFSET_WEB_PAL][2];
		    //set the new palette color to websafe value:
		    vs_color(atari_plot_vdi_handle, i, &pal[i][0]);
		}
		if( i >= OFFSET_CUST_PAL && i<OFFSET_CUST_PAL+16 ) {
		    /* here we define 20 additional gray colors... */
		    rgbcol[1] = rgbcol[2] = rgbcol[3] = ((graytone&0x0F) << 4);
		    rgb_to_vdi1000( &rgbcol[0], &pal[i][0] );
		    vs_color(atari_plot_vdi_handle, i, &pal[i][0]);
		    graytone++;
		}

	    }
	    vdi1000_to_rgb( &pal[i][0],  &rgb_lookup[i][0] );
	}

    } else {
	/* no need to change the palette - its application specific */
    }
#else
    bitmap_convert = bitmap_convert_tc;
#endif

    /* Setup Hermes conversion handles */
    unsigned long hermesflags = (atari_plot_flags & PLOT_FLAG_DITHER) ? HERMES_CONVERT_DITHER : 0;
    hermes_cnv_h = Hermes_ConverterInstance(hermesflags);
    assert( hermes_cnv_h );
    hermes_res_h = Hermes_ConverterInstance(hermesflags);
    assert( hermes_res_h );

    /* set up the src & dst format: */
    /* netsurf uses RGBA ... */
    nsfmt.a = 0xFFUL;
    nsfmt.b = 0x0FF00UL;
    nsfmt.g = 0x0FF0000UL;
    nsfmt.r = 0x0FF000000UL;
    nsfmt.bits = 32;
    nsfmt.indexed = false;
    nsfmt.has_colorkey = false;

    vfmt.r = vdi_sysinfo.mask_r;
    vfmt.g = vdi_sysinfo.mask_g;
    vfmt.b = vdi_sysinfo.mask_b;
    vfmt.a = vdi_sysinfo.mask_a;
    vfmt.bits = atari_plot_bpp_virt;
    vfmt.indexed = (atari_plot_bpp_virt <= 8) ? 1 : 0;
    vfmt.has_colorkey = 0;

    return( err );
}


int plot_finalise( void )
{

    delete_font_plotter(fplotter);

#ifdef WITH_8BPP_SUPPORT
    if (vfmt.indexed) {
	int i;
	for (i=OFFSET_WEB_PAL; i<OFFSET_CUST_PAL+16; i++) {
	    vs_color(atari_plot_vdi_handle, i, &sys_pal[i][0]);
	}
    }
#endif

    /* close Hermes stuff: */
    Hermes_ConverterReturn(hermes_cnv_h);
    Hermes_Done();

    /* free up temporary buffers */
    free(buf_packed );
    free(buf_planar);
    snapshot_destroy();

    return 0;
}


bool plot_lock(void)
{
    if ((atari_plot_flags & PLOT_FLAG_LOCKED) != 0)
	return(true);
    if( !wind_update(BEG_UPDATE|0x100) )
	return(false);
    if( !wind_update(BEG_MCTRL|0x100) ){
	wind_update(END_UPDATE);
	return(false);
    }
    atari_plot_flags |= PLOT_FLAG_LOCKED;
    graf_mouse(M_OFF, NULL);
    return(true);
}


bool plot_unlock(void)
{
    if( (atari_plot_flags & PLOT_FLAG_LOCKED) == 0 )
	return(true);
    wind_update(END_MCTRL);
    wind_update(END_UPDATE);
    graf_mouse(M_ON, NULL);
    vs_clip_off(atari_plot_vdi_handle);
    atari_plot_flags &=  ~PLOT_FLAG_LOCKED;
    return(false);
}


/* exported interface documented in atari/plot.h */
bool
plot_set_dimensions(const struct redraw_context *ctx, int x, int y, int w, int h)
{
    bool doupdate = false;
    struct rect newclip = {0, 0, w, h};
    GRECT absclip = {x, y, w, h};

    if (!(w == view.w && h == view.h)) {
	view.w = (short)w;
	view.h = (short)h;
	doupdate = true;
    }
    if (!(x == view.x && y == view.y)) {
	view.x = (short)x;
	view.y = (short)y;
	doupdate = true;
    }
    if (doupdate==true)
	update_visible_rect();

    //dbg_rect("plot_set_dimensions", &newclip);

    plot_set_abs_clipping(&absclip);
    ctx->plot->clip(ctx, &newclip);
    return(true);
}


/**
 * Get current canvas size
 *
 * \param dst the GRECT * which receives the canvas size
 */
bool plot_get_dimensions(GRECT *dst)
{
    dst->g_x = view.x;
    dst->g_y = view.y;
    dst->g_w = view.w;
    dst->g_h = view.h;
    return(true);
}


/**
 * set scale of plotter.
 * \param scale the new scale value
 * \return the old scale value
 */

float plot_set_scale(float scale)
{
    float ret = view.scale;

    view.scale = scale;

    return(ret);
}


float plot_get_scale(void)
{
    return(view.scale);
}


/**
 * Subsequent calls to plot_clip will be clipped by the absolute clip.
 *
 * \param area the maximum clipping rectangle (absolute screen coords)
 */
void plot_set_abs_clipping(const GRECT *area)
{
    GRECT canvas;

    plot_get_dimensions(&canvas);

    if(!rc_intersect(area, &canvas)){
	view.abs_clipping.x0 = 0;
	view.abs_clipping.x1 = 0;
	view.abs_clipping.y0 = 0;
	view.abs_clipping.y1 = 0;
    } else {
	view.abs_clipping.x0 = area->g_x;
	view.abs_clipping.x1 = area->g_x + area->g_w;
	view.abs_clipping.y0 = area->g_y;
	view.abs_clipping.y1 = area->g_y + area->g_h;
    }
}


/**
 * Get the maximum clip extent, in absolute screen coords
 * \param dst the structure that receives the absolute clipping
 */
void plot_get_abs_clipping(struct rect *dst)
{
    *dst = view.abs_clipping;
}


/**
 * Get the maximum clip extent, in absolute screen coords
 * \param dst the structure that receives the absolute clipping
 */
void plot_get_abs_clipping_grect(GRECT *dst)
{
    dst->g_x = view.abs_clipping.x0;
    dst->g_w = view.abs_clipping.x1 - view.abs_clipping.x0;
    dst->g_y = view.abs_clipping.y0;
    dst->g_h = view.abs_clipping.y1 - view.abs_clipping.y0;
}


VdiHdl plot_get_vdi_handle(void)
{
    return(atari_plot_vdi_handle);
}


long plot_get_flags(void)
{
    return(atari_plot_flags);
}


bool plot_get_clip(struct rect * out)
{
    out->x0 = view.clipping.x0;
    out->y0 = view.clipping.y0;
    out->x1 = view.clipping.x1;
    out->y1 = view.clipping.y1;
    return( true );
}


void plot_get_clip_grect(GRECT * out)
{
    struct rect clip={0,0,0,0};

    plot_get_clip(&clip);

    out->g_x = clip.x0;
    out->g_y = clip.y0;
    out->g_w = clip.x1 - clip.x0;
    out->g_h = clip.y1 - clip.y0;
}


FONT_PLOTTER plot_get_text_plotter()
{
    return(fplotter);
}


void plot_set_text_plotter(FONT_PLOTTER font_plotter)
{
    fplotter = font_plotter;
}


/**
 * \brief Sets a clip rectangle for subsequent plot operations.
 *
 * \param ctx The current redraw context.
 * \param clip The rectangle to limit all subsequent plot
 *              operations within.
 * \return NSERROR_OK on success else error code.
 */
static nserror
plot_clip(const struct redraw_context *ctx, const struct rect *clip)
{
    GRECT canvas, screen, gclip, maxclip;
    short pxy[4];

    screen.g_x = 0;
    screen.g_y = 0;
    screen.g_w = vdi_sysinfo.scr_w;
    screen.g_h = vdi_sysinfo.scr_h;

    plot_get_dimensions(&canvas);

    view.clipping.y0 = clip->y0;
    view.clipping.y1 = clip->y1;
    view.clipping.x0 = clip->x0;
    view.clipping.x1 = clip->x1;

    plot_get_clip_grect(&gclip);

    gclip.g_x += canvas.g_x;
    gclip.g_y += canvas.g_y;

    rc_intersect(&canvas, &gclip);

    if(gclip.g_h < 0){
	gclip.g_h = 0;
    }

    if (!rc_intersect(&screen, &gclip)) {
	//dbg_rect("cliprect: ", &view.clipping);
	//dbg_grect("screen: ", &canvas);
	//dbg_grect("canvas clipped: ", &gclip);
	//assert(1 == 0);
    }

    // When setting VDI clipping, obey to maximum cliping rectangle:
    plot_get_abs_clipping_grect(&maxclip);
    rc_intersect(&maxclip, &gclip);

    //dbg_grect("canvas clipped to screen", &gclip);

    pxy[0] = gclip.g_x;
    pxy[1] = gclip.g_y;
    pxy[2] = pxy[0] + gclip.g_w;
    pxy[3] = pxy[1] + gclip.g_h;

    vs_clip(atari_plot_vdi_handle, 1, (short*)&pxy);

    return NSERROR_OK;
}


/**
 * Plots an arc
 *
 * plot an arc segment around (x,y), anticlockwise from angle1
 *  to angle2. Angles are measured anticlockwise from
 *  horizontal, in degrees.
 *
 * \param ctx The current redraw context.
 * \param pstyle Style controlling the arc plot.
 * \param x The x coordinate of the arc.
 * \param y The y coordinate of the arc.
 * \param radius The radius of the arc.
 * \param angle1 The start angle of the arc.
 * \param angle2 The finish angle of the arc.
 * \return NSERROR_OK on success else error code.
 */
static nserror
plot_arc(const struct redraw_context *ctx,
	 const plot_style_t *pstyle,
	 int x, int y, int radius, int angle1, int angle2)
{
    vswr_mode(atari_plot_vdi_handle, MD_REPLACE);
    if (pstyle->fill_type == PLOT_OP_TYPE_NONE) {
	return NSERROR_OK;
    }

    if (pstyle->fill_type != PLOT_OP_TYPE_SOLID) {
	vsl_rgbcolor(atari_plot_vdi_handle, pstyle->stroke_colour);
	vsf_perimeter(atari_plot_vdi_handle, 1);
	vsf_interior(atari_plot_vdi_handle, 1 );
	v_arc(atari_plot_vdi_handle,
	      view.x + x,
	      view.y + y,
	      radius,
	      angle1 * 10,
	      angle2 * 10);
    } else {
	vsf_rgbcolor(atari_plot_vdi_handle, pstyle->fill_colour);
	vsl_width(atari_plot_vdi_handle, 1);
	vsf_perimeter(atari_plot_vdi_handle, 1);
	v_arc(atari_plot_vdi_handle,
	      view.x + x,
	      view.y + y, radius,
	      angle1 * 10,
	      angle2 * 10);
    }

    return NSERROR_OK;
}


/**
 * Plots a circle
 *
 * Plot a circle centered on (x,y), which is optionally filled.
 *
 * \param ctx The current redraw context.
 * \param pstyle Style controlling the circle plot.
 * \param x x coordinate of circle centre.
 * \param y y coordinate of circle centre.
 * \param radius circle radius.
 * \return NSERROR_OK on success else error code.
 */
static nserror
plot_disc(const struct redraw_context *ctx,
	  const plot_style_t *pstyle,
	  int x, int y, int radius)
{
    if (pstyle->fill_type != PLOT_OP_TYPE_SOLID) {
	vsf_rgbcolor(atari_plot_vdi_handle, pstyle->stroke_colour);
	vsf_perimeter(atari_plot_vdi_handle, 1);
	vsf_interior(atari_plot_vdi_handle, 0);
	v_circle(atari_plot_vdi_handle, view.x + x, view.y + y, radius);
    } else {
	vsf_rgbcolor(atari_plot_vdi_handle, pstyle->fill_colour);
	vsf_perimeter(atari_plot_vdi_handle, 0);
	vsf_interior(atari_plot_vdi_handle, FIS_SOLID);
	v_circle(atari_plot_vdi_handle, view.x + x, view.y + y, radius);
    }
    return NSERROR_OK;
}


/**
 * Plots a line
 *
 * plot a line from (x0,y0) to (x1,y1). Coordinates are at
 *  centre of line width/thickness.
 *
 * \param ctx The current redraw context.
 * \param pstyle Style controlling the line plot.
 * \param line A rectangle defining the line to be drawn
 * \return NSERROR_OK on success else error code.
 */
static nserror
plot_line(const struct redraw_context *ctx,
	  const plot_style_t *pstyle,
	  const struct rect *line)
{
    short pxy[4];
    uint32_t lt;
    int sw = plot_style_fixed_to_int(pstyle->stroke_width);

    if (((line->x0 < 0) && (line->x1 < 0)) ||
	((line->y0 < 0) && (line->y1 < 0))) {
	return NSERROR_OK;
    }

    pxy[0] = view.x + MAX(0, line->x0);
    pxy[1] = view.y + MAX(0, line->y0);
    pxy[2] = view.x + MAX(0, line->x1);
    pxy[3] = view.y + MAX(0, line->y1);

    if ((line->y0 > view.h-1) && (line->y1 > view.h-1)) {
	return NSERROR_OK;
    }

    //printf("view: %d,%d,%d,%d\n", view.x, view.y, view.w, view.h);
    //printf("line: %d,%d,%d,%d\n", x0,  y0,  x1,  y1);

    //plot_vdi_clip(true);

    if (sw == 0) {
	sw = 1;
    }
    NSLT2VDI(lt, pstyle)
	vsl_type(atari_plot_vdi_handle, (lt&0x0F));
    /* if the line style is not available within VDI system,define own style: */
    if ((lt&0x0F) == 7 ) {
	vsl_udsty(atari_plot_vdi_handle, ((lt&0xFFFF00) >> 8));
    }
    vsl_width(atari_plot_vdi_handle, (short)sw);
    vsl_rgbcolor(atari_plot_vdi_handle, pstyle->stroke_colour);
    v_pline(atari_plot_vdi_handle, 2, (short *)&pxy );
    //plot_vdi_clip(false);

    return NSERROR_OK;
}


/**
 * Plots a rectangle.
 *
 * The rectangle can be filled an outline or both controlled
 *  by the plot style The line can be solid, dotted or
 *  dashed. Top left corner at (x0,y0) and rectangle has given
 *  width and height.
 *
 * \param ctx The current redraw context.
 * \param pstyle Style controlling the rectangle plot.
 * \param rect A rectangle defining the line to be drawn
 * \return NSERROR_OK on success else error code.
 */
static nserror
plot_rectangle(const struct redraw_context *ctx,
	       const plot_style_t *pstyle,
	       const struct rect *rect)
{
    short pxy[4];
    GRECT r, rclip, sclip;
    int sw = plot_style_fixed_to_int(pstyle->stroke_width);
    uint32_t lt;

    /* current canvas clip: */
    rclip.g_x = view.clipping.x0;
    rclip.g_y = view.clipping.y0;
    rclip.g_w = view.clipping.x1 - view.clipping.x0;
    rclip.g_h = view.clipping.y1 - view.clipping.y0;

    /* physical clipping: */
    sclip.g_x = rclip.g_x;
    sclip.g_y = rclip.g_y;
    sclip.g_w = view.vis_w;
    sclip.g_h = view.vis_h;

    rc_intersect(&sclip, &rclip);
    r.g_x = rect->x0;
    r.g_y = rect->y0;
    r.g_w = rect->x1 - rect->x0;
    r.g_h = rect->y1 - rect->y0;

    if (!rc_intersect(&rclip, &r)) {
	return NSERROR_OK;
    }

    if (pstyle->stroke_type != PLOT_OP_TYPE_NONE) {
	/*
	  manually draw the line, because we do not need vdi clipping
	  for vertical / horizontal line draws.
	*/
	if (sw == 0)
	    sw = 1;

	NSLT2VDI(lt, pstyle);
	vsl_type(atari_plot_vdi_handle, (lt&0x0F));
	/*
	  if the line style is not available within VDI system,
	  define own style:
	*/
	if ((lt&0x0F) == 7 ) {
	    vsl_udsty(atari_plot_vdi_handle, ((lt&0xFFFF00) >> 8));
	}
	vsl_width(atari_plot_vdi_handle, (short)sw );
	vsl_rgbcolor(atari_plot_vdi_handle, pstyle->stroke_colour);
	/* top border: */
	if (r.g_y == rect->y0) {
	    pxy[0] = view.x + r.g_x;
	    pxy[1] = view.y + r.g_y ;
	    pxy[2] = view.x + r.g_x + r.g_w;
	    pxy[3] = view.y + r.g_y;
	    v_pline(atari_plot_vdi_handle, 2, (short *)&pxy);
	}

	/* right border: */
	if (r.g_x + r.g_w == rect->x1 ) {
	    pxy[0] = view.x + r.g_x + r.g_w;
	    pxy[1] = view.y + r.g_y;
	    pxy[2] = view.x + r.g_x + r.g_w;
	    pxy[3] = view.y + r.g_y + r.g_h;
	    v_pline(atari_plot_vdi_handle, 2, (short *)&pxy);
	}

	/* bottom border: */
	if ( r.g_y+r.g_h == rect->y1 ) {
	    pxy[0] = view.x + r.g_x;
	    pxy[1] = view.y + r.g_y+r.g_h;
	    pxy[2] = view.x + r.g_x+r.g_w;
	    pxy[3] = view.y + r.g_y+r.g_h;
	    v_pline(atari_plot_vdi_handle, 2, (short *)&pxy);
	}

	/* left border: */
	if ( r.g_x == rect->x0 ) {
	    pxy[0] = view.x + r.g_x;
	    pxy[1] = view.y + r.g_y;
	    pxy[2] = view.x + r.g_x;
	    pxy[3] = view.y + r.g_y + r.g_h;
	    v_pline(atari_plot_vdi_handle, 2, (short *)&pxy);
	}
    }

    if (pstyle->fill_type != PLOT_OP_TYPE_NONE ) {
	short stroke_width = (short)(pstyle->stroke_type != PLOT_OP_TYPE_NONE) ?
	    plot_style_fixed_to_int(pstyle->stroke_width) : 0;

	vsf_rgbcolor(atari_plot_vdi_handle, pstyle->fill_colour);
	vsf_perimeter(atari_plot_vdi_handle, 0);
	vsf_interior(atari_plot_vdi_handle, FIS_SOLID);


	pxy[0] = view.x + r.g_x + stroke_width;
	pxy[1] = view.y + r.g_y + stroke_width;
	pxy[2] = view.x + r.g_x + r.g_w -1 - stroke_width;
	pxy[3] = view.y + r.g_y + r.g_h -1 - stroke_width;

	vsf_style(atari_plot_vdi_handle, 1);
	v_bar(atari_plot_vdi_handle, (short*)&pxy);
    }

    return NSERROR_OK;
}


/**
 * Plot a polygon
 *
 * Plots a filled polygon with straight lines between
 * points. The lines around the edge of the ploygon are not
 * plotted. The polygon is filled with the non-zero winding
 * rule.
 *
 * \param ctx The current redraw context.
 * \param pstyle Style controlling the polygon plot.
 * \param p verticies of polygon
 * \param n number of verticies.
 * \return NSERROR_OK on success else error code.
 */
static nserror
plot_polygon(const struct redraw_context *ctx,
	     const plot_style_t *pstyle,
	     const int *p,
	     unsigned int n)
{
    short pxy[n*2];
    unsigned int i = 0;

    if (vdi_sysinfo.maxpolycoords > 0)
	assert( (signed int)n < vdi_sysinfo.maxpolycoords);

    vsf_interior(atari_plot_vdi_handle, FIS_SOLID);
    vsf_style(atari_plot_vdi_handle, 1);
    for (i = 0; i<n*2; i=i+2) {
	pxy[i] = (short)view.x+p[i];
	pxy[i+1] = (short)view.y+p[i+1];
    }

    if (pstyle->fill_type == PLOT_OP_TYPE_SOLID) {
	vsf_rgbcolor(atari_plot_vdi_handle, pstyle->fill_colour);
	v_fillarea(atari_plot_vdi_handle, n, (short*)&pxy);
    } else {
	pxy[n*2]=pxy[0];
	pxy[n*2+1]=pxy[1];
	vsl_rgbcolor(atari_plot_vdi_handle, pstyle->stroke_colour);
	v_pline(atari_plot_vdi_handle, n+1,  (short *)&pxy);
    }

    return NSERROR_OK;
}


/**
 * Plots a path.
 *
 * Path plot consisting of cubic Bezier curves. Line and fill colour is
 *  controlled by the plot style.
 *
 * \param ctx The current redraw context.
 * \param pstyle Style controlling the path plot.
 * \param p elements of path
 * \param n nunber of elements on path
 * \param transform A transform to apply to the path.
 * \return NSERROR_OK on success else error code.
 */
static nserror
plot_path(const struct redraw_context *ctx,
	  const plot_style_t *pstyle,
	  const float *p,
	  unsigned int n,
	  const float transform[6])
{
    /** \todo Implement atari path plot */
    return NSERROR_OK;
}


/**
 * Plot a bitmap
 *
 * Tiled plot of a bitmap image. (x,y) gives the top left
 * coordinate of an explicitly placed tile. From this tile the
 * image can repeat in all four directions -- up, down, left
 * and right -- to the extents given by the current clip
 * rectangle.
 *
 * The bitmap_flags say whether to tile in the x and y
 * directions. If not tiling in x or y directions, the single
 * image is plotted. The width and height give the dimensions
 * the image is to be scaled to.
 *
 * \param ctx The current redraw context.
 * \param bitmap The bitmap to plot
 * \param x The x coordinate to plot the bitmap
 * \param y The y coordiante to plot the bitmap
 * \param width The width of area to plot the bitmap into
 * \param height The height of area to plot the bitmap into
 * \param bg the background colour to alpha blend into
 * \param flags the flags controlling the type of plot operation
 * \return NSERROR_OK on success else error code.
 */
static nserror
plot_bitmap(const struct redraw_context *ctx,
	    struct bitmap *bitmap,
	    int x, int y,
	    int width,
	    int height,
	    colour bg,
	    bitmap_flags_t flags)
{
    struct bitmap * bm = NULL;
    bool repeat_x = (flags & BITMAPF_REPEAT_X);
    bool repeat_y = (flags & BITMAPF_REPEAT_Y);
    int bmpw,bmph;
    struct rect clip = {0,0,0,0};

    bmpw = atari_bitmap_get_width(bitmap);
    bmph = atari_bitmap_get_height(bitmap);

    if(view.scale != 1.0){
	width = (int)(((float)width)*view.scale);
	height = (int)(((float)height)*view.scale);
    }

    if ( repeat_x || repeat_y ) {
	plot_get_clip(&clip);
	if (repeat_x && width == 1 && repeat_y && height == 1 ) {
	    width = MAX( width, clip.x1 - x );
	    height = MAX( height,  clip.y1 - y );
	} else if (repeat_x && width == 1 ) {
	    width = MAX( width, clip.x1 - x);
	} else if (repeat_y && height == 1) {
	    height = MAX( height, clip.y1 - y );
	}
    }

    if (width != bmpw || height != bmph) {
	atari_bitmap_resize(bitmap, hermes_res_h, &nsfmt, width, height );
	if (bitmap->resized) {
	    bm = bitmap->resized;
	} else {
	    bm = bitmap;
	}
    } else {
	bm = bitmap;
    }

    /* out of memory? */
    if (bm == NULL) {
	printf("plot: out of memory! bmp: %p, bmpres: %p\n",
	       bitmap, bitmap->resized );
	return NSERROR_NOMEM;
    }

    if (!(repeat_x || repeat_y) ) {
	plot_blit_bitmap(bm, x, y, bg, flags);
    } else {
	int xf,yf;
	int xoff = x;
	int yoff = y;

	if (yoff > clip.y0) {
	    yoff = (clip.y0 - height) + ((yoff - clip.y0) % height);
	}
	if (xoff > clip.x0) {
	    xoff = (clip.x0 - width) + ((xoff - clip.x0) % width);
	}
	/* for now, repeating just works in the rigth / down direction */
	/*
	  if( repeat_x == true )
	  xoff = clip.x0;
	  if(repeat_y == true )
	  yoff = clip.y0;
	*/

	for (xf = xoff; xf < clip.x1; xf += width ) {
	    for (yf = yoff; yf < clip.y1; yf += height ) {
		plot_blit_bitmap(bm, xf, yf, bg, flags );
		if (!repeat_y) {
		    break;
		}
	    }
	    if (!repeat_x) {
		break;
	    }
	}
    }

    return NSERROR_OK;
}


/**
 * Text plotting.
 *
 * \param ctx The current redraw context.
 * \param fstyle plot style for this text
 * \param x x coordinate
 * \param y y coordinate
 * \param text UTF-8 string to plot
 * \param length length of string, in bytes
 * \return NSERROR_OK on success else error code.
 */
static nserror
plot_text(const struct redraw_context *ctx,
	  const struct plot_font_style *fstyle,
	  int x,
	  int y,
	  const char *text,
	  size_t length)
{
    if (view.scale != 1.0) {
	plot_font_style_t newstyle = *fstyle;
	newstyle.size = (int)((float)fstyle->size*view.scale);
	fplotter->text(fplotter, x, y, text, length, &newstyle);
    } else {
	fplotter->text(fplotter, x, y, text, length, fstyle);
    }

    return NSERROR_OK;
}

/** atari plottr operation table */
const struct plotter_table atari_plotters = {
    .rectangle = plot_rectangle,
    .line = plot_line,
    .polygon = plot_polygon,
    .clip = plot_clip,
    .text = plot_text,
    .disc = plot_disc,
    .arc = plot_arc,
    .bitmap = plot_bitmap,
    .path = plot_path,
    .flush = NULL,
    .group_start = NULL,
    .group_end = NULL,
    .option_knockout = true
};
