netsurf/atari/plot/plot.c
2013-04-12 01:05:37 +02:00

2118 lines
58 KiB
C
Executable File

/*
* 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 <sys/types.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#include <stdbool.h>
#include <mt_gem.h>
#include "image/bitmap.h"
#include "utils/log.h"
#include "utils/utf8.h"
#include "utils/utils.h"
#include "desktop/gui.h"
#include "desktop/plotters.h"
#include "atari/bitmap.h"
#include "atari/gui.h"
#include "desktop/options.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 clipping; /* clipping rectangle */
};
/*
* Capture the screen at x,y location
* param self instance
* param x absolute screen coords
* param y absolute screen coords
* param w width
* param h height
*
* This creates an snapshot in RGBA format (NetSurf's native format)
*
*/
static struct bitmap * snapshot_create(int x, int y, int w, int h);
/* 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;
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
//unsigned short c[4];
RGB1000 c;
rgb_to_vdi1000( (unsigned char*)&cin, &c);
vs_color(vdih, OFFSET_CUSTOM_COLOR, (unsigned 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;
rgb_to_vdi1000( (unsigned char*)&cin, &c);
vs_color( vdih, OFFSET_CUSTOM_COLOR, (unsigned 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 (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.
*
*/
static struct s_vdi_sysinfo * read_vdi_sysinfo(short vdih, struct s_vdi_sysinfo * info) {
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, &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 ret = 0;
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) && (g!=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: %d\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: %d\n", temp.screensize);
printf(" unsigned long mask_r: 0x0%08x\n", temp.mask_r);
printf(" unsigned long mask_g: 0x0%08x\n", temp.mask_g);
printf(" unsigned long mask_b: 0x0%08x\n", temp.mask_b);
printf(" unsigned long mask_a: 0x0%08x\n", temp.mask_a);
printf(" short maxintin: %d\n", temp.maxintin);
printf(" short maxpolycoords: %d\n", temp.maxpolycoords);
printf(" unsigned long EdDiVersion: 0x0%03x\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 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 );
}
/*
* Create an snapshot of the screen in netsurf ABGR format
*/
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 = bitmap_create(w, h, 0);
} else {
buf_scr_compat = 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,
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;
}
}
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;
}
}
if(buf_scr_compat != NULL ) {
size_t bs = 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 = 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) {
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= 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 (img) onto the
background images (bg). 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= bitmap_get_rowstride(img);
bg_stride = 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
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 err;
int bw, bh;
int process_w, process_h;
struct bitmap * scrbuf = NULL;
struct bitmap * source;
bool cache = ( flags & BITMAPF_BUFFER_NATIVE );
bool opaque = 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 );
process_w = bw = bitmap_get_width( img );
process_h = bh = 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;
if( buf_packed == NULL )
buf_packed =(void*)malloc( blocks * CONV_BLOCK_SIZE);
else
buf_packed =(void*)realloc(buf_packed,blocks * CONV_BLOCK_SIZE);
assert( buf_packed );
if( buf_packed == NULL ) {
return( 0-ERR_NO_MEM );
}
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 = (void*)malloc( dstsize );
if (native.fd_addr == NULL){
if (scrbuf != NULL)
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;
if( buf_planar == NULL )
buf_planar =(void*)malloc( blocks * CONV_BLOCK_SIZE );
else
buf_planar =(void*)realloc(buf_planar, blocks * CONV_BLOCK_SIZE);
assert(buf_planar);
if( buf_planar == NULL ) {
return( 0-ERR_NO_MEM );
}
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 = bitmap_get_rowstride(img);
uint32_t prev_pixel = 0x12345678;
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
* img: the bitmap
* x: coordinate where the bitmap REGION (described in clip)
* shall be drawn (screen coords)
* y: coordinate where the bitmap REGION (described in clip)
* shall be drawn (screen coords)
* clip: which area of the bitmap shall be drawn
* bg: background color
* flags: blit flags
* 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 = 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 = bitmap_get_width( img );
bh = 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) {
if (dstsize > size_buf_packed) {
int blocks = (dstsize / (CONV_BLOCK_SIZE-1))+1;
if( buf_packed == NULL )
buf_packed =(void*)malloc( blocks * CONV_BLOCK_SIZE );
else
buf_packed =(void*)realloc(buf_packed,
blocks * CONV_BLOCK_SIZE);
assert( buf_packed );
if( buf_packed == NULL ) {
if( scrbuf != NULL )
bitmap_destroy( scrbuf );
return( 0-ERR_NO_MEM );
}
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 )
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) {
/* free the mem if it was an large allocation ... */
buf_packed = realloc(buf_packed, CONV_KEEP_LIMIT);
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, 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 );
}
/*
Init screen and font driver objects.
Returns non-zero value > -1 when the objects could be succesfully created.
Returns value < 0 to indicate an error
*/
int plot_init(char * fdrvrname)
{
GRECT loc_pos= {0,0,360,400};
int err=0,i;
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);
LOG(("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);
die(("Unable to load font plotter %s -> %s", fdrvrname, desc ));
}
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;
}
update_visible_rect();
struct rect clip;
clip.x0 = 0;
clip.y0 = 0;
clip.x1 = view.w;
clip.y1 = view.h;
plot_clip(&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 */
i = 0;
unsigned char * col;
unsigned char rgbcol[4];
unsigned char graytone=0;
if( vdi_sysinfo.scr_bpp <= 8 ){
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 )
{
int i=0;
delete_font_plotter(fplotter);
#ifdef WITH_8BPP_SUPPORT
if (vfmt.indexed) {
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();
}
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);
}
bool plot_rectangle(int x0, int y0, int x1, int y1,
const plot_style_t *pstyle )
{
short pxy[4];
GRECT r, rclip, sclip;
int sw = 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 = x0;
r.g_y = y0;
r.g_w = x1 - x0;
r.g_h = y1 - y0;
if (!rc_intersect( &rclip, &r )) {
return(true);
}
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 == 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 == 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 == 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 == 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) ?
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 (true);
}
bool plot_line(int x0, int y0, int x1, int y1,
const plot_style_t *pstyle )
{
short pxy[4];
uint32_t lt;
int sw = pstyle->stroke_width;
if((x0 < 0 && x1 < 0) || (y0 < 0 && y1 < 0)){
return(true);
}
pxy[0] = view.x + MAX(0,x0);
pxy[1] = view.y + MAX(0,y0);
pxy[2] = view.x + MAX(0,x1);
pxy[3] = view.y + MAX(0,y1);
if((y0 > view.h-1) && (y1 > view.h-1))
return(true);
//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 (true);
}
static bool plot_polygon(const int *p, unsigned int n,
const plot_style_t *pstyle)
{
short pxy[n*2];
unsigned int i=0;
short d[4];
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 ( true );
}
bool plot_set_dimensions(int x, int y, int w, int h)
{
bool doupdate = false;
struct rect newclip = {0, 0, w, h};
if (!(w == view.w && h == view.h)) {
struct rect newclip = { 0, 0, w-1, h-1 };
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_clip(&newclip);
return(true);
}
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);
}
bool plot_clip(const struct rect *clip)
{
GRECT canvas, screen, gclip, isection;
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);
}
//assert(rc_intersect(&screen, &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 ( true );
}
VdiHdl plot_get_vdi_handle(void)
{
return(atari_plot_vdi_handle);
}
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;
}
static bool plot_text(int x, int y, const char *text, size_t length, const plot_font_style_t *fstyle )
{
fplotter->text(fplotter, x, y, text, length, fstyle);
return ( true );
}
static bool plot_disc(int x, int y, int radius, const plot_style_t *pstyle)
{
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(true);
}
static bool plot_arc(int x, int y, int radius, int angle1, int angle2,
const plot_style_t *pstyle)
{
vswr_mode(atari_plot_vdi_handle, MD_REPLACE );
if (pstyle->fill_type == PLOT_OP_TYPE_NONE)
return(true);
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 (true);
}
static bool plot_bitmap(int x, int y, int width, int height,
struct bitmap *bitmap, 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 = bitmap_get_width(bitmap);
bmph = bitmap_get_height(bitmap);
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 ) {
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( true );
}
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 ( true );
}
static bool plot_path(const float *p, unsigned int n, colour fill, float width,
colour c, const float transform[6])
{
return ( true );
}
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
};