/* * Copyright 2004 James Bursa * Copyright 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 . */ /** \file * Page thumbnail creation (implementation). * * Thumbnails are created by redirecting output to a sprite and rendering the * page at a small scale. */ #include #include #include #include "rufl.h" #include "oslib/colourtrans.h" #include "oslib/osfile.h" #include "oslib/osspriteop.h" #include "content/content.h" #include "content/hlcache.h" #include "content/urldb.h" #include "desktop/plotters.h" #include "image/bitmap.h" #include "render/font.h" #include "riscos/bitmap.h" #include "riscos/gui.h" #include "riscos/options.h" #include "riscos/oslib_pre7.h" #include "riscos/thumbnail.h" #include "riscos/tinct.h" #include "utils/log.h" /* Whether we can use 32bpp sprites */ static int thumbnail_32bpp_available = -1; /* Sprite output context saving */ struct thumbnail_save_area { osspriteop_save_area *save_area; int context1; int context2; int context3; }; /* Internal prototypes */ static osspriteop_area *thumbnail_create_8bpp(struct bitmap *bitmap); static void thumbnail_test(void); static struct thumbnail_save_area* thumbnail_switch_output( osspriteop_area *sprite_area, osspriteop_header *sprite_header); static void thumbnail_restore_output(struct thumbnail_save_area *save_area); /** * Create a thumbnail of a page. * * \param content content structure to thumbnail * \param bitmap the bitmap to draw to * \param url the URL the thumbnail belongs to, or NULL */ bool thumbnail_create(hlcache_handle *content, struct bitmap *bitmap, const char *url) { float scale = 1.0; struct thumbnail_save_area *save_area; osspriteop_area *sprite_area = NULL; osspriteop_header *sprite_header = NULL; _kernel_oserror *error; assert(content); assert(bitmap); /* check if we have access to 32bpp sprites natively */ if (thumbnail_32bpp_available == -1) thumbnail_test(); /* if we don't support 32bpp sprites then we redirect to an 8bpp * image and then convert back. */ if (thumbnail_32bpp_available != 1) { sprite_area = thumbnail_create_8bpp(bitmap); if (!sprite_area) return false; sprite_header = (osspriteop_header *)(sprite_area + 1); } else { const uint8_t *pixbufp = bitmap_get_buffer(bitmap); if (!pixbufp || !bitmap->sprite_area) return false; sprite_area = bitmap->sprite_area; sprite_header = (osspriteop_header *)(sprite_area + 1); } /* set up the plotters */ plot = ro_plotters; ro_plot_origin_x = 0; ro_plot_origin_y = bitmap->height * 2; if (content_get_width(content)) scale = (float)bitmap->width / (float)content_get_width(content); ro_plot_set_scale(scale); current_redraw_browser = NULL; /* no selection */ /* switch output and redraw */ save_area = thumbnail_switch_output(sprite_area, sprite_header); if (!save_area) { if (thumbnail_32bpp_available != 1) free(sprite_area); return false; } rufl_invalidate_cache(); colourtrans_set_gcol(os_COLOUR_WHITE, colourtrans_SET_BG_GCOL, os_ACTION_OVERWRITE, 0); os_clg(); content_redraw(content, 0, 0, bitmap->width, bitmap->height, 0, 0, bitmap->width, bitmap->height, scale, 0xFFFFFF); thumbnail_restore_output(save_area); rufl_invalidate_cache(); /* if we changed to 8bpp then go back to 32bpp */ if (thumbnail_32bpp_available != 1) { const uint8_t *pixbufp = bitmap_get_buffer(bitmap); if (!pixbufp || !bitmap->sprite_area) { free(sprite_area); return false; } error = _swix(Tinct_ConvertSprite, _INR(2,3), sprite_header, (osspriteop_header *)(bitmap->sprite_area + 1)); free(sprite_area); if (error) return false; } /* register the thumbnail with the URL */ if (url) urldb_set_thumbnail(url, bitmap); bitmap_modified(bitmap); return true; } /** * Convert a bitmap to 8bpp. * * \param bitmap the bitmap to convert * \return a sprite area containing an 8bpp sprite */ osspriteop_area *thumbnail_convert_8bpp(struct bitmap *bitmap) { struct thumbnail_save_area *save_area; osspriteop_area *sprite_area = NULL; osspriteop_header *sprite_header = NULL; sprite_area = thumbnail_create_8bpp(bitmap); if (!sprite_area) return NULL; sprite_header = (osspriteop_header *)(sprite_area + 1); /* switch output and redraw */ save_area = thumbnail_switch_output(sprite_area, sprite_header); if (save_area == NULL) { if (thumbnail_32bpp_available != 1) free(sprite_area); return false; } _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7), (osspriteop_header *)(bitmap->sprite_area + 1), 0, 0, tinct_ERROR_DIFFUSE); thumbnail_restore_output(save_area); if (sprite_header->image != sprite_header->mask) { /* build the sprite mask from the alpha channel */ void *buf = bitmap_get_buffer(bitmap); unsigned *dp = (unsigned *) buf; if (!dp) return sprite_area; int w = bitmap_get_width(bitmap); int h = bitmap_get_height(bitmap); int dp_offset = bitmap_get_rowstride(bitmap) / 4 - w; int mp_offset = ((sprite_header->width + 1) * 4) - w; byte *mp = (byte*)sprite_header + sprite_header->mask; bool alpha = ((unsigned)sprite_header->mode & 0x80000000U) != 0; while (h-- > 0) { int x = 0; for(x = 0; x < w; x++) { unsigned d = *dp++; if (alpha) *mp++ = (d >> 24) ^ 0xff; else *mp++ = (d < 0xff000000U) ? 0 : 0xff; } dp += dp_offset; mp += mp_offset; } } return sprite_area; } /** * Creates an 8bpp canvas. * * \param bitmap the bitmap to clone the size of * \return a sprite area containing an 8bpp sprite */ osspriteop_area *thumbnail_create_8bpp(struct bitmap *bitmap) { unsigned image_size = ((bitmap->width + 3) & ~3) * bitmap->height; bool opaque = bitmap_get_opaque(bitmap); osspriteop_header *sprite_header = NULL; osspriteop_area *sprite_area = NULL; unsigned area_size; /* clone the sprite */ area_size = sizeof(osspriteop_area) + sizeof(osspriteop_header) + image_size + 2048; if (!opaque) area_size += image_size; sprite_area = (osspriteop_area *)malloc(area_size); if (!sprite_area) { LOG(("no memory for malloc()")); return NULL; } sprite_area->size = area_size; sprite_area->sprite_count = 1; sprite_area->first = 16; sprite_area->used = area_size; sprite_header = (osspriteop_header *)(sprite_area + 1); sprite_header->size = area_size - sizeof(osspriteop_area); memset(sprite_header->name, 0x00, 12); strcpy(sprite_header->name, "bitmap"); sprite_header->left_bit = 0; sprite_header->height = bitmap->height - 1; sprite_header->mode = os_MODE8BPP90X90; sprite_header->right_bit = ((bitmap->width << 3) - 1) & 31; sprite_header->width = ((bitmap->width + 3) >> 2) - 1; sprite_header->image = sizeof(osspriteop_header) + 2048; sprite_header->mask = sizeof(osspriteop_header) + 2048; if (!opaque) sprite_header->mask += image_size; /* create the palette. we don't read the necessary size like * we really should as we know it's going to have 256 entries * of 8 bytes = 2048. */ xcolourtrans_read_palette((osspriteop_area *)os_MODE8BPP90X90, (osspriteop_id)0, (os_palette *)(sprite_header + 1), 2048, (colourtrans_palette_flags)(1 << 1), 0); return sprite_area; } /** * Check to see whether 32bpp sprites are available. * * Rather than using Wimp_ReadSysInfo we test if 32bpp sprites are available * in case the user has a 3rd party patch to enable them. */ static void thumbnail_test(void) { unsigned int area_size; osspriteop_area *sprite_area; /* try to create a 1x1 32bpp sprite */ area_size = sizeof(osspriteop_area) + sizeof(osspriteop_header) + sizeof(int); if ((sprite_area = (osspriteop_area *)malloc(area_size)) == NULL) { LOG(("Insufficient memory to perform sprite test.")); return; } sprite_area->size = area_size + 1; sprite_area->sprite_count = 0; sprite_area->first = 16; sprite_area->used = 16; if (xosspriteop_create_sprite(osspriteop_NAME, sprite_area, "test", false, 1, 1, (os_mode)tinct_SPRITE_MODE)) thumbnail_32bpp_available = 0; else thumbnail_32bpp_available = 1; free(sprite_area); } /** * Switches output to the specified sprite and returns the previous context. */ static struct thumbnail_save_area* thumbnail_switch_output( osspriteop_area *sprite_area, osspriteop_header *sprite_header) { struct thumbnail_save_area *save_area; int size; /* create a save area */ save_area = calloc(sizeof(struct thumbnail_save_area), 1); if (save_area == NULL) return NULL; /* allocate OS_SpriteOp save area */ if (xosspriteop_read_save_area_size(osspriteop_PTR, sprite_area, (osspriteop_id)sprite_header, &size)) { free(save_area); return NULL; } /* create the save area */ save_area->save_area = malloc((unsigned)size); if (save_area->save_area == NULL) { free(save_area); return NULL; } save_area->save_area->a[0] = 0; /* switch output to sprite */ if (xosspriteop_switch_output_to_sprite(osspriteop_PTR, sprite_area, (osspriteop_id)sprite_header, save_area->save_area, 0, &save_area->context1, &save_area->context2, &save_area->context3)) { free(save_area->save_area); free(save_area); return NULL; } return save_area; } /** * Restores output to the specified context, and destroys it. */ static void thumbnail_restore_output(struct thumbnail_save_area *save_area) { /* we don't care if we err, as there's nothing we can do about it */ xosspriteop_switch_output_to_sprite(osspriteop_PTR, (osspriteop_area *)save_area->context1, (osspriteop_id)save_area->context2, (osspriteop_save_area *)save_area->context3, 0, 0, 0, 0); free(save_area->save_area); free(save_area); }