/* * Copyright 2004, 2005 Richard Wilson * * 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 . */ #include #include #include #include #include #include #include #include #include #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; }