mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-12-23 20:46:50 +03:00
397 lines
10 KiB
C
397 lines
10 KiB
C
/*
|
|
* Copyright 2004, 2005 Richard Wilson <info@tinct.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 <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <swis.h>
|
|
#include <oslib/colourtrans.h>
|
|
#include <oslib/os.h>
|
|
#include <oslib/osspriteop.h>
|
|
#include <oslib/wimp.h>
|
|
#include <oslib/wimpreadsysinfo.h>
|
|
|
|
#include "utils/nsoption.h"
|
|
#include "utils/log.h"
|
|
|
|
#include "riscos/buffer.h"
|
|
#include "riscos/gui.h"
|
|
#include "riscos/tinct.h"
|
|
#include "riscos/wimp.h"
|
|
#include "riscos/wimputils.h"
|
|
|
|
#define BUFFER_EXCLUSIVE_USER_REDRAW "Only support pure user redraw (faster)"
|
|
//#define BUFFER_EMULATE_32BPP "Redirect to a 32bpp sprite and plot with Tinct"
|
|
|
|
/** Absent from OSLib
|
|
*/
|
|
#ifndef osspriteop_TYPEEXPANSION
|
|
#define osspriteop_TYPEEXPANSION ((osspriteop_mode_word) 0xFu)
|
|
#endif
|
|
#ifndef osspriteop_TYPE16BPP4K
|
|
#define osspriteop_TYPE16BPP4K ((osspriteop_mode_word) 0x10u)
|
|
#endif
|
|
|
|
static void ro_gui_buffer_free(void);
|
|
|
|
|
|
/** The buffer characteristics
|
|
*/
|
|
static osspriteop_area *buffer = NULL;
|
|
static char buffer_name[12] = "scr_buffer";
|
|
|
|
/** The current clip area
|
|
*/
|
|
static os_box clipping;
|
|
|
|
/** The current save area
|
|
*/
|
|
static osspriteop_save_area *save_area;
|
|
static int context0;
|
|
static int context1;
|
|
static int context2;
|
|
static int context3;
|
|
|
|
/** The current sprite mode
|
|
*/
|
|
static os_mode mode;
|
|
|
|
|
|
/**
|
|
* Opens a buffer for writing to.
|
|
*
|
|
* The ro_plot_origin_ variables are updated to reflect the new screen origin,
|
|
* so the variables should be set before calling this function, and not
|
|
* changed until after ro_gui_buffer_close() has been called.
|
|
*
|
|
* \param redraw the current WIMP redraw area to buffer
|
|
*/
|
|
void ro_gui_buffer_open(wimp_draw *redraw)
|
|
{
|
|
int size;
|
|
int total_size;
|
|
os_coord sprite_size;
|
|
int bpp, word_width;
|
|
bool palette;
|
|
os_error *error;
|
|
int palette_size = 0;
|
|
#ifdef BUFFER_EXCLUSIVE_USER_REDRAW
|
|
osspriteop_header *header;
|
|
#endif
|
|
|
|
/* Close any open buffer
|
|
*/
|
|
if (buffer)
|
|
ro_gui_buffer_close();
|
|
|
|
/* Store our clipping region
|
|
*/
|
|
clipping = redraw->clip;
|
|
|
|
/* Stop bad rectangles
|
|
*/
|
|
if ((clipping.x1 < clipping.x0) ||
|
|
(clipping.y1 < clipping.y0)) {
|
|
LOG("Invalid clipping rectangle (%i, %i) to (%i,%i)", clipping.x0, clipping.y0, clipping.x1, clipping.y1);
|
|
return;
|
|
}
|
|
|
|
/* Work out how much buffer we need
|
|
*/
|
|
sprite_size.x = clipping.x1 - clipping.x0 + 1;
|
|
sprite_size.y = clipping.y1 - clipping.y0 + 1;
|
|
ro_convert_os_units_to_pixels(&sprite_size, (os_mode)-1);
|
|
if (sprite_size.y == 1) /* work around SpriteExtend bug */
|
|
sprite_size.y = 2;
|
|
|
|
#ifdef BUFFER_EMULATE_32BPP
|
|
bpp = 5;
|
|
palette = false;
|
|
#else
|
|
/* Get the screen depth as we can't use palettes for >8bpp
|
|
*/
|
|
xos_read_mode_variable((os_mode)-1, os_MODEVAR_LOG2_BPP, &bpp, 0);
|
|
palette = (bpp < 4);
|
|
#endif
|
|
|
|
/* Get our required buffer size
|
|
*/
|
|
word_width = ((sprite_size.x << bpp) + 31) >> 5;
|
|
if (palette)
|
|
palette_size = ((1 << (1 << bpp)) << 3);
|
|
total_size = sizeof(osspriteop_area) + sizeof(osspriteop_header) +
|
|
(word_width * sprite_size.y * 4) + palette_size;
|
|
buffer = (osspriteop_area *)malloc(total_size);
|
|
if (!buffer) {
|
|
LOG("Failed to allocate memory");
|
|
ro_gui_buffer_free();
|
|
return;
|
|
}
|
|
buffer->size = total_size;
|
|
buffer->first = 16;
|
|
|
|
#ifdef BUFFER_EMULATE_32BPP
|
|
mode = tinct_SPRITE_MODE;
|
|
#else
|
|
if ((error = xwimpreadsysinfo_wimp_mode(&mode)) != NULL) {
|
|
LOG("Error reading mode '%s'", error->errmess);
|
|
ro_gui_buffer_free();
|
|
return;
|
|
}
|
|
|
|
/* if we're not in a numbered screen mode then we need
|
|
to build a suitable sprite mode word */
|
|
if (mode >= (os_mode)0x100) {
|
|
static const ns_os_vdu_var_list vars = {
|
|
os_MODEVAR_LOG2_BPP,
|
|
{
|
|
os_MODEVAR_MODE_FLAGS,
|
|
os_MODEVAR_NCOLOUR,
|
|
os_MODEVAR_XEIG_FACTOR,
|
|
os_MODEVAR_YEIG_FACTOR,
|
|
os_VDUVAR_END_LIST
|
|
}
|
|
};
|
|
struct {
|
|
int log2bpp;
|
|
int flags;
|
|
int ncolour;
|
|
int xeig, yeig;
|
|
} vals;
|
|
int type;
|
|
|
|
error = xos_read_vdu_variables(PTR_OS_VDU_VAR_LIST(&vars), (int *)&vals);
|
|
if (error) {
|
|
LOG("Error reading mode properties '%s'", error->errmess);
|
|
ro_gui_buffer_free();
|
|
return;
|
|
}
|
|
|
|
switch (vals.ncolour) {
|
|
case 1:
|
|
case 3:
|
|
case 15:
|
|
case 63:
|
|
case 255:
|
|
/* Paletted modes are pixel packing order agnostic */
|
|
type = 1 + vals.log2bpp;
|
|
mode = (os_mode)((type << osspriteop_TYPE_SHIFT) |
|
|
osspriteop_NEW_STYLE |
|
|
((180 >> vals.yeig) << osspriteop_YRES_SHIFT) |
|
|
((180 >> vals.xeig) << osspriteop_XRES_SHIFT));
|
|
break;
|
|
case 4095:
|
|
/* 16bpp 4k colours */
|
|
type = osspriteop_TYPE16BPP4K;
|
|
mode = (os_mode)((osspriteop_TYPEEXPANSION << osspriteop_TYPE_SHIFT) |
|
|
osspriteop_NEW_STYLE |
|
|
(vals.yeig << 6) |
|
|
(vals.xeig << 4) |
|
|
(type << 20) |
|
|
(vals.flags & 0xFF00));
|
|
break;
|
|
case 65535:
|
|
switch ((vals.flags & 0x3000) >> os_MODE_FLAG_DATA_FORMAT_SHIFT) {
|
|
case os_MODE_FLAG_DATA_FORMAT_RGB:
|
|
if (vals.flags & 0xC000) {
|
|
/* Non VIDC packing order */
|
|
if (vals.flags & os_MODE_FLAG_FULL_PALETTE)
|
|
type = osspriteop_TYPE16BPP64K;
|
|
else
|
|
type = osspriteop_TYPE16BPP;
|
|
mode = (os_mode)((osspriteop_TYPEEXPANSION << osspriteop_TYPE_SHIFT) |
|
|
osspriteop_NEW_STYLE |
|
|
(vals.yeig << 6) |
|
|
(vals.xeig << 4) |
|
|
(type << 20) |
|
|
(vals.flags & 0xFF00));
|
|
} else {
|
|
/* VIDC packing order */
|
|
if (vals.flags & os_MODE_FLAG_FULL_PALETTE)
|
|
type = osspriteop_TYPE16BPP64K;
|
|
else
|
|
type = osspriteop_TYPE16BPP;
|
|
mode = (os_mode)((type << osspriteop_TYPE_SHIFT) |
|
|
osspriteop_NEW_STYLE |
|
|
((180 >> vals.yeig) << osspriteop_YRES_SHIFT) |
|
|
((180 >> vals.xeig) << osspriteop_XRES_SHIFT));
|
|
}
|
|
break;
|
|
default:
|
|
LOG("Unhandled 16bpp format from flags %d", vals.flags);
|
|
ro_gui_buffer_free();
|
|
return;
|
|
}
|
|
break;
|
|
case -1:
|
|
/* 16M colours */
|
|
switch ((vals.flags & 0x3000) >> os_MODE_FLAG_DATA_FORMAT_SHIFT) {
|
|
case os_MODE_FLAG_DATA_FORMAT_RGB:
|
|
if (vals.flags & 0xC000) {
|
|
/* Non VIDC packing order */
|
|
type = osspriteop_TYPE32BPP;
|
|
mode = (os_mode)((osspriteop_TYPEEXPANSION << osspriteop_TYPE_SHIFT) |
|
|
osspriteop_NEW_STYLE |
|
|
(vals.yeig << 6) |
|
|
(vals.xeig << 4) |
|
|
(type << 20) |
|
|
(vals.flags & 0xFF00));
|
|
} else {
|
|
/* VIDC packing order */
|
|
type = osspriteop_TYPE32BPP;
|
|
mode = (os_mode)((type << osspriteop_TYPE_SHIFT) |
|
|
osspriteop_NEW_STYLE |
|
|
((180 >> vals.yeig) << osspriteop_YRES_SHIFT) |
|
|
((180 >> vals.xeig) << osspriteop_XRES_SHIFT));
|
|
}
|
|
break;
|
|
default:
|
|
LOG("Unhandled 32bpp data format from flags %d", vals.flags);
|
|
ro_gui_buffer_free();
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
LOG("Unhandled NCOLOUR value %d", vals.ncolour);
|
|
ro_gui_buffer_free();
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUFFER_EXCLUSIVE_USER_REDRAW
|
|
/* Create the sprite manually so we don't waste time clearing the
|
|
background.
|
|
*/
|
|
buffer->sprite_count = 1;
|
|
buffer->used = total_size;
|
|
header = (osspriteop_header *)(buffer + 1);
|
|
header->size = total_size - sizeof(osspriteop_area);
|
|
memcpy(header->name, buffer_name, 12);
|
|
header->width = word_width - 1;
|
|
header->height = sprite_size.y - 1;
|
|
header->left_bit = 0;
|
|
header->right_bit = ((sprite_size.x << bpp) - 1) & 31;
|
|
header->image = sizeof(osspriteop_header) + palette_size;
|
|
header->mask = header->image;
|
|
header->mode = mode;
|
|
if (palette)
|
|
xcolourtrans_read_palette((osspriteop_area *)mode,
|
|
(osspriteop_id)os_CURRENT_MODE,
|
|
(os_palette *)(header + 1), palette_size,
|
|
(colourtrans_palette_flags)
|
|
colourtrans_FLASHING_PALETTE, 0);
|
|
#else
|
|
/* Read the current contents of the screen
|
|
*/
|
|
buffer->sprite_count = 0;
|
|
buffer->used = 16;
|
|
if ((error = xosspriteop_get_sprite_user_coords(osspriteop_NAME,
|
|
buffer, buffer_name, palette,
|
|
clipping.x0, clipping.y0,
|
|
clipping.x1, clipping.y1)) != NULL) {
|
|
LOG("Grab error '%s'", error->errmess);
|
|
ro_gui_buffer_free();
|
|
return;
|
|
}
|
|
#endif
|
|
/* Allocate OS_SpriteOp save area
|
|
*/
|
|
if ((error = xosspriteop_read_save_area_size(osspriteop_PTR,
|
|
buffer, (osspriteop_id)(buffer + 1), &size)) != NULL) {
|
|
LOG("Save area error '%s'", error->errmess);
|
|
ro_gui_buffer_free();
|
|
return;
|
|
}
|
|
if ((save_area = malloc((size_t)size)) == NULL) {
|
|
ro_gui_buffer_free();
|
|
return;
|
|
}
|
|
save_area->a[0] = 0;
|
|
|
|
/* Switch output to sprite
|
|
*/
|
|
if ((error = xosspriteop_switch_output_to_sprite(osspriteop_PTR,
|
|
buffer, (osspriteop_id)(buffer + 1), save_area,
|
|
&context0, &context1, &context2, &context3)) != NULL) {
|
|
LOG("Switching error '%s'", error->errmess);
|
|
free(save_area);
|
|
ro_gui_buffer_free();
|
|
return;
|
|
}
|
|
|
|
/* Emulate an origin as the FontManager doesn't respect it in
|
|
most cases.
|
|
*/
|
|
ro_plot_origin_x -= clipping.x0;
|
|
ro_plot_origin_y -= clipping.y0;
|
|
|
|
/* Update the ECF origin
|
|
*/
|
|
if ((error = xos_set_ecf_origin(-ro_plot_origin_x,
|
|
-ro_plot_origin_y)) != NULL) {
|
|
LOG("Invalid ECF origin: '%s'", error->errmess);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Closes any open buffer and flushes the contents to screen
|
|
*/
|
|
void ro_gui_buffer_close(void)
|
|
{
|
|
/* Check we have an open buffer
|
|
*/
|
|
if (!buffer)
|
|
return;
|
|
|
|
/* Remove any previous redirection
|
|
*/
|
|
ro_plot_origin_x += clipping.x0;
|
|
ro_plot_origin_y += clipping.y0;
|
|
xosspriteop_unswitch_output(context0, context1, context2, context3);
|
|
free(save_area);
|
|
|
|
/* Plot the contents to screen
|
|
*/
|
|
if (mode == tinct_SPRITE_MODE)
|
|
_swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7),
|
|
(char *)(buffer + 1),
|
|
clipping.x0, clipping.y0,
|
|
nsoption_int(plot_fg_quality));
|
|
else
|
|
xosspriteop_put_sprite_user_coords(osspriteop_PTR,
|
|
buffer, (osspriteop_id)(buffer + 1),
|
|
clipping.x0, clipping.y0, (os_action)0);
|
|
ro_gui_buffer_free();
|
|
|
|
/* Update the ECF origin
|
|
*/
|
|
os_set_ecf_origin(0, 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* Releases any buffer memory depending on cache constraints.
|
|
*/
|
|
static void ro_gui_buffer_free(void)
|
|
{
|
|
free(buffer);
|
|
buffer = NULL;
|
|
}
|