The graphics verbs documented a coordinate transform that did not exist. With SCALE off a coordinate went straight to akgl_draw_* as a pixel address, so an 800x600 window drew a C128 listing into its corner and left the rest unused -- while the chapter said coordinates were 320x200 and stretching to fit was the host's business. akbasic_GraphicsBackend gains a size entry point, require_graphics() asks it before every verb that draws so a resized window is honoured between two statements, and 320x200 becomes the fallback for a backend that leaves it NULL. It is the record's one optional member, so a host written against the old header keeps the behaviour it had. SCALE now maps onto the device, and RGR(1)/RGR(2) report the drawing surface so a program can use a window whose size it did not choose. RGR(0) is BASIC 7.0's own field, the GRAPHIC mode. SCALE also mapped xmax onto the width rather than onto the last pixel, so SCALE 1, 319, 199 followed by DRAW 1, 319, 199 drew nothing at all -- one pixel past the surface. Fixed in the same line, because it is what makes "SCALE gives a C128 listing the whole window" true rather than nearly true. The akgl test renders against a 128x128 target, deliberately smaller than the old constants: a SCALE still dividing by them misses it entirely rather than landing somewhere plausible. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
115 lines
4.6 KiB
C
115 lines
4.6 KiB
C
/**
|
|
* @file graphics_tables.c
|
|
* @brief Implements the BASIC 7.0 color palette lookup.
|
|
*
|
|
* This is a table, so it is laid out as one. The RGB values are the widely
|
|
* reproduced VIC-II palette (Pepto's measurement of a PAL 6569R3), which is what
|
|
* an emulator shows and therefore what somebody porting a listing expects to
|
|
* see. They are not a C128 ROM constant -- the real machine has no RGB anywhere
|
|
* in it, it has a chroma/luma encoder -- so this is a choice, not a transcription.
|
|
*/
|
|
|
|
#include <akerror.h>
|
|
|
|
#include <akbasic/error.h>
|
|
#include <akbasic/graphics.h>
|
|
|
|
/**
|
|
* @brief The sixteen BASIC 7.0 colors, indexed from 1 as BASIC indexes them.
|
|
*
|
|
* Slot 0 is unused and left black so an off-by-one reads as black rather than as
|
|
* whatever the last entry happened to be.
|
|
*/
|
|
static const akbasic_Color PALETTE[17] = {
|
|
{ 0x00, 0x00, 0x00, 0xff }, /* 0 -- unused; BASIC counts colors from 1 */
|
|
{ 0x00, 0x00, 0x00, 0xff }, /* 1 -- black */
|
|
{ 0xff, 0xff, 0xff, 0xff }, /* 2 -- white */
|
|
{ 0x88, 0x39, 0x32, 0xff }, /* 3 -- red */
|
|
{ 0x67, 0xb6, 0xbd, 0xff }, /* 4 -- cyan */
|
|
{ 0x8b, 0x3f, 0x96, 0xff }, /* 5 -- purple */
|
|
{ 0x55, 0xa0, 0x49, 0xff }, /* 6 -- green */
|
|
{ 0x40, 0x31, 0x8d, 0xff }, /* 7 -- blue */
|
|
{ 0xbf, 0xce, 0x72, 0xff }, /* 8 -- yellow */
|
|
{ 0x8b, 0x54, 0x29, 0xff }, /* 9 -- orange */
|
|
{ 0x57, 0x42, 0x00, 0xff }, /* 10 -- brown */
|
|
{ 0xb8, 0x69, 0x62, 0xff }, /* 11 -- light red */
|
|
{ 0x50, 0x50, 0x50, 0xff }, /* 12 -- dark grey */
|
|
{ 0x78, 0x78, 0x78, 0xff }, /* 13 -- medium grey */
|
|
{ 0x94, 0xe0, 0x89, 0xff }, /* 14 -- light green */
|
|
{ 0x78, 0x69, 0xc4, 0xff }, /* 15 -- light blue */
|
|
{ 0x9f, 0x9f, 0x9f, 0xff } /* 16 -- light grey */
|
|
};
|
|
|
|
/**
|
|
* @brief What each COLOR source is bound to before a program says otherwise.
|
|
*
|
|
* A C128 powers on with a blue screen and light blue text. That is not
|
|
* reproduced: a graphics screen defaulting to blue-on-blue makes an unset COLOR
|
|
* look like a bug in the interpreter, so the defaults here are the readable ones.
|
|
* The choice is worth naming rather than hiding, since it is the one place these
|
|
* tables are not a transcription.
|
|
*/
|
|
static const int SOURCE_DEFAULTS[AKBASIC_COLOR_SOURCES] = {
|
|
1, /* 0 -- 40-column background: black */
|
|
2, /* 1 -- 40-column foreground: white */
|
|
3, /* 2 -- multicolor 1: red */
|
|
4, /* 3 -- multicolor 2: cyan */
|
|
1, /* 4 -- 40-column border: black */
|
|
2, /* 5 -- character color: white */
|
|
1 /* 6 -- 80-column background: black */
|
|
};
|
|
|
|
akerr_ErrorContext *akbasic_graphics_palette(int index, akbasic_Color *dest)
|
|
{
|
|
PREPARE_ERROR(errctx);
|
|
|
|
FAIL_ZERO_RETURN(errctx, (dest != NULL), AKERR_NULLPOINTER,
|
|
"NULL destination in palette");
|
|
FAIL_ZERO_RETURN(errctx, (index >= 1 && index <= 16), AKBASIC_ERR_BOUNDS,
|
|
"Color index %d out of range (1 to 16)", index);
|
|
*dest = PALETTE[index];
|
|
SUCCEED_RETURN(errctx);
|
|
}
|
|
|
|
akerr_ErrorContext *akbasic_graphics_state_init(akbasic_GraphicsState *obj)
|
|
{
|
|
PREPARE_ERROR(errctx);
|
|
int source = 0;
|
|
|
|
FAIL_ZERO_RETURN(errctx, (obj != NULL), AKERR_NULLPOINTER,
|
|
"NULL graphics state in init");
|
|
obj->mode = 0;
|
|
obj->x = 0.0;
|
|
obj->y = 0.0;
|
|
obj->scaling = false;
|
|
obj->linewidth = 1;
|
|
obj->xmax = (double)AKBASIC_GRAPHICS_WIDTH;
|
|
obj->ymax = (double)AKBASIC_GRAPHICS_HEIGHT;
|
|
/*
|
|
* The size assumed until a device says otherwise, which require_graphics()
|
|
* asks it before every verb that draws. GRAPHIC CLR comes back through here
|
|
* and deliberately resets it: the next verb re-asks, so a stale size cannot
|
|
* outlive the device that reported it.
|
|
*/
|
|
obj->devwidth = AKBASIC_GRAPHICS_WIDTH;
|
|
obj->devheight = AKBASIC_GRAPHICS_HEIGHT;
|
|
for ( source = 0; source < AKBASIC_COLOR_SOURCES; source++ ) {
|
|
obj->source[source] = SOURCE_DEFAULTS[source];
|
|
}
|
|
SUCCEED_RETURN(errctx);
|
|
}
|
|
|
|
akerr_ErrorContext *akbasic_graphics_source_color(akbasic_GraphicsState *obj, int source, akbasic_Color *dest)
|
|
{
|
|
PREPARE_ERROR(errctx);
|
|
|
|
FAIL_ZERO_RETURN(errctx, (obj != NULL && dest != NULL), AKERR_NULLPOINTER,
|
|
"NULL argument in source_color");
|
|
FAIL_ZERO_RETURN(errctx, (source >= 0 && source < AKBASIC_COLOR_SOURCES),
|
|
AKBASIC_ERR_BOUNDS,
|
|
"Color source %d out of range (0 to %d)",
|
|
source, AKBASIC_COLOR_SOURCES - 1);
|
|
PASS(errctx, akbasic_graphics_palette(obj->source[source], dest));
|
|
SUCCEED_RETURN(errctx);
|
|
}
|