2239 lines
60 KiB
C
Executable File
2239 lines
60 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/plotters.h"
|
|
#include "desktop/mouse.h"
|
|
|
|
#include "atari/bitmap.h"
|
|
#include "utils/nsoption.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;
|
|
};
|
|
|
|
/*
|
|
* 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 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: %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;
|
|
}
|
|
}
|
|
|
|
#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 = 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
|
|
/**
|
|
* 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 = 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
|
|
// 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; //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
|
|
* 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, ®ion );
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
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 */
|
|
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();
|
|
}
|
|
|
|
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;
|
|
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 );
|
|
}
|
|
|
|
/***
|
|
* Set plot origin and canvas size
|
|
* \param x the x origin
|
|
* \param y the y origin
|
|
* \param w the width of the plot area
|
|
* \param h the height of the plot area
|
|
*/
|
|
bool plot_set_dimensions(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);
|
|
plot_clip(&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;
|
|
}
|
|
|
|
bool plot_clip(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 ( true );
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static bool plot_text(int x, int y, const char *text, size_t length, const plot_font_style_t *fstyle )
|
|
{
|
|
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 ( 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(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 ) {
|
|
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
|
|
};
|