/* * Copyright 2004 James Bursa * Copyright 2005 Richard Wilson * Copyright 2008 Adrian Lees * * 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 * Generic bitmap handling (RISC OS implementation). * * This implements the interface given by desktop/bitmap.h using RISC OS * sprites. */ #include #include #include #include #include #include "oslib/osfile.h" #include "oslib/osfind.h" #include "oslib/osgbpb.h" #include "oslib/osspriteop.h" #include "oslib/wimp.h" #include "content/content.h" #include "image/bitmap.h" #include "riscos/bitmap.h" #include "riscos/image.h" #include "desktop/options.h" #include "riscos/palettes.h" #include "riscos/content-handlers/sprite.h" #include "riscos/tinct.h" #include "utils/filename.h" #include "utils/log.h" #include "utils/utils.h" /** Colour in the overlay sprite that allows the bitmap to show through */ #define OVERLAY_INDEX 0xfe #define MAINTENANCE_THRESHOLD 32 /** Size of buffer used when constructing mask data to be saved */ #define SAVE_CHUNK_SIZE 4096 /** The head of the bitmap list */ struct bitmap *bitmap_head = NULL; /** Whether maintenance of the pool states is needed */ bool bitmap_maintenance = false; /** Whether maintenance of the pool is high priority */ bool bitmap_maintenance_priority = false; /** Maximum amount of memory for direct images */ unsigned int bitmap_direct_size; /** Current amount of memory for direct images */ unsigned int bitmap_direct_used = 0; /** Total size of compressed area */ unsigned int bitmap_compressed_size; /** Total size of compressed area */ unsigned int bitmap_compressed_used = 0; /** Compressed data header */ struct bitmap_compressed_header { int width; int height; char name[12]; unsigned int flags; unsigned int input_size; }; char bitmap_unixname[256]; char bitmap_filename[256]; static bool bitmap_initialise(struct bitmap *bitmap); static void bitmap_decompress(struct bitmap *bitmap); static void bitmap_compress(struct bitmap *bitmap); static void bitmap_load_file(struct bitmap *bitmap); static void bitmap_save_file(struct bitmap *bitmap); static void bitmap_delete_file(struct bitmap *bitmap); /** * Initialise the bitmap memory pool. */ void bitmap_initialise_memory(void) { int available_memory, direct_size, compressed_size; int free_slot; os_error *error; /* calculate how much memory is currently free (Note that the free_slot returned by wimp_slot_size includes the next_slot; the value displayed by the TaskManager has been adjusted to make it more logical for the user). */ error = xwimp_slot_size(-1, -1, NULL, NULL, &free_slot); if (error) { LOG(("xwimp_slot_size: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return; } available_memory = free_slot; /* calculate our memory block sizes */ if (nsoption_int(image_memory_direct) == -1) { /* claim 25% of free memory - min 256KB, max 32768KB */ direct_size = available_memory / 4; if (direct_size < (256 << 10)) direct_size = (256 << 10); if (direct_size > (32768 << 10)) direct_size = (32768 << 10); } else { direct_size = (nsoption_int(image_memory_direct) << 10); } if (nsoption_int(image_memory_compressed) == -1) { /* claim 10% of free memory - min 256KB, max 4192KB */ compressed_size = available_memory / 10; if (compressed_size < (256 << 10)) compressed_size = 0; if (compressed_size > (4192 << 10)) compressed_size = (4192 << 10); } else { compressed_size = (nsoption_int(image_memory_compressed) << 10); } /* set our values. No fixed buffers here, ho hum. */ bitmap_direct_size = direct_size; bitmap_compressed_size = compressed_size; bitmap_maintenance = bitmap_maintenance_priority = true; } /** * Prepare for the end of a session. */ void bitmap_quit(void) { struct bitmap *bitmap; for (bitmap = bitmap_head; bitmap; bitmap = bitmap->next) if ((bitmap->state & BITMAP_PERSISTENT) && ((bitmap->state & BITMAP_MODIFIED) || (bitmap->filename[0] == '\0'))) bitmap_save_file(bitmap); } /** * Create a bitmap. * * \param width width of image in pixels * \param height width of image in pixels * \param clear whether to clear the image ready for use * \return an opaque struct bitmap, or NULL on memory exhaustion */ void *bitmap_create(int width, int height, unsigned int state) { struct bitmap *bitmap; if (width == 0 || height == 0) return NULL; bitmap = calloc(1, sizeof(struct bitmap)); if (!bitmap) return NULL; bitmap->width = width; bitmap->height = height; bitmap->state = state; /* link into our list of bitmaps at the head */ if (bitmap_head) { bitmap->next = bitmap_head; bitmap_head->previous = bitmap; } bitmap_head = bitmap; return bitmap; } /** * Create a persistent, opaque bitmap from a file reference. * * \param file the file containing the image data * \return an opaque struct bitmap, or NULL on memory exhaustion */ struct bitmap *bitmap_create_file(char *file) { struct bitmap *bitmap; char *r; fileswitch_object_type obj_type; os_error *error; if (file[0] == '\0') return NULL; /* check the file exists */ sprintf(bitmap_unixname, "%s/%s", TEMP_FILENAME_PREFIX, file); r = __riscosify(bitmap_unixname, 0, __RISCOSIFY_NO_SUFFIX, bitmap_filename, 256, 0); if (r == 0) { LOG(("__riscosify failed")); return NULL; } error = xosfile_read_stamped_no_path(bitmap_filename, &obj_type, 0, 0, 0, 0, 0); if ((error) || (obj_type != fileswitch_IS_FILE)) return NULL; if (!filename_claim(file)) return NULL; bitmap = calloc(1, sizeof(struct bitmap)); if (!bitmap) return NULL; bitmap->state = BITMAP_OPAQUE | BITMAP_PERSISTENT | BITMAP_READY; strcpy(bitmap->filename, file); /* link in at the head */ if (bitmap_head) { bitmap->next = bitmap_head; bitmap_head->previous = bitmap; } bitmap_head = bitmap; return bitmap; } /** * Overlay a sprite onto the given bitmap * * \param bitmap bitmap object * \param s 8bpp sprite to be overlayed onto bitmap */ void bitmap_overlay_sprite(struct bitmap *bitmap, const osspriteop_header *s) { const os_colour *palette; const byte *sp, *mp; bool masked = false; bool alpha = false; os_error *error; int dp_offset; int sp_offset; unsigned *dp; int x, y; int w, h; assert(sprite_bpp(s) == 8); if ((unsigned)s->mode & 0x80000000U) alpha = true; error = xosspriteop_read_sprite_info(osspriteop_PTR, (osspriteop_area *)0x100, (osspriteop_id)s, &w, &h, NULL, NULL); if (error) { LOG(("xosspriteop_read_sprite_info: 0x%x:%s", error->errnum, error->errmess)); return; } sp_offset = ((s->width + 1) * 4) - w; if (w > bitmap->width) w = bitmap->width; if (h > bitmap->height) h = bitmap->height; dp_offset = bitmap_get_rowstride(bitmap) / 4; dp = (void*)bitmap_get_buffer(bitmap); if (!dp) return; sp = (byte*)s + s->image; mp = (byte*)s + s->mask; sp += s->left_bit / 8; mp += s->left_bit / 8; if (s->image > (int)sizeof(*s)) palette = (os_colour*)(s + 1); else palette = default_palette8; if (s->mask != s->image) { masked = true; bitmap_set_opaque(bitmap, false); } /* (partially-)transparent pixels in the overlayed sprite retain * their transparency in the output bitmap; opaque sprite pixels * are also propagated to the bitmap, except those which are the * OVERLAY_INDEX colour which allow the original bitmap contents to * show through */ for (y = 0; y < h; y++) { unsigned *sdp = dp; for(x = 0; x < w; x++) { os_colour d = ((unsigned)palette[(*sp) << 1]) >> 8; if (*sp++ == OVERLAY_INDEX) d = *dp; if (masked) { if (alpha) d |= ((*mp << 24) ^ 0xff000000U); else if (*mp) d |= 0xff000000U; } *dp++ = d; mp++; } dp = sdp + dp_offset; sp += sp_offset; mp += sp_offset; } } /** * Initialise a bitmaps sprite area. * * \param bitmap the bitmap to initialise * \param clear whether to clear the image ready for use */ bool bitmap_initialise(struct bitmap *bitmap) { unsigned int area_size; osspriteop_area *sprite_area; osspriteop_header *sprite; assert(!bitmap->sprite_area); area_size = 16 + 44 + bitmap->width * bitmap->height * 4; if (bitmap->state & BITMAP_CLEAR_MEMORY) bitmap->sprite_area = calloc(1, area_size); else bitmap->sprite_area = malloc(area_size); if (!bitmap->sprite_area) return false; bitmap->state |= BITMAP_READY; bitmap_direct_used += area_size; /* area control block */ sprite_area = bitmap->sprite_area; sprite_area->size = area_size; sprite_area->sprite_count = 1; sprite_area->first = 16; sprite_area->used = area_size; /* sprite control block */ sprite = (osspriteop_header *) (sprite_area + 1); sprite->size = area_size - 16; memset(sprite->name, 0x00, 12); strncpy(sprite->name, "bitmap", 12); sprite->width = bitmap->width - 1; sprite->height = bitmap->height - 1; sprite->left_bit = 0; sprite->right_bit = 31; sprite->image = sprite->mask = 44; sprite->mode = tinct_SPRITE_MODE; bitmap_maintenance = true; bitmap_maintenance_priority |= (bitmap_direct_used > bitmap_direct_size * 0.9); return true; } /** * Sets whether a bitmap should be plotted opaque * * \param bitmap a bitmap, as returned by bitmap_create() * \param opaque whether the bitmap should be plotted opaque */ void bitmap_set_opaque(void *vbitmap, bool opaque) { struct bitmap *bitmap = (struct bitmap *) vbitmap; assert(bitmap); if (opaque) bitmap->state |= BITMAP_OPAQUE; else bitmap->state &= ~BITMAP_OPAQUE; } /** * Tests whether a bitmap has an opaque alpha channel * * \param bitmap a bitmap, as returned by bitmap_create() * \return whether the bitmap is opaque */ bool bitmap_test_opaque(void *vbitmap) { struct bitmap *bitmap = (struct bitmap *) vbitmap; unsigned char *sprite; unsigned int width, height, size; osspriteop_header *sprite_header; unsigned *p, *ep; assert(bitmap); sprite = bitmap_get_buffer(bitmap); if (!sprite) return false; width = bitmap_get_rowstride(bitmap); sprite_header = (osspriteop_header *) (bitmap->sprite_area + 1); height = (sprite_header->height + 1); size = width * height; p = (void *) sprite; ep = (void *) (sprite + (size & ~31)); while (p < ep) { /* \todo prefetch(p, 128)? */ if (((p[0] & p[1] & p[2] & p[3] & p[4] & p[5] & p[6] & p[7]) & 0xff000000U) != 0xff000000U) return false; p += 8; } ep = (void *) (sprite + size); while (p < ep) { if ((*p & 0xff000000U) != 0xff000000U) return false; p++; } return true; } /** * Gets whether a bitmap should be plotted opaque * * \param bitmap a bitmap, as returned by bitmap_create() */ bool bitmap_get_opaque(void *vbitmap) { struct bitmap *bitmap = (struct bitmap *) vbitmap; assert(bitmap); return (bitmap->state & BITMAP_OPAQUE); } /** * Return a pointer to the pixel data in a bitmap. * * \param bitmap a bitmap, as returned by bitmap_create() * \return pointer to the pixel buffer * * The pixel data is packed as BITMAP_FORMAT, possibly with padding at the end * of rows. The width of a row in bytes is given by bitmap_get_rowstride(). */ unsigned char *bitmap_get_buffer(void *vbitmap) { struct bitmap *bitmap = (struct bitmap *) vbitmap; assert(bitmap); /* move to the head of the list */ if (bitmap_head != bitmap) { if (bitmap->previous) bitmap->previous->next = bitmap->next; if (bitmap->next) bitmap->next->previous = bitmap->previous; bitmap->next = bitmap_head; bitmap_head->previous = bitmap; bitmap->previous = NULL; bitmap_head = bitmap; } /* dynamically create the buffer */ if (!(bitmap->state & BITMAP_READY)) { if (!bitmap_initialise(bitmap)) return NULL; } /* image is already decompressed, no change to image states */ if (bitmap->sprite_area) return ((unsigned char *) (bitmap->sprite_area)) + 16 + 44; /* load and/or decompress the image */ if (bitmap->filename[0]) bitmap_load_file(bitmap); if (bitmap->compressed) bitmap_decompress(bitmap); bitmap_maintenance = true; bitmap_maintenance_priority |= (bitmap_direct_used > bitmap_direct_size * 0.9); if (bitmap->sprite_area) return ((unsigned char *) (bitmap->sprite_area)) + 16 + 44; return NULL; } /** * Find the width of a pixel row in bytes. * * \param bitmap a bitmap, as returned by bitmap_create() * \return width of a pixel row in the bitmap */ size_t bitmap_get_rowstride(void *vbitmap) { struct bitmap *bitmap = (struct bitmap *) vbitmap; return bitmap->width * 4; } /** * Free a bitmap. * * \param bitmap a bitmap, as returned by bitmap_create() */ void bitmap_destroy(void *vbitmap) { struct bitmap *bitmap = (struct bitmap *) vbitmap; struct bitmap_compressed_header *header; unsigned int area_size; assert(bitmap); /* delink from list */ bitmap_maintenance = true; if (bitmap_head == bitmap) bitmap_head = bitmap->next; if (bitmap->previous) bitmap->previous->next = bitmap->next; if (bitmap->next) bitmap->next->previous = bitmap->previous; /* destroy bitmap */ if (bitmap->sprite_area) { area_size = 16 + 44 + bitmap->width * bitmap->height * 4; bitmap_direct_used -= area_size; free(bitmap->sprite_area); } if (bitmap->compressed) { header = (void *) bitmap->compressed; bitmap_compressed_used -= header->input_size + sizeof(struct bitmap_compressed_header); free(bitmap->compressed); } if (bitmap->filename[0]) bitmap_delete_file(bitmap); free(bitmap); } /** * Save a bitmap in the platform's native format. * * \param bitmap a bitmap, as returned by bitmap_create() * \param path pathname for file * \param flags modify the behaviour of the save * \return true on success, false on error and error reported */ bool bitmap_save(void *vbitmap, const char *path, unsigned flags) { struct bitmap *bitmap = (struct bitmap *) vbitmap; os_error *error; if (!bitmap->sprite_area) bitmap_get_buffer(bitmap); if (!bitmap->sprite_area) return false; if (bitmap_get_opaque(bitmap)) { error = xosspriteop_save_sprite_file(osspriteop_USER_AREA, (bitmap->sprite_area), path); if (error) { LOG(("xosspriteop_save_sprite_file: 0x%x: %s", error->errnum, error->errmess)); warn_user("SaveError", error->errmess); return false; } return true; } else { /* to make the saved sprite useful we must convert from 'Tinct' * format to either a bi-level mask or a Select-style full * alpha channel */ osspriteop_area *area = bitmap->sprite_area; osspriteop_header *hdr = (void *) ((char *) area + area->first); unsigned width = hdr->width + 1, height = hdr->height + 1; unsigned image_size = height * width * 4; unsigned char *chunk_buf; unsigned *p, *elp, *eip; unsigned mask_size; size_t chunk_pix; struct { osspriteop_area area; osspriteop_header hdr; } file_hdr; os_fw fw; /* we only support 32bpp sprites */ if ((((unsigned)hdr->mode >> 27)&15) != 6) { assert(!"Unsupported sprite format in bitmap_save"); return false; } chunk_buf = malloc(SAVE_CHUNK_SIZE); if (!chunk_buf) { warn_user("NoMemory", NULL); return false; } file_hdr.area = *area; file_hdr.hdr = *hdr; if (flags & BITMAP_SAVE_FULL_ALPHA) { mask_size = ((width + 3) & ~3) * height; chunk_pix = SAVE_CHUNK_SIZE; file_hdr.hdr.mode = (os_mode)((unsigned)file_hdr.hdr.mode | (1U<<31)); } else { mask_size = (((width + 31) & ~31)/8) * height; chunk_pix = SAVE_CHUNK_SIZE<<3; file_hdr.hdr.mode = (os_mode)((unsigned)file_hdr.hdr.mode & ~(1U<<31)); } file_hdr.area.sprite_count = 1; file_hdr.area.first = sizeof(file_hdr.area); file_hdr.area.used = sizeof(file_hdr) + image_size + mask_size; file_hdr.hdr.image = sizeof(file_hdr.hdr); file_hdr.hdr.mask = file_hdr.hdr.image + image_size; file_hdr.hdr.size = file_hdr.hdr.mask + mask_size; error = xosfind_openoutw(0, path, NULL, &fw); if (error) { LOG(("xosfind_openoutw: 0x%x: %s", error->errnum, error->errmess)); free(chunk_buf); warn_user("SaveError", error->errmess); return false; } p = (void *) ((char *) hdr + hdr->image); /* write out the area header, sprite header and image data */ error = xosgbpb_writew(fw, (byte*)&file_hdr + 4, sizeof(file_hdr)-4, NULL); if (!error) error = xosgbpb_writew(fw, (byte*)p, image_size, NULL); if (error) { LOG(("xosgbpb_writew: 0x%x: %s", error->errnum, error->errmess)); free(chunk_buf); xosfind_closew(fw); warn_user("SaveError", error->errmess); return false; } /* then write out the mask data in chunks */ eip = p + (width * height); /* end of image */ elp = p + width; /* end of line */ while (p < eip) { unsigned char *dp = chunk_buf; unsigned *ep = p + chunk_pix; if (ep > elp) ep = elp; if (flags & BITMAP_SAVE_FULL_ALPHA) { while (p < ep) { *dp++ = ((unsigned char*)p)[3]; p++; } } else { unsigned char mb = 0; int msh = 0; while (p < ep) { if (((unsigned char*)p)[3]) mb |= (1 << msh); if (++msh >= 8) { *dp++ = mb; msh = 0; mb = 0; } p++; } if (msh > 0) *dp++ = mb; } if (p >= elp) { /* end of line yet? */ /* align to word boundary */ while ((int)dp & 3) *dp++ = 0; /* advance end of line pointer */ elp += width; } error = xosgbpb_writew(fw, (byte*)chunk_buf, dp-chunk_buf, NULL); if (error) { LOG(("xosgbpb_writew: 0x%x: %s", error->errnum, error->errmess)); free(chunk_buf); xosfind_closew(fw); warn_user("SaveError", error->errmess); return false; } } error = xosfind_closew(fw); if (error) { LOG(("xosfind_closew: 0x%x: %s", error->errnum, error->errmess)); warn_user("SaveError", error->errmess); } error = xosfile_set_type(path, osfile_TYPE_SPRITE); if (error) { LOG(("xosfile_set_type: 0x%x: %s", error->errnum, error->errmess)); warn_user("SaveError", error->errmess); } free(chunk_buf); return true; } } /** * The bitmap image has changed, so flush any persistent cache. * * \param bitmap a bitmap, as returned by bitmap_create() */ void bitmap_modified(void *vbitmap) { struct bitmap *bitmap = (struct bitmap *) vbitmap; bitmap->state |= BITMAP_MODIFIED; } /** * Performs routine maintenance. */ void bitmap_maintain(void) { unsigned int memory = 0; unsigned int compressed_memory = 0; struct bitmap *bitmap = bitmap_head; struct bitmap_compressed_header *header; unsigned int maintain_direct_size; LOG(("Performing maintenance.")); /* under heavy loads allow an extra 30% to work with */ maintain_direct_size = bitmap_direct_size; if (!bitmap_maintenance_priority) maintain_direct_size = maintain_direct_size * 0.7; if ((!bitmap) || ((bitmap_direct_used < maintain_direct_size) && (bitmap_compressed_used < bitmap_compressed_size))) { bitmap_maintenance = bitmap_maintenance_priority; bitmap_maintenance_priority = false; return; } /* we don't change the first bitmap_MEMORY entries as they * will automatically be loaded/decompressed from whatever state * they are in when neeeded. */ for (; bitmap && (memory < maintain_direct_size); bitmap = bitmap->next) { if (bitmap->sprite_area) memory += bitmap->width * bitmap->height * 4; else if ((bitmap->compressed) && (!bitmap_maintenance_priority)) { header = (void *) bitmap->compressed; compressed_memory += header->input_size + sizeof(struct bitmap_compressed_header); } } if (!bitmap) { bitmap_maintenance = bitmap_maintenance_priority; bitmap_maintenance_priority = false; return; } /* under heavy loads, we ignore compression */ if (!bitmap_maintenance_priority) { /* for the next section, up until bitmap_COMPRESSED we * forcibly compress the data if it's currently held directly * in memory */ for (; bitmap && (compressed_memory < bitmap_compressed_size); bitmap = bitmap->next) { if (bitmap->sprite_area) { if ((bitmap->width * bitmap->height) <= (512 * 512)) bitmap_compress(bitmap); else bitmap_save_file(bitmap); return; } if (bitmap->compressed) { header = (void *) bitmap->compressed; compressed_memory += header->input_size + sizeof(struct bitmap_compressed_header); } } if (!bitmap) { bitmap_maintenance = false; return; } } /* for the remaining entries we dump to disk */ for (; bitmap; bitmap = bitmap->next) { if ((bitmap->sprite_area) || (bitmap->compressed)) { if (bitmap_maintenance_priority) { if (bitmap->sprite_area) bitmap_save_file(bitmap); } else { bitmap_save_file(bitmap); return; } } } bitmap_maintenance = bitmap_maintenance_priority; bitmap_maintenance_priority = false; } void bitmap_decompress(struct bitmap *bitmap) { unsigned int area_size; _kernel_oserror *error; int output_size; struct bitmap_compressed_header *header; assert(bitmap->compressed); /* ensure the width/height is correct */ header = (void *)bitmap->compressed; if ((header->width != bitmap->width) || (header->height != bitmap->height)) { LOG(("Warning: Mismatch between bitmap and compressed sizes")); return; } /* create the image memory/header to decompress to */ if (!bitmap_initialise(bitmap)) return; /* decompress the data */ output_size = bitmap->width * bitmap->height * 4 + sizeof(struct osspriteop_header); error = _swix(Tinct_Decompress, _IN(0) | _IN(2) | _IN(3) | _IN(7), bitmap->compressed, (char *)(bitmap->sprite_area + 1), output_size, 0); if (error) { LOG(("Decompression error")); free(bitmap->sprite_area); bitmap->sprite_area = NULL; } else { LOG(("Decompressed")); area_size = header->input_size + sizeof(struct bitmap_compressed_header); bitmap_compressed_used -= area_size; free(bitmap->compressed); bitmap->compressed = NULL; area_size = 16 + 44 + bitmap->width * bitmap->height * 4; bitmap_direct_used += area_size; } } void bitmap_compress(struct bitmap *bitmap) { unsigned int area_size; _kernel_oserror *error; char *output; unsigned int output_size, new_size; unsigned int flags = 0; float calc; /* get the maximum output size (33/32 * size) */ output_size = ((bitmap->width * bitmap->height * 4 * 33) >> 5) + sizeof(struct bitmap_compressed_header); output = malloc(output_size); if (!output) return; /* compress the data */ if (bitmap->state & BITMAP_OPAQUE) flags |= tinct_OPAQUE_IMAGE; error = _swix(Tinct_Compress, _IN(0) | _IN(2) | _IN(7) | _OUT(0), (char *)(bitmap->sprite_area + 1), output, flags, &new_size); if (error) { LOG(("Compression error")); free(output); } else { bitmap->compressed = realloc(output, new_size); if (!bitmap->compressed) { free(output); } else { bitmap_compressed_used += new_size; if (bitmap->sprite_area) { area_size = 16 + 44 + bitmap->width * bitmap->height * 4; bitmap_direct_used -= area_size; free(bitmap->sprite_area); } bitmap->sprite_area = NULL; calc = (100 / (float)output_size) * new_size; LOG(("Compression: %i->%i, %.3f%%", output_size, new_size, calc)); } } } void bitmap_load_file(struct bitmap *bitmap) { int len; fileswitch_object_type obj_type; os_error *error; char *r; struct bitmap_compressed_header *bitmap_compressed; osspriteop_header *bitmap_direct; int *data; assert(bitmap->filename); sprintf(bitmap_unixname, "%s/%s", TEMP_FILENAME_PREFIX, bitmap->filename); r = __riscosify(bitmap_unixname, 0, __RISCOSIFY_NO_SUFFIX, bitmap_filename, 256, 0); if (r == 0) { LOG(("__riscosify failed")); return; } error = xosfile_read_stamped_no_path(bitmap_filename, &obj_type, 0, 0, &len, 0, 0); if ((error) || (obj_type != fileswitch_IS_FILE)) return; bitmap->compressed = malloc(len); if (!bitmap->compressed) return; error = xosfile_load_stamped_no_path(bitmap_filename, (byte *) bitmap->compressed, 0, 0, 0, 0, 0); if (error) { free(bitmap->compressed); bitmap->compressed = NULL; return; } data = (void *) bitmap->compressed; LOG(("Loaded file from disk")); /* Sanity check the file we've just loaded: * If it's an uncompressed buffer, then it's a raw sprite area, * including the total size word at the start. Therefore, we check * that: * a) The declared total area size == file length * b) The offset to the first free word == file length * c) There is only 1 sprite in the area * d) The name of the sprite in the area is "bitmap" * * If it's a compressed buffer, then we check that: * a) The declared input size + header size == file length * b) The name of the buffer is "bitmap" * * If it's neither of these, we fail. */ if (*data == len && *(data + 3) == len && *(data + 1) == 1 && strncmp(bitmap->compressed + 20, "bitmap", 6) == 0) { bitmap->sprite_area = (void *) bitmap->compressed; bitmap->compressed = NULL; bitmap_direct = (osspriteop_header *)(bitmap->sprite_area + 1); bitmap->width = bitmap_direct->width + 1; bitmap->height = bitmap_direct->height + 1; bitmap_direct_used += 16 + 44 + bitmap->width * bitmap->height * 4; } else if ((int) (*(data + 6) + sizeof(struct bitmap_compressed_header)) == len && strncmp(bitmap->compressed + 8, "bitmap", 6) == 0) { bitmap_compressed = (void *) bitmap->compressed; bitmap_compressed_used -= bitmap_compressed->input_size + sizeof(struct bitmap_compressed_header); bitmap->width = bitmap_compressed->width; bitmap->height = bitmap_compressed->height; } else { free(bitmap->compressed); bitmap->compressed = NULL; return; } if (bitmap->state & BITMAP_MODIFIED) bitmap_delete_file(bitmap); } void bitmap_save_file(struct bitmap *bitmap) { unsigned int area_size; const char *filename; char *r; os_error *error; struct bitmap_compressed_header *header; assert(bitmap); if (!bitmap->compressed && !bitmap->sprite_area) { LOG(("bitmap has no data")); return; } /* unmodified bitmaps will still have their file available */ if ((!(bitmap->state & BITMAP_MODIFIED)) && bitmap->filename[0]) { if (bitmap->sprite_area) free(bitmap->sprite_area); bitmap->sprite_area = NULL; if (bitmap->compressed) free(bitmap->compressed); bitmap->compressed = NULL; return; } /* dump the data (compressed or otherwise) to disk */ filename = filename_request(); if (!filename) { LOG(("filename_request failed")); return; } strcpy(bitmap->filename, filename); sprintf(bitmap_unixname, "%s/%s", TEMP_FILENAME_PREFIX, bitmap->filename); r = __riscosify(bitmap_unixname, 0, __RISCOSIFY_NO_SUFFIX, bitmap_filename, 256, 0); if (r == 0) { LOG(("__riscosify failed")); return; } if (bitmap->compressed) { header = (void *) bitmap->compressed; area_size = header->input_size + sizeof(struct bitmap_compressed_header); error = xosfile_save_stamped(bitmap_filename, 0xffd, (byte *) bitmap->compressed, (byte *) bitmap->compressed + area_size); } else { area_size = bitmap->width * bitmap->height * 4 + sizeof(osspriteop_header) + sizeof(osspriteop_area); error = xosfile_save_stamped(bitmap_filename, 0xffd, (byte *) bitmap->sprite_area, ((byte *) bitmap->sprite_area) + area_size); } if (error) { LOG(("xosfile_save_stamped: 0x%x: %s", error->errnum, error->errmess)); bitmap->filename[0] = 0; } else { if (bitmap->sprite_area) { bitmap_direct_used -= area_size; free(bitmap->sprite_area); } bitmap->sprite_area = NULL; if (bitmap->compressed) { bitmap_compressed_used -= area_size; free(bitmap->compressed); } bitmap->compressed = NULL; bitmap->state &= ~BITMAP_MODIFIED; LOG(("Saved file to disk")); } } void bitmap_delete_file(struct bitmap *bitmap) { assert(bitmap->filename[0]); filename_release(bitmap->filename); bitmap->filename[0] = 0; } int bitmap_get_width(void *vbitmap) { struct bitmap *bitmap = (struct bitmap *) vbitmap; return bitmap->width; } int bitmap_get_height(void *vbitmap) { struct bitmap *bitmap = (struct bitmap *) vbitmap; return bitmap->height; } /** * Find the bytes per pixel of a bitmap * * \param vbitmap a bitmap, as returned by bitmap_create() * \return bytes per pixel */ size_t bitmap_get_bpp(void *vbitmap) { struct bitmap *bitmap = (struct bitmap *)vbitmap; assert(bitmap); return 4; }