mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-11-28 01:09:39 +03:00
2d2fc67265
Substantial speed increase for off-screen buffering svn path=/import/netsurf/; revision=1459
242 lines
6.3 KiB
C
242 lines
6.3 KiB
C
/*
|
|
* This file is part of NetSurf, http://netsurf.sourceforge.net/
|
|
* Licensed under the GNU General Public License,
|
|
* http://www.opensource.org/licenses/gpl-license
|
|
* Copyright 2004, 2005 Richard Wilson <info@tinct.net>
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "oslib/colourtrans.h"
|
|
#include "oslib/os.h"
|
|
#include "oslib/osspriteop.h"
|
|
#include "oslib/wimp.h"
|
|
#include "oslib/wimpreadsysinfo.h"
|
|
#include "netsurf/riscos/buffer.h"
|
|
#include "netsurf/riscos/options.h"
|
|
#include "netsurf/riscos/wimp.h"
|
|
#include "netsurf/utils/log.h"
|
|
//#define NDEBUG
|
|
#define BUFFER_EXCLUSIVE_USER_REDRAW "Only support pure user redraw (faster)"
|
|
|
|
/* SCREEN BUFFERING
|
|
================
|
|
|
|
Because RISC OS provides no native way for windows to be buffered (ie
|
|
the contents is only updated when the task has finished doing any
|
|
drawing) certain situation cause the window contents to flicker in an
|
|
undesirable manner. Examples of this are GIF and MNG animations, and
|
|
web pages with fixed backgrounds.
|
|
|
|
To overcome this, a very simple, transparent, interface is provided here
|
|
to allow for output to be buffered. It should be noted that screen
|
|
buffering can lower the perceived client response time as the user is
|
|
unable to see that the application is doing anything.
|
|
|
|
[rjw] - Mon 19th July 2004
|
|
*/
|
|
|
|
static void ro_gui_buffer_free(void);
|
|
|
|
|
|
/** The buffer characteristics
|
|
*/
|
|
static bool buffer_active = false;
|
|
static osspriteop_area *buffer = NULL;
|
|
static char buffer_name[] = "scr_buffer\0\0";
|
|
|
|
/** The current clip area
|
|
*/
|
|
static os_box clipping;
|
|
|
|
/** The current save area
|
|
*/
|
|
static osspriteop_save_area *save_area;
|
|
static osspriteop_area *context1;
|
|
static osspriteop_id context2;
|
|
static osspriteop_save_area *context3;
|
|
|
|
|
|
/**
|
|
* Opens a buffer for writing to.
|
|
*
|
|
* \param redraw the current WIMP redraw area to buffer
|
|
*/
|
|
void ro_gui_buffer_open(wimp_draw *redraw) {
|
|
int size;
|
|
int orig_x0, orig_y0;
|
|
int total_size;
|
|
os_coord sprite_size;
|
|
int bpp, word_width;
|
|
bool palette;
|
|
os_error *error;
|
|
int palette_size = 0;
|
|
int claim_size;
|
|
#ifdef BUFFER_EXCLUSIVE_USER_REDRAW
|
|
os_mode mode;
|
|
osspriteop_header *header;
|
|
#endif
|
|
|
|
/* Close any open buffer
|
|
*/
|
|
if (buffer_active)
|
|
ro_gui_buffer_close();
|
|
|
|
/* Store our clipping region
|
|
*/
|
|
clipping = redraw->clip;
|
|
|
|
/* 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);
|
|
|
|
/* 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);;
|
|
|
|
/* 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;
|
|
|
|
if ((buffer == NULL) || (total_size > buffer->size)) {
|
|
claim_size = (total_size > (option_screen_cache << 10)) ?
|
|
total_size : (option_screen_cache << 10);
|
|
if (buffer)
|
|
free(buffer);
|
|
buffer = (osspriteop_area *)malloc(claim_size);
|
|
if (!buffer) {
|
|
LOG(("Failed to allocate memory"));
|
|
return;
|
|
}
|
|
buffer->size = claim_size;
|
|
buffer->first = 16;
|
|
}
|
|
|
|
/* Set the sprite area details
|
|
*/
|
|
#ifdef BUFFER_EXCLUSIVE_USER_REDRAW
|
|
if ((error = xwimpreadsysinfo_wimp_mode(&mode)) != NULL) {
|
|
LOG(("Error reading mode '%s'", error->errmess));
|
|
ro_gui_buffer_free();
|
|
return;
|
|
}
|
|
|
|
/* 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)0,
|
|
(os_palette *)(header + 1), palette_size,
|
|
(colourtrans_palette_flags)(1 << 1), 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, 0,
|
|
(int *)&context1, (int *)&context2,
|
|
(int *)&context3)) != NULL) {
|
|
LOG(("Switching error '%s'", error->errmess));
|
|
free(save_area);
|
|
ro_gui_buffer_free();
|
|
return;
|
|
}
|
|
|
|
/* Move the origin such that (x0, y0) becomes (0, 0). To do this
|
|
we use VDU 29,(1 << 16) - x0; (1 << 16) - y0; because RISC OS
|
|
is so insanely legacy driven.
|
|
*/
|
|
orig_x0 = (1 << 16) - clipping.x0;
|
|
orig_y0 = (1 << 16) - clipping.y0;
|
|
os_writec((char)29);
|
|
os_writec(orig_x0 & 0xff); os_writec(orig_x0 >> 8);
|
|
os_writec(orig_y0 & 0xff); os_writec(orig_y0 >> 8);
|
|
buffer_active = true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Closes any open buffer and flushes the contents to screen
|
|
*/
|
|
void ro_gui_buffer_close(void) {
|
|
|
|
/* Check we have an open buffer
|
|
*/
|
|
if (!buffer_active)
|
|
return;
|
|
|
|
/* Remove any previous redirection
|
|
*/
|
|
xosspriteop_switch_output_to_sprite(osspriteop_PTR,
|
|
context1, context2, context3,
|
|
0, 0, 0, 0);
|
|
free(save_area);
|
|
|
|
/* Plot the contents to screen
|
|
*/
|
|
xosspriteop_put_sprite_user_coords(osspriteop_PTR,
|
|
buffer, (osspriteop_id)(buffer + 1),
|
|
clipping.x0, clipping.y0, (os_action)0);
|
|
ro_gui_buffer_free();
|
|
buffer_active = false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Releases any buffer memory depending on cache constraints.
|
|
*/
|
|
static void ro_gui_buffer_free(void) {
|
|
if (buffer == NULL)
|
|
return;
|
|
if (buffer->size > (option_screen_cache << 10)) {
|
|
free(buffer);
|
|
buffer = NULL;
|
|
}
|
|
}
|