/* Copyright 1997 Acorn Computers Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /***************************************************/ /* File : Images.c */ /* */ /* Purpose: Image related functions. */ /* */ /* Author : Merlyn Kline for Customer browser */ /* This source adapted by A.D.Hodgkinson */ /* */ /* History: 28-Nov-96: Created with dummy function */ /* for temporary use in other */ /* routines. */ /* 20-Jan-97: Filled in with most of the */ /* functions present and */ /* converted to the new data */ /* structures where needed. */ /* 15-Mar-97: Reorganised a bit, putting */ /* functions in a more logical */ /* order to aid legibility. */ /* 17-Dec-97: Added support for saving as */ /* a Draw file. */ /***************************************************/ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <math.h> #include "swis.h" #include "flex.h" #include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */ #include "wimp.h" #include "wimplib.h" #include "event.h" #include "toolbox.h" #include "svcprint.h" #include "Global.h" #include "FromROSLib.h" #include "MiscDefs.h" #include "Utils.h" #include "Browser.h" #include "Fetch.h" /* (Which itself includes URLstat.h) */ #include "FetchHTML.h" #include "FetchPage.h" #include "FontManage.h" #include "ImgHistory.h" #include "PrintStyle.h" #include "Protocols.h" #include "Redraw.h" #include "Reformat.h" #include "Save.h" #include "SaveDraw.h" #include "TokenUtils.h" #include "Toolbars.h" #include "URLutils.h" #include "URLveneer.h" #include "Windows.h" #include "Images.h" /* Statics */ static _kernel_oserror * image_load_chunk (browser_data * b, int image, char * buffer, int bytes); static void image_remove_data (browser_data * b, int image); static void image_remove_all_data (browser_data * b, int image); static void image_delay (browser_data * b, int image); static void image_abandon (browser_data * b, int image); static void image_delete_image_entry (int image); static void image_refetch (browser_data * b, int image, int priority, int redraw); static _kernel_oserror * image_update_area (browser_data * b, int x, int y, BBox * ubox, int image, int redraw); static _kernel_oserror * image_update_image (browser_data * b, int image, BBox * box); static _kernel_oserror * image_register_filler (int xref); static int image_data_offset (browser_data * b, int image); static int image_count_fetches (browser_data * b); static int image_get_token_image_xref (browser_data * b, HStream * token); static int image_get_token_image_actual (browser_data * b, HStream * token); static void image_lock_image_size (browser_data * b, int image); static void image_unlock_image_size (browser_data * b, int image); static _kernel_oserror * image_get_image_size (browser_data * b, int image, BBox * box, int ignore_stored); static _kernel_oserror * image_set_image_size (browser_data * b, int image, BBox * box); static int image_can_be_saved_as_sprite (browser_data * b, int i); /* Locals */ static image_info * idata = NULL; static char * ddata = NULL; static int nimages = 0; static int lastimage = 0; static int animhandler = 0; static char * unique_name = NULL; /* For animated images, the filler function for the background of a */ /* transparent image will get called only for the main image with the */ /* associated image data - not for any cross referenced images. These */ /* statics hold the browser and image being redrawn at any time, so */ /* the filler function knows which cross referencing image is */ /* actually being redrawn. */ static int image_redrawing = -1; static browser_data * browser_redrawing = NULL; /* Local compilation options */ #undef TRACE_FETCH_STORE // Unimplemented functions: // /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ // static _kernel_oserror * image_savesprite_start_save1(image_info * ip,int * isize) // { // int size; // // * isize=-1; // if(ip && ip->istore && ip->istore->width>0) // { // ERROUT((_kernel_oserror*)ip->istore->StartExport(ip->istore,&size)); // size-=12; /* subtract the size of the sprite area header */ // } // else // { // sprite_header * p; // sprite_area * sa; // sprite_id sid; // // image_get_broken_sprite(&sa,&sid); // p=(sprite_header*)sid.s.addr; // size=WORDALIGN(p->next); // } // * isize=size; // return(NULL); // } // // /* ----------------------------------------------------------------------*/ // _kernel_oserror * image_savesprite_start_save(browser_data * b,int token,int * isize) // { // image_info * ip; // // ip=image_get_token_image_xref(b,token); // return(image_savesprite_start_save1(ip,isize)); // } // // // /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ // static int image_savesprite_save_image1(image_info * ip) // { // sprite_header * p; // sprite_area * sa; // sprite_id sid; // // if(ip && ip->istore && ip->istore->width>0) // { // int len; // char buffer[2048]; // int skip=12; /* skip first 12 bytes (sprite area header) */ // // do // { // len=sizeof(buffer); // if(wimpt_complain((_kernel_oserror*)ip->istore->Export(ip->istore,buffer,&len))) return(0); // if(len-skip>0 && !save_write_bytes(buffer+skip,len-skip)) return(0); // skip-=len; // if(skip<0) skip=0; // } while(len); // return(1); // } // image_get_broken_sprite(&sa,&sid); // p=(sprite_header*)sid.s.addr; // return(save_write_bytes((void*)p,WORDALIGN(p->next))); // } // // /* ----------------------------------------------------------------------*/ // int image_savesprite_save_image(browser_data * b,int token) // { // image_info * ip; // // ip=image_get_token_image_xref(b,token); // return(image_savesprite_save_image1(ip)); // } // // /* ----------------------------------------------------------------------*/ // void image_savesprite_end_save(browser_data * b,int token) // { // image_info * ip; // // ip=image_get_token_image_xref(b,token); // if(ip && ip->istore && ip->istore->width>0) ip->istore->EndExport(ip->istore); // } // // /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ // static int image_picture_saver(void * handle,char * pathname) // { // browser * b; // int * si,token; // image_info * ip; // // pathname=pathname; // si=(int*)handle; // b=(browser*)si[0]; // token=si[1]; // ip=NULL; // if(token>0) ip=image_get_token_image_xref(b,token); // else if(b->background_image>0) ip=image_info_addr(b,b->background_image); // if(ip && ip->istore && ip->istore->width>0) // { // int len; // char buffer[2048]; // // do // { // len=sizeof(buffer); // if(wimpt_complain((_kernel_oserror*)ip->istore->Export(ip->istore,buffer,&len))) return(0); // if(len && !save_write_bytes(buffer,len)) return(0); // } while(len); // } // else // { // sprite_area area,* sa; // sprite_id sid; // sprite_header * p; // // image_get_broken_sprite(&sa,&sid); // p=(sprite_header*)sid.s.addr; // area.size=sizeof(sprite_area)+p->next; // area.number=1; // area.sproff=sizeof(sprite_area); // area.freeoff=area.size; // if(!save_write_bytes((void*)(&area.number),sizeof(area)-4)) return(0); // return(save_write_bytes((void*)p,p->next)); // } // return(1); // } // // /* ----------------------------------------------------------------------*/ // _kernel_oserror * image_save_picture(browser_data * b,int token) // { // int size,saveinfo[2]; // char pathname[256]; // image_info * ip; // // ip=NULL; // if(token && fetch_token_address(b,token)->style&IMG) ip=image_get_token_image_xref(b,token); // if(!ip && b->background_image>0) // { // ip=image_info_addr(b,b->background_image); // token=0; // } // if(ip && ip->istore && ip->istore->width>0) ip->istore->StartExport(ip->istore,&size); // else // { // sprite_area * sa; // sprite_id sid; // sprite_header * p; // // image_get_broken_sprite(&sa,&sid); // p=(sprite_header*)sid.s.addr; // size=sizeof(sprite_area)+p->next-4; // } // saveinfo[0]=(int)b; // saveinfo[1]=token; // strcpy(pathname,msgs_lookup("sprfile:SpriteFile")); // save_saveas(FILETYPE_SPRITE,pathname,size,image_picture_saver,NULL,(void*)saveinfo); // if(ip && ip->istore && ip->istore->width>0) ip->istore->EndExport(ip->istore); // return(NULL); // } // /* ----------------------------------------------------------------------*/ // _kernel_oserror * image_saveback_start_save(browser_data * b,int * isize) // { // image_info * ip; // // ip=image_info_addr(b,b->background_image); // return(image_savesprite_start_save1(ip,isize)); // } // // /* ----------------------------------------------------------------------*/ // int image_saveback_save_image(browser_data * b) // { // image_info * ip; // // ip=image_info_addr(b,b->background_image); // return(image_savesprite_save_image1(ip)); // } // // /* ----------------------------------------------------------------------*/ // void image_saveback_end_save(browser_data * b) // { // image_info * ip; // // ip=image_info_addr(b,b->background_image); // if(ip && idata[i].istore && idata[i].istore->width>0) idata[i].istore->EndExport(idata[i].istore); // } // **** static const char * image_get_url(HStream * t) // **** { // **** if ISBODY(t) // **** { // **** if (t->tagno == TAG_TABLE) return NULL; // **** else if (t->style & IMG) return t->src; // **** else if ISOBJECT(t) return NULL; // Eeek // **** else if (t->style & FORM) // **** { // **** if (t->tagno == TAG_INPUT) // **** { // **** switch (HtmlINPUTtype(t)) // **** { // **** case inputtype_IMAGE: return HtmlINPUTsrc(t); // **** default: return NULL; // **** } // **** } // **** else return NULL; // **** } // **** } // **** else if ISFRAMESET(t) return NULL; // **** else if ISHEAD(t) // **** { // **** if (t->tag == BODY) return HtmlBODYbackground(t); // **** } // **** // **** return NULL; // **** } /*************************************************/ /* image_load_chunk() */ /* */ /* Tell the image library to deal with some of */ /* image data we've fetched for a given image. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* The image number, from 0 to */ /* nimages - 1; */ /* */ /* Pointer to a buffer containing */ /* the data, as a char *; */ /* */ /* The amount of data in that */ /* buffer. */ /*************************************************/ static _kernel_oserror * image_load_chunk(browser_data * b, int image, char * buffer, int bytes) { BBox box; _kernel_oserror * e = NULL; #ifdef TRACE if (tl & (1u<<15)) Printf("image_load_chunk: Called for image %d\n",image); #endif /* If the library has filled in the istore field, tell it to */ /* get some of the data we've fetched. */ if (idata[image].istore) e = idata[image].istore -> Load(idata[image].istore, buffer, bytes, &box); #ifdef TRACE if (tl & (1u<<15)) Printf("image_load_chunk: Exitting with error\n"); #endif /* Return errors for STRICT_PARSER builds only */ #ifdef STRICT_PARSER if (e) return e; #else if (e) return NULL; #endif /* Update the image on the page to reflect the new data */ image_update_image(b, image, &box); #ifdef TRACE if (tl & (1u<<15)) Printf("image_load_chunk: Successful\n"); #endif return NULL; } /*************************************************/ /* image_remove_data() */ /* */ /* Discards the fetched image data associated */ /* with an image, but leaves the URL information */ /* alone. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* The image number, from 0 to */ /* nimages - 1. */ /*************************************************/ static void image_remove_data(browser_data * b, int image) { int p, oldsize, remove; #ifdef TRACE if (tl & (1u<<15)) Printf("image_remove_data: Called for image %d\n",image); #endif /* Get the current data size */ oldsize = flex_size((flex_ptr) &ddata); /* The alloc field holds the total amount of data associated */ /* with the image, and the ualloc field holds the amount */ /* that the URL uses. So subtract ualloc from alloc to get */ /* the amount that must be removed. */ remove = idata[image].alloc - idata[image].ualloc; /* Need to shuffle the rest of the flex block */ /* down and shrink the block, if this isn't */ /* the last image (i.e. p != oldsize). */ p = image_data_offset(b,image) + idata[image].alloc; if (p != oldsize) memmove(ddata + p - remove, ddata + p, oldsize - p); flex_extend((flex_ptr) &ddata, oldsize - remove); #ifdef TRACE flexcount -= remove; if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif /* Subtract the amount removed from the alloc field */ /* and set isize to zero, i.e. we're not storing */ /* any image data anymore. */ idata[image].alloc -= remove; idata[image].isize = 0; #ifdef TRACE if (tl & (1u<<15)) Printf("image_remove_data: Successful\n"); #endif } /*************************************************/ /* image_remove_all_data() */ /* */ /* Discards the fetched image data associated */ /* with an image, including URL information. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* The image number, from 0 to */ /* nimages - 1. */ /*************************************************/ static void image_remove_all_data(browser_data * b, int image) { int p, oldsize, remove; #ifdef TRACE if (tl & (1u<<15)) Printf("image_remove_all_data: Called for image %d\n",image); #endif /* Get the current data size */ oldsize = flex_size((flex_ptr) &ddata); /* The alloc field holds the total amount of data associated */ /* with the image. */ remove = idata[image].alloc; if (remove) { /* Need to shuffle the rest of the flex block */ /* down and shrink the block, if this isn't */ /* the last image (i.e. p != oldsize). */ p = image_data_offset(b, image); if (p != oldsize) memmove(ddata + p, ddata + p + remove, oldsize - p - remove); flex_extend((flex_ptr) &ddata, oldsize - remove); #ifdef TRACE flexcount -= remove; if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif } /* Subtract the amount removed from the alloc field */ /* and set isize to zero, i.e. we're not storing */ /* any image data anymore. */ idata[image].alloc = 0; idata[image].isize = 0; #ifdef TRACE if (tl & (1u<<15)) Printf("image_remove_all_data: Successful\n"); #endif } /*************************************************/ /* image_delay() */ /* */ /* Stop fetching data for an image, even if it */ /* hasn't all been fetched, but allow the fetch */ /* to continue later on. */ /* */ /* Call with care. Keeping part of an image is */ /* dangerous - ImageLib may object. Normally, */ /* this should only be called for images which */ /* have not started to fetch. Stopping an image */ /* mid-fetch is more safely done through */ /* image_abandon. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* The image number, from 0 to */ /* nimages - 1. */ /*************************************************/ static void image_delay(browser_data * b, int image) { #ifdef TRACE if (tl & (1u<<15)) Printf("image_delay: Called for image %d\n",image); #endif /* Stop any fetching for the image */ if (idata[image].handle) html_close(idata[image].handle); idata[image].handle = 0; idata[image].delayed = 1; /* If this is a background, go back to the plain background */ if (image == b->background_image) b->antialias_colour = redraw_backcol(b); if (idata[image].istore) image_update_image(b, image, NULL); /* There may be a new overall fetch status, so ensure the */ /* status bar is up to date */ toolbars_cancel_status(b, Toolbars_Status_GetPics); #ifdef TRACE if (tl & (1u<<15)) Printf("image_delay: Successful\n"); #endif } /*************************************************/ /* image_delay_fetches() */ /* */ /* Stops any images which have not started to */ /* fetch for a given browser from fetching at */ /* all, by setting the image's 'delayed' flag. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the images. */ /*************************************************/ void image_delay_fetches(browser_data * b) { if (nimages) { int image; for (image = 0; image < nimages; image++) { /* Only interested in non-cross referencing images */ /* owned by this browser which are not fetching. */ if ( idata[image].owner == b && idata[image].xref < 0 && !idata[image].handle ) image_delay(b, image); } } } /*************************************************/ /* image_abandon() */ /* */ /* Stop fetching data for an image, even if it */ /* hasn't all been fetched. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* The image number, from 0 to */ /* nimages - 1. */ /*************************************************/ static void image_abandon(browser_data * b, int image) { BBox end; #ifdef TRACE if (tl & (1u<<15)) Printf("image_abandon: Called for image %d\n",image); #endif /* Stop any fetching for the image */ if (idata[image].handle) html_close(idata[image].handle); idata[image].handle = 0; idata[image].fetched = 1; /* If this is a background, go back to the plain background */ if (image == b->background_image) b->antialias_colour = redraw_backcol(b); if (idata[image].istore) { /* Let this fail silently */ idata[image].istore -> EndLoad(idata[image].istore, &end); /* Redraw the image */ image_update_image(b, image, &end); } /* There may be a new overall fetch status, so ensure the */ /* status bar is up to date */ toolbars_cancel_status(b, Toolbars_Status_GetPics); #ifdef TRACE if (tl & (1u<<15)) Printf("image_abandon: Successful\n"); #endif } /*************************************************/ /* image_abort_fetches() */ /* */ /* Stop fetching data for all images belonging */ /* to a given browser, discarding all data for */ /* those images. The fetches cannot be restarted */ /* after this! */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the images. */ /*************************************************/ void image_abort_fetches(browser_data * b) { int i; /* Loop through the images */ for (i = 0; i < nimages; i++) { /* If owned by this browser... */ if (idata[i].owner == b) { /* Mark the image as fetched (so pending images don't then */ /* start to fetch after fetching images stop). */ idata[i].fetched = 1; /* Stop any images which are fetching */ if (idata[i].handle) { html_close(idata[i].handle); idata[i].handle = 0; idata[i].bytesgot = 0; /* If this is a background, go back to the plain background */ if (i == b->background_image) b->antialias_colour = redraw_backcol(b); if (idata[i].istore) { #ifdef STRICT_PARSER /* Report any errors as Continue-only in strict parser mode */ _kernel_oserror * e; e = idata[i].istore -> Delete(idata[i].istore); if (e) { e->errnum = Utils_Error_Custom_Message; show_error_ret(e); } #else /* If not in Strict mode, ignore any errors */ idata[i].istore -> Delete(idata[i].istore); #endif idata[i].istore = NULL; /* Remove any fetched data (not URL information though) */ image_remove_data(b, i); /* Redraw the item */ browser_update_token(b, idata[i].token, 0, 0); } } } } /* There may be a new overall fetch status, so ensure the */ /* status bar is up to date */ toolbars_cancel_status(b, Toolbars_Status_GetPics); } /*************************************************/ /* image_new_image() */ /* */ /* Starts fetching a new image. Creates a record */ /* in the idata flex block and increments the */ /* image counter. If an image is already being */ /* fetched with the same URL then a cross */ /* reference is set up to point to that, else a */ /* fetch is initiated. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image to fetch; */ /* */ /* Pointer to the URL to fetch; */ /* */ /* The address of the token that led */ /* to this function call, i.e. the */ /* token representing that image; */ /* */ /* 1 if the image is for the */ /* background (it will be turned */ /* into a sprite for fast plotting), */ /* else 0. If 2, then the */ /* background_image field of the */ /* browser_data structure is filled */ /* in with the relevant image number */ /* and the image will again be */ /* turned into a sprite for fast */ /* plotting. */ /*************************************************/ _kernel_oserror * image_new_image(browser_data * b, const char * url, HStream * token, int background) { int size, ok, ulen, xref; HStream * tp; #ifdef TRACE if (tl & (1u<<15)) Printf("image_new_image: Called\n"); #endif /* Can't proceed without the URL to fetch! */ if (!url) url = ""; /* Point to *something*, even if it is blank */ xref = -1; tp = token; /* Need to make sure we don't already have an image with this URL */ /* if there are already some images present. */ if (nimages) { int test = 1; /* If this is a File URL, check it's not scrap - don't allow cross */ /* referencing if it is. */ if (!strncmp(url, FileMethod, sizeof(FileMethod) - 1)) { char test_url[Limits_URL]; /* Build the URL we'd be on for a scrap file fetch */ StrNCpy0(test_url, Save_ScrapFile); urlutils_pathname_to_url(test_url, sizeof(test_url)); /* If it's the same as the URL we've been given, don't */ /* cross reference the image. */ if (!strcmp(url, test_url)) test = 0; } if (test) { int i; for (i = 0; i < nimages; i++) { /* Can we simply create a cross reference to another image? */ if ( idata[i].alloc && /* The image has data allocated for it */ !strcmp(url, ddata + image_data_offset(b,i)) && /* This holds a URL which matches that passed in */ idata[i].xref < 0 && /* The image doesn't cross reference things itself */ idata[i].background == !!background && /* We're not trying to make a b/g an f/g or vice versa */ ( !background || /* This isn't a background image, or... */ ( background && /* If this is a background image, the background */ idata[i].owner->background_colour == b->background_colour /* colours match in the two owner browsers */ ) ) ) { xref = i; /* If all of the above are satisfied, cross reference */ i = nimages; /* this image with the one it matches. */ } } } } if (xref >= 0) ulen = 0; /* Don't need any extra data for a cross referenced image. */ else { ulen = (strlen(url) + 1); ulen = (int) WordAlign(ulen); } size = ulen; /* Allocate space for the image_info structure */ if (idata) { int oldsize; oldsize = flex_size ((flex_ptr) &idata); ok = flex_extend((flex_ptr) &idata, oldsize + sizeof(image_info)); } else ok = flex_alloc ((flex_ptr) &idata, sizeof(image_info)); /* Allocate space for the image data */ if (ok && ddata) { int oldsize; #ifdef TRACE flexcount += sizeof(image_info); if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif oldsize = flex_size ((flex_ptr) &ddata); ok = flex_extend((flex_ptr) &ddata, oldsize + size); } else ok = flex_alloc ((flex_ptr) &ddata, size); /* Throw back an error if out of memory */ if (!ok) { erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("NoMemImg:There is not enough free memory for any new images (%0).", 0, "1")); return &erb; } #ifdef TRACE flexcount += size; if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif /* Increment the image counter, zero the contents of the image_info structure */ /* and fill in some of the fields where contents are known now. */ memset(&idata[nimages], 0, sizeof(image_info)); /* Precautionary catch-all */ idata[nimages].owner = b; idata[nimages].x = -1; idata[nimages].y = -1; idata[nimages].alloc = size; idata[nimages].ualloc = ulen; idata[nimages].isize = 0; idata[nimages].token = tp; idata[nimages].xref = xref; idata[nimages].background = !!background; idata[nimages].hadfiller = 0; /* Set the 'delayed' flag if it's not a background image and */ /* images are flagged as not being shown right now, or if it */ /* is a backgrond image and backgrounds are flagged to be */ /* plain only. */ if (background) { idata[nimages].delayed = !b->show_background; idata[nimages].canredraw = 1; /* Set the background image number */ if (background == 2) b->background_image = nimages; } else { idata[nimages].delayed = (!b->show_foreground && b->displayed != Display_External_Image); } /* If there's data for a URL, copy it into the above allocated block */ /* and ImageLib about the new image. */ if (ulen) { strcpy(ddata + image_data_offset(b, nimages), url); idata[nimages].istore = NewImage(NULL, background ? IMAGE_FAST : 0); } // This works, but you've then got to alter the dataoffset fields // of higher images whenever you remove any data from one lower in // the flex block. That's not implemented yet, and the speed penalty // of this may offset the gains in image_data_offset. // // if (!nimages) idata[nimages].dataoffset = 0; // else idata[nimages].dataoffset = idata[nimages - 1].dataoffset + alloc; /* If the image is not a background one, need to update the */ /* width and height fields according to any details in the */ /* HTML source. */ if (!background) { /* Need to flag if the HTML gives us an absolute size */ if ( OBJECT_HAS_WIDTH (token) && OBJECT_HAS_HEIGHT (token) && OBJECT_WIDTH_UNITS (token) == UNITS_PIXELS && OBJECT_HEIGHT_UNITS(token) == UNITS_PIXELS ) idata[nimages].fixedsize = 1, idata[nimages].canredraw = 1; else { /* If reformats aren't immediate, image_update_image may ask for */ /* one and then if canredraw is set, will start updating the */ /* image. This is invalid unless the reformat has definitely */ /* occurred (a full size image plots inside a default size */ /* placeholder...). However, if reformats happen straight away, */ /* we'll always be able to redraw. */ if (!choices.refo_wait) idata[nimages].canredraw = 1; else idata[nimages].canredraw = 0; } idata[nimages].currw = idata[nimages].currh = -1; } nimages++; if (!ulen) { /* Since we aren't starting a fetch for this image, must call image_update_image */ /* on it to make sure any required reformats etc. (e.g. because a width or height */ /* but not both was specified in the HTML) are carried out. */ image_update_image(b, nimages - 1, NULL); } /* Reflect any change of status */ toolbars_update_status(b, Toolbars_Status_GetPics); toolbars_set_button_states(b); /* Finished. */ #ifdef TRACE if (tl & (1u<<15)) Printf("image_new_image: Successful\n"); #endif return NULL; } /*************************************************/ /* image_process_null() */ /* */ /* The main engine of the image loading system. */ /* Allows up to choices.maximages images to be */ /* simultaneously fetched. Handles redraw, */ /* memory allocation, out-of-memory fetch stops, */ /* and so-forth. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the images. */ /*************************************************/ _kernel_oserror * image_process_null(browser_data * b) { _kernel_oserror * e; #ifdef TRACE if (tl & (1u<<15)) Printf("image_process_null: Called\n"); #endif /* If there are not maximages images being fetched and there are still */ /* images which have not started fetching, then start a fetch for one. */ /* Then do a 'get next chunk' on the next image with a fetch going */ /* on. Cause a screen update as appropriate. */ if (nimages && b->fetch_status != BS_DATAFETCH) { int image, fetches; fetches = image_count_fetches(b); if (lastimage < 0 || lastimage >= nimages) lastimage = nimages - 1; image = lastimage; /* For all images up to and including the last one dealt with in a */ /* null event, initiate fetches if they haven't already started. */ do { image ++; if (image >= nimages) image = 0; if ( !idata[image].handle && !idata[image].fetched && !idata[image].delayed && idata[image].xref < 0 && fetches < choices.maximages ) { e = html_get(ddata + image_data_offset(b,image), /* Document to fetch */ NULL, /* Pointer to any extra data */ (int *) &idata[image].handle, /* Place to return the fetch handle */ URL_Method_http_GET, /* Fetch method */ #ifndef SINGLE_USER user.name, /* User name for MailServ, if using */ #else /* a multiuser version */ NULL, #endif 0, /* 0 = Don't parse fetched data */ !b->reloading); /* 1 = Proxy can be used, 0 = can't */ if (e) { /* Don't report it; countless 'I've failed' reports for */ /* the very beginning of a fetch are not very helpful. */ /* Particularly, 'file not found' for file fetches, can */ /* be major pain. */ image_abandon(b, image); } } } while (image != lastimage && !idata[image].handle); lastimage = image; /* If the image we're now on has a fetch handle... */ if (idata[image].handle) { char buffer[10240]; /* ...And a fetch has already started... */ if (idata[image].started) { int done, bytes; /* ...Then get the next chunk of data. If the fetch */ /* call gives an error, abandon the image fetch and */ /* exit. */ e = fetch_get_raw_data(NULL, /* The browser_data structure */ idata[image].handle, /* Fetch handle */ buffer, /* Buffer for fetched data */ sizeof(buffer), /* Buffer's size */ &done, /* 'done' = 1 for fetch completed, else 0 */ &bytes); /* Number of bytes put into buffer */ if (e) { image_abandon(b, image); return e; } idata[image].bytesgot += bytes; /* If the fetch_get_raw_data call resulted in data being fetched, */ /* send that to the image library and reflect the fetched data in */ /* the status bar. */ if (bytes) { e = image_load_chunk(b, image, buffer, bytes); if (e) { image_abandon(b, image); return e; } toolbars_update_progress(b); } /* If the fetch has finished, update everything as appropriate */ if (done) { BBox end; html_close(idata[image].handle); idata[image].handle = 0; idata[image].fetched = 1; idata[image].success = 1; if (idata[image].istore) { #ifdef STRICT_PARSER /* Report any errors as Continue-only in strict parser mode */ e = idata[image].istore -> EndLoad(idata[image].istore, &end); if (e) { e->errnum = Utils_Error_Custom_Message; show_error_ret(e); } #else /* If not in Strict mode, ignore any errors */ idata[image].istore -> EndLoad(idata[image].istore, &end); #endif } /* If the image is a background image and the library has */ /* dealt with it (this is assumed to be the case if the */ /* library has assigned a width to the image) then get */ /* the background text colour from the library on the */ /* basis of the image's contents. Else, use the plain */ /* background colour. */ /* */ /* Note that it is not until the call to BGCol that */ /* ImageLib actually turns the background image into a */ /* sprite in the current colour depth, so if this isn't */ /* called, the sprite will plot slowly resulting in */ /* (typically) painfully slow redraws. */ if (image == idata[image].owner->background_image) { b->antialias_colour = redraw_backcol(b); if (idata[image].istore->width > 0) { #ifdef STRICT_PARSER /* Report any errors as Continue-only in strict parser mode */ e = idata[image].istore -> BGCol(idata[image].istore, &b->antialias_colour, b->background_colour != -1 && b->use_source_cols); if (e) { e->errnum = Utils_Error_Custom_Message; show_error_ret(e); } #else /* If not in Strict mode, ignore any errors */ idata[image].istore -> BGCol(idata[image].istore, &b->antialias_colour, b->background_colour != -1 && b->use_source_cols); #endif } } image_update_image(b, image, &end); } } else { /* In this block of code, the current image has had a fetch */ /* started for it, but hasn't fetched any data yet - so we */ /* haven't (for example) got a full header for the request. */ int remn, sofar, waiting; int realsize; HStream * tptr; void * store; /* Go in with no store; only interested in new data, as */ /* anything fetched so far goes into the ddata block. */ store = NULL; /* At this stage, must put the data fetched so far back into the */ /* store. This is because headers may come in over several */ /* passes through the fetcher - if we give the fetcher an empty */ /* block each time, it won't be able to see the whole header */ /* block and the fetch fails (well, the image library keeps */ /* waiting for something to happen). At the time of writing this */ /* is demonstrated by the images at http://www.debenhams.co.uk/'. */ if (idata[image].isize) { int p = image_data_offset(b, image) + idata[image].ualloc; /* Complain if the flex allocation fails, else copy the data across */ if (!flex_alloc(&store, idata[image].isize)) { image_abandon(b, image); return NULL; } else { #ifdef TRACE flexcount += idata[image].isize; if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif memcpy(store, ddata + p, idata[image].isize); } } #ifdef TRACE_FETCH_STORE if (store) { int offset; int size = flex_size(&store); char cha; int nl = 0; Printf("Image %d pre-fetch store:\n\n",image); for (offset = 0; offset < size; offset ++) { cha = ((char *) store)[offset]; if (cha > 31) Printf("%c",cha); if (cha == 13 || cha == 10) { if (!nl) { Printf("\n"); nl = 1; } else nl = 0; } } Printf("\nEnd of store\n\n"); } #endif /* Call the fetch routine */ e = html_get_next_token(b, idata[image].handle, &remn, &sofar, &tptr, &waiting, &store, ddata + image_data_offset(b, image), 1); if (e) { /* If there's an error, free the store and ditch the image */ if (store) { #ifdef TRACE flexcount -= flex_size(&store); if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif flex_free(&store); } image_abandon(b, image); #ifdef TRACE if (tl & (1u<<15)) Printf("image_process_null: Exitting with error\n"); #endif return e; } #ifdef TRACE_FETCH_STORE if (store) { int offset; int size = flex_size(&store); char cha; int nl = 0; Printf("Image %d post-fetch store:\n\n",image); for (offset = 0; offset < size; offset ++) { cha = ((char *) store)[offset]; if (cha > 31) Printf("%c",cha); if (cha == 13 || cha == 10) { if (!nl) { Printf("\n"); nl = 1; } else nl = 0; } } Printf("\nEnd of store\n\n"); } #endif /* How much data was added? */ if (store) realsize = flex_size(&store) - idata[image].isize; /* We already had 'isize' bytes before the fetcher was entered */ else realsize = 0; /* If we've got data in the local store, copy it to the image store. */ if (realsize >= 0) { int p, oldsize; /* oldsize holds the whole image data block size (for all images) */ oldsize = flex_size((flex_ptr) &ddata); /* Allocate 'size' extra bytes, doing the relevant error handling */ /* code if the allocation fails */ if (!flex_extend((flex_ptr) &ddata, oldsize + realsize)) { #ifdef TRACE flexcount -= flex_size(&store); if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif flex_free(&store); image_abandon(b,image); erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("NoMemImg:There is not enough free memory for any new images (%0).", 0, "2")); return &erb; } #ifdef TRACE else { flexcount += realsize; if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); } #endif /* p points to the first byte after the data used by this image */ p = image_data_offset(b, image) + idata[image].alloc; /* Move all data from p up to the end of the block (as was, hence */ /* oldsize) along the block by size bytes, effectively creating */ /* a gap of that many bytes just after this image's data. If p */ /* = oldsize, this was the last image in the block and there's */ /* no data above it to move (so don't do the memmove). */ if (p != oldsize) memmove(ddata + p + realsize, ddata + p, oldsize - p); /* Copy the new data into that gap */ memcpy(ddata + p, ((char *) store) + idata[image].isize, realsize); /* Update the image's image data and total data pointers */ idata[image].alloc += realsize; idata[image].isize += realsize; } /* Act according to the fetch status */ switch (waiting) { case 1: break; /* Nothing happened yet, or the HTTP response header is still coming in */ case 2: break; /* Redirected, we can ignore and wait for the fetcher to cope */ case 0: /* Not awaiting delivery (shouldn't happen in this context, but here 'just in case') */ case 3: /* We have some data */ { int i, n, n1; /* Flag that a fetch has started to happen */ idata[image].started = 1; /* Set 'n' to the size of data that was fetched */ if (store) n = flex_size(&store); else n = 0; /* Increment the 'bytes so far' counter for the image */ idata[image].bytesgot += n; /* Push the data into the library, in chunks defined by */ /* the size of 'buffer' (allocated as a char[] above). */ i = 0; while (n) { n1 = n; if (n1 > sizeof(buffer)) n1 = sizeof(buffer); memcpy(buffer, (char *) store + i, n1); i += n1; n -= n1; e = image_load_chunk(b, image, buffer, n1); if (e) { /* If there's an error, free the store and ditch the image */ if (store) { #ifdef TRACE flexcount -= flex_size(&store); if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif flex_free(&store); } image_abandon(b, image); #ifdef TRACE if (tl & (1u<<15)) Printf("image_process_null: Exitting with error\n"); #endif return e; } } /* Get rid of any accumulated data and update the status bar */ image_remove_data(b, image); toolbars_update_progress(b); } break; } /* Free the temporary store, if it was used */ if (store) { #ifdef TRACE flexcount -= flex_size(&store); if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif flex_free(&store); } /* Closure of long 'if' that deals with either continuing */ /* to fetch data for an image, or getting the first chunk */ /* of data for a fetch. (The code immediately above is for */ /* getting the first chunk). */ } /* Closure of long 'if' that sees if a fetch has started */ /* for an image. (The code immediately above excutes if */ /* it has, else a fetch is started). */ } /* Closure of long 'if' that checks to see if there are */ /* images to be fetched, and that the fetch status is */ /* not BS_DATAFETCH (i.e. other fetch activity has */ /* ceased). */ } #ifdef TRACE if (tl & (1u<<15)) Printf("image_process_null: Successful\n"); #endif return NULL; } /*************************************************/ /* image_delete_image_entry() */ /* */ /* Deletes an image_info structure from the */ /* array of structures. The cross reference */ /* image numbers of subsequent images are */ /* adjusted appropriately. */ /* */ /* The idata flex block is not shrunk; this is */ /* expected to be done externally, to avoid */ /* possibly freeing many small blocks */ /* consecutively (which can be slow with flex). */ /* */ /* Parameters: Image number to remove. */ /*************************************************/ static void image_delete_image_entry(int image) { browser_data * b; int i; nimages--; if (lastimage == image) lastimage--; if (lastimage >= nimages) lastimage = nimages - 1; /* Only need to alter xref numbers or move flex data */ /* around if this wasn't the last image in the block */ if (image < nimages) /* (Not 'nimages - 1' as nimages has been decremented by this point) */ { memmove(&idata[image], &idata[image + 1], sizeof(image_info) * (nimages - image)); for (i = image; i < nimages; i++) { #ifdef TRACE if (idata[i].xref == image) Printf("JUST DELETED IMAGE %d WHICH WAS XREF'D AND SHOULDN'T HAVE BEEN\n"); #endif /* Did this cross reference an image which will have changed number? */ if (idata[i].xref > image) idata[i].xref--; /* Did this have a filler function registered for it? */ if (idata[i].hadfiller) { /* If so, then provided the filler function is still available, */ /* reregister with the new image number. */ if (!idata[i].istore->RegisterFiller) { idata[i].hadfiller = 0; } else { show_error_ret(image_register_filler(i)); } } } } /* Need to also alter browser_data structures' background image */ /* number fields. */ b = last_browser; while (b) { if (b->background_image > image) b->background_image--; b = b->previous; } } /*************************************************/ /* image_discard() */ /* */ /* Discards all images for a given browser */ /* window, freeing the memory and shutting down */ /* any active fetches. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the images. */ /*************************************************/ _kernel_oserror * image_discard(browser_data * b) { int newsize, i, xref; #ifdef TRACE if (tl & (1u<<15)) Printf("\nimage_discard: Called for %p\n\n", b); #endif if (!nimages) return NULL; /* See if any images cross reference those in the browser */ /* for which images will be discarded. If so, 'move' the */ /* cross referencee over to be owned by the cross */ /* referencer. */ _swix(Hourglass_Start, _IN(0), 1); #ifdef TRACE if (tl & (1u<<15)) { Printf("image_discard: First pass, checking for cross referencees owned by %p\n", b); Printf(" There are %d images before this pass.\n\n", nimages); } #endif for (i = nimages - 1; i >= 0; i--) /* Going backwards helps out flex */ { xref = idata[i].xref; if (idata[i].owner != b && xref >= 0 && idata[xref].owner == b) { /* Keep the xref image, since that holds the real data and */ /* is doing all the fetching, etc. - it works out as less */ /* effort overall (especially in the second pass below) to */ /* delete the cross referencing image. This may seem a bit */ /* odd as though we've been called to delete the images */ /* owned by browser 'b', in the first pass we end up */ /* deleting structures owned by completely different */ /* browsers... */ #ifdef TRACE if (tl & (1u<<15)) Printf("image_discard: Transferring cross referencer %d to cross referencee %d\n", i, xref); #endif idata[xref].owner = idata[i].owner; idata[xref].x = idata[i].x; idata[xref].y = idata[i].y; idata[xref].token = idata[i].token; idata[xref].currw = idata[i].currw; idata[xref].currh = idata[i].currh; idata[xref].xref = -1; idata[xref].delayed = idata[i].delayed; idata[xref].fixedsize = idata[i].fixedsize; idata[xref].background = idata[i].background; idata[xref].hadfirst = idata[i].hadfirst; idata[xref].canredraw = idata[i].canredraw; idata[xref].priority = idata[i].priority; /* Did the image we're transferring a new owner to have */ /* RegisterFiller called for it, with the old owner? */ if (idata[xref].hadfiller) { /* If an image became a background, ImageLib may have withdrawn the */ /* RegisterFiller function... */ if (!idata[xref].istore->RegisterFiller) { idata[xref].hadfiller = 0; } else { /* Recall the RegisterFiller function with the new owner details */ show_error_ret(image_register_filler(xref)); } } /* If this is a background image for the cross referencer... */ if (i == idata[i].owner->background_image) { /* Update the browser_data 'background_image' field to point to the */ /* new cross reference image number. */ idata[i].owner->background_image = xref; } /* If the cross referencee was being fetched, may need to */ /* claim fetch nulls in the cross referencer to finish */ /* the job. */ if (idata[xref].handle && !idata[i].owner->fetch_handler) { fetchpage_claim_nulls(idata[i].owner); toolbars_update_status(idata[i].owner, Toolbars_Status_GetPics); } /* Delete the cross referencer */ #ifdef TRACE if (tl & (1u<<15)) Printf("image_discard: Deleting entry owned by %p that cross references image %d\n", idata[i].owner, idata[i].xref); #endif image_remove_all_data(idata[i].owner, i); image_delete_image_entry(i); } } #ifdef TRACE if (tl & (1u<<15)) { Printf("\nimage_discard: First pass complete, there are now %d images\n", nimages); Printf(" Second pass, removing remaining images owned by %p\n\n", b); } #endif /* Now delete any remaining images owned by the given browser */ for (i = nimages - 1; i >= 0; i--) /* Going backwards helps out flex */ { if (idata[i].owner == b) { /* If the image was being fetched, close that session */ if (idata[i].handle) { html_close(idata[i].handle); idata[i].handle = 0; } if (idata[i].istore) { #ifdef STRICT_PARSER /* Report any errors as Continue-only in strict parser mode */ _kernel_oserror * e; e = idata[i].istore -> Delete(idata[i].istore); if (e) { e->errnum = Utils_Error_Custom_Message; show_error_ret(e); } #else /* If not in Strict mode, ignore any errors */ idata[i].istore -> Delete(idata[i].istore); #endif } image_remove_all_data(b, i); image_delete_image_entry(i); } } #ifdef TRACE if (tl & (1u<<15)) Printf("\nimage_discard: Second pass complete, there are now %d images\n\n", nimages); #endif /* If there is an animation handler present, but no animated */ /* images remain, remove that handler. */ if (animhandler) { int found = 0; #ifdef TRACE if (tl & (1u<<15)) Printf("image_discard: There is an animation handler present; checking to see if it can be removed\n"); #endif for (i = 0; i < nimages; i++) { if (idata[i].istore && idata[i].istore->animated) { found = 1; break; } } if (!found) { #ifdef TRACE if (tl & (1u<<15)) Printf("image_discard: Removing animation handler\n\n"); #endif deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) image_animate_images, NULL); animhandler = 0; } #ifdef TRACE else { if (tl & (1u<<15)) Printf("image_discard: The handler is still required\n\n"); } #endif } /* Ensure the image_info data block matches the size of the images now present */ newsize = nimages * sizeof(image_info); #ifdef TRACE { int oldsize; oldsize = flex_size((flex_ptr) &idata); flexcount -= (oldsize - newsize); if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); } #endif if (idata) { if (newsize) flex_extend((flex_ptr) &idata, newsize); else flex_free((flex_ptr) &idata); } _swix(Hourglass_Off, 0); #ifdef TRACE if (tl & (1u<<15)) Printf("image_discard: Successful\n\n"); #endif return NULL; } /*************************************************/ /* image_refetch() */ /* */ /* Reloads an image, redrawing as required and */ /* setting the priority flag as asked. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the images; */ /* */ /* The image number; */ /* */ /* Value to set Priority flag to (so */ /* that if 'show foreground images' */ /* is turned off, the image will */ /* override it); */ /* */ /* 1 to redraw the image, 0 to not */ /* bother (e.g. for many images, */ /* may want to redraw the whole */ /* window instead). */ /*************************************************/ static void image_refetch(browser_data * b, int image, int priority, int redraw) { int i; /* Sanity check */ if (image < 0 || image >= nimages || !b) return; /* Use the cross referenced base image if necessary */ if (idata[image].xref >= 0) image = idata[image].xref; if ( ( !idata[image].fetched && /* If the image isn't fetched... */ idata[image].delayed /* ...and was flagged as delayed, */ ) || ( idata[image].fetched && /* or has fetched but... */ !idata[image].success && /* ...not successfully, and... */ !idata[image].istore /* ...it has no ImageLib entry */ ) ) { /* If the image says it isn't fetched, reset its 'bytesgot' counter */ if (!idata[image].fetched) idata[image].bytesgot = 0; /* Set up the flags */ idata[image].success = 0; idata[image].delayed = 0; idata[image].fetched = 0; idata[image].priority = priority; /* If we deleted the ImageLib entry, need to recreate it */ if (!idata[image].istore && idata[image].xref == -1) { idata[image].istore = NewImage(NULL, image == b->background_image ? IMAGE_FAST : 0); } /* Now handle the fetching */ if (!b->fetch_handler) { fetchpage_claim_nulls(b); toolbars_update_status(b, Toolbars_Status_GetPics); } } else { /* If the image is already loaded, just redraw it */ idata[image].priority = priority; idata[image].hadfirst = 0; if (redraw) image_update_image(b, image, NULL); } /* Deal with all cross referencers of the base image */ for (i = 0; i < nimages; i++) { if (idata[i].xref == image) { idata[i].priority = priority; idata[i].hadfirst = 0; if (redraw) image_update_image(b, i, NULL); } } } /*************************************************/ /* image_reload() */ /* */ /* Reloads an image, redrawing as required and */ /* setting the priority flag so that even if a */ /* browser has 'show foreground images' turned */ /* off, the reloading image (and all that cross */ /* reference it) will still be shown. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the images; */ /* */ /* Address of the token representing */ /* the image. */ /*************************************************/ void image_reload(browser_data * b, HStream * token) { int image = image_get_token_image_xref(b, token); if (image < 0) return; image_refetch(b, image, 1, 1); } /*************************************************/ /* image_restart_fetches() */ /* */ /* If image loading has been suspended for any */ /* reason, it can be resumed by calling this */ /* function. Any images already loaded will be */ /* reshown. The priority flag for images is not */ /* set, so the browser must be set to display */ /* foreground images for anything to show up. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the images; */ /* */ /* 1 to restart foreground fetches, */ /* else 0; */ /* */ /* 1 to restart background fetches, */ /* else 0. */ /*************************************************/ void image_restart_fetches(browser_data * b, int foreground, int background) { if (nimages) { int image; /* Loop round all the images */ for (image = 0; image < nimages; image++) { /* If an image has not been fetched and is flagged as being */ /* delayed, or an image has been fetched but has not been */ /* flagged as succesfully fetched and has not been dealt */ /* with by the image library (the istore field is NULL), */ /* then clear all the fetch flags for the image so that it */ /* will later be fetched. */ if (idata[image].owner == b) { /* Only fetch foreground or background images if asked to */ if ( (idata[image].background && background) || (!idata[image].background && foreground) ) image_refetch(idata[image].owner, image, 0, 0); } } } } /*************************************************/ /* image_update_area() */ /* */ /* Updates (redraws) part of an image, which */ /* will reflect any new data fetched for it if */ /* the whole image hasn't been fetched yet. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* The left hand X window coordinate */ /* in OS units for the image; */ /* */ /* The bottom Y window coordinate in */ /* OS units for the image; */ /* */ /* A BBox defining a rectangle */ /* relative to X and Y which is the */ /* area to update; */ /* */ /* The image number, from 0 to */ /* nimages - 1; */ /* */ /* 1 to force a redraw (i.e. clear */ /* any previously drawn data first), */ /* zero to not clear the region. */ /*************************************************/ static _kernel_oserror * image_update_area(browser_data * b, int x, int y, BBox * ubox, int image, int redraw) { _kernel_oserror * e; WimpRedrawWindowBlock r; int more, sx, sy; int xref; #ifdef TRACE if (tl & (1u<<15)) Printf("image_update_area: Called for image %d\n",image); #endif /* If the image cannot be redrawn yet, don't redraw it... */ if (!idata[image].canredraw) { #ifdef TRACE if (tl & (1u<<15)) Printf("image_update_area: Image %d can't be redrawn yet (canredraw = 0)\n",image); #endif return NULL; } r.window_handle = b->window_handle; r.visible_area = *ubox; r.visible_area.xmin += x; r.visible_area.ymin += y; r.visible_area.xmax += x; r.visible_area.ymax += (y + 4); /* Ensure that 'xref' holds the number of the given image, which may well */ /* cross reference another, 'image', that has the actual render data. */ xref = image; if (idata[image].xref >= 0) image = idata[image].xref; /* If the image may acquire a mask during rendering, even if it is not */ /* initially transparent, need to force the area to redraw to clear first */ if ( idata[image].istore->mutable_mask || redraw ) wimp_force_redraw(r.window_handle, r.visible_area.xmin, r.visible_area.ymin, r.visible_area.xmax, r.visible_area.ymax); else { /* Otherwise, do a less flickery non-clearing update */ wimp_update_window(&r, &more); sx = coords_x_toscreen(x, &r); sy = coords_y_toscreen(y, &r); while(more) { if ( b->show_foreground || b->displayed == Display_External_Image || idata[xref].priority ) { browser_redrawing = b; image_redrawing = xref; e = idata[image].istore -> Render(idata[image].istore, sx, sy, 100, idata[xref].currw, idata[xref].currh); browser_redrawing = NULL; image_redrawing = -1; #ifdef STRICT_PARSER if (e) { e->errnum = Utils_Error_Custom_Message; show_error_ret(e); } #endif } #ifdef ANTI_TWITTER if (!printing) anti_twitter(&r); #endif wimp_get_rectangle(&r,&more); } } #ifdef TRACE if (tl & (1u<<15)) Printf("image_update_area: Successful\n"); #endif return NULL; } /*************************************************/ /* image_update_image() */ /* */ /* Update the specified image number on screen, */ /* including all which cross reference it. If */ /* the image has changed size on screen then */ /* start a reformat from the first occurrence */ /* (i.e. smallest token number), otherwise just */ /* do an update on the relevant rectangle for */ /* each occurrence. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the images; */ /* */ /* The image number, from 0 to */ /* nimages - 1; */ /* */ /* Pointer (which may be NULL) to a */ /* BBox into which the image's size */ /* will be returned. */ /*************************************************/ static _kernel_oserror * image_update_image(browser_data * b, int image, BBox * box) { /* Update the specified image number on the screen, including all */ /* which xref to it. If the image has changed size on screen then start */ /* a reformat from the first occurrence (ie the smallest token number), */ /* otherwise just do an update of the relevant rectangle for each */ /* occurrence. */ int i, actual; BBox whole, partial; #ifdef TRACE if (tl & (1u<<15)) Printf("image_update_image: Called for image %d\n",image); #endif /* If a BBox is passed in here, then the image will be redrawn in that */ /* specific region. Otherwise, the region is derived from the image's */ /* size. The BBox 'whole' is used for the latter case, so by setting */ /* 'box' to the address of this if a BBox was *not* given, we can then */ /* transparently use this elsewhere and update either the requested */ /* area or the whole image without needing special case code. */ /* */ /* If the image's 'hadfirst' flag is clear, it hasn't been through */ /* this function yet. In that case, want to ensure that the whole */ /* image is redrawn to clear any placeholder that will be plotted in */ /* its place; otherwise, only the part of the placeholder that */ /* corresponds to the requested redraw box will be cleared. */ if (!box || !idata[image].hadfirst) box = &whole; /* Now ensure that 'image' is the image with the render information, as */ /* the rest of the code has to deal with all xref images to this one */ /* anyway. */ /* */ /* So 'actual' is now the image number given to image_update_image, and */ /* 'image' is the number of the image with associated render data; this */ /* may be the same as 'actual' or may be equal to the contents of its */ /* 'xref' field. */ actual = image; if (idata[image].xref >= 0) image = idata[image].xref; /* If there appears to be no data for the image, or it is less than */ /* one pixel wide (i.e. not displayable for whatever reason), exit. */ if (!idata[image].istore || (idata[image].istore && idata[image].istore->width < 1)) return NULL; /* Check if the image has become animated */ if (idata[image].istore->animated) { /* If the image is animated and we haven't got a null handler */ /* for animations, install one now. */ if (!animhandler) { register_null_claimant(Wimp_ENull, (WimpEventHandler *) image_animate_images, NULL); animhandler = 1; } } if (idata[image].istore -> RegisterFiller && !idata[image].hadfiller) { show_error_ret(image_register_filler(image)); } /* When the image library gets some data for an image it will fill */ /* in the width_os and height_os fields of the istore structure */ /* that 'image' points to. If the HTML source didn't specify a */ /* size for this or cross referencing images and the size is not */ /* the same as stored in currw / currh, flag that a reformat is */ /* needed for this image. */ if ( !idata[actual].hadfirst && idata[image].istore->width_os >= 0 && idata[image].istore->height_os >= 0 ) { for (i = 0; i < nimages; i++) { if ( ( idata[i].xref == image || i == image ) && !idata[i].fixedsize ) { BBox size; /* Ignore any existing currw and currh values - find the image */ /* size based entirely on the HTML and the image itself */ image_get_image_size(b, i, &size, 1); /* Does the returned value differ from the stored size? If so, */ /* flag that we must do a reformat for this image. It is up to */ /* the reformatter to call back to routines in this source */ /* file and update currw and currh once the image is */ /* incorporated into a line array. */ if ( size.xmax != idata[i].currw || size.ymax != idata[i].currh ) idata[i].reformat = 1; } } } /* If the image is for the background, just refresh the window contents */ /* with browser_update_window (remembering to update those windows that */ /* cross reference the same background image, too). */ { int isbackground = 0; browser_data * check = last_browser; /* First check if the image is the background of this browser. If */ /* not then check all browsers. */ if (actual == b->background_image || image == b->background_image) isbackground = 1; else { while (check && !isbackground) { if (actual == check->background_image || image == check->background_image) isbackground = 1; check = check->previous; } } /* If this is a background, update all instances of the image */ /* and exit the update routine here. */ if (isbackground) { for (i = 0; i < nimages; i++) { if ((i == image || idata[i].xref == image) && idata[image].fetched) { browser_update_bottom(idata[i].owner, NULL); } } return NULL; } } /* Look through all images for cross references */ whole.xmin = whole.ymin = 0; for (i = 0; i < nimages; i++) { /* If we're on the current image, or on one which */ /* references it, then proceed. */ if (i == image || idata[i].xref == image) { int l, x, y; int noplot = 0; HStream * tp; BBox ibox; /* Set the 'whole' BBox to the size of the base image in OS units */ image_get_image_size(b, i, &whole, 0); /* Ensure the redraw box returned by the image Load call */ /* (or the BBox 'whole' from above) is scaled to fit the */ /* actual image's dimensions. */ /* */ /* We don't scale if box = &whole, as in that case the */ /* bbox is already scaled, or if for some reason the */ /* width or height fields in the ImageLib image struct */ /* seem to be invalid. */ if ( box != &whole && idata[image].istore->width_os > 0 && idata[image].istore->height_os > 0 ) { partial.xmin = (box->xmin * whole.xmax) / idata[image].istore->width_os; partial.ymin = (box->ymin * whole.ymax) / idata[image].istore->height_os; partial.xmax = (box->xmax * whole.xmax) / idata[image].istore->width_os; partial.ymax = (box->ymax * whole.ymax) / idata[image].istore->height_os; } else partial = *box; /* If the image doesn't have position information, can't plot it */ if (idata[i].x == -1 && idata[i].y == -1) noplot = 1; /* Only continue if the line containing a chunk */ /* relating to the token which relates to this */ /* image (!) can be found. */ l = tokenutils_find_ancestor_line(idata[i].owner, idata[i].token); if (l < 0) noplot = 1; /* May not be redrawing the image, but might still want to */ /* reformat the page here if the size has changed. */ if (noplot) { if (!idata[i].hadfirst && !idata[i].fixedsize) { /* This is the first time we've known how big the image is since it */ /* was encountered in the HTML; record it in the image history, then. */ if (idata[i].istore) { /* Ignore errors, this isn't vital */ imghistory_record((const char **) &ddata, image_data_offset(idata[i].owner, i), idata[i].istore->width_os, idata[i].istore->height_os); } /* Now do the reformat, if the size changed */ idata[i].hadfirst = 1; if (idata[i].reformat) { idata[i].reformat = 0; reformat_format_from(idata[i].owner, l - 1, 0, i); } /* If we don't need to reformat, we can (potentially!) redraw */ else idata[i].canredraw = 1; } } else { x = idata[i].x; y = idata[i].y; tp = idata[i].token; if ( !reformat_get_image_size(idata[i].owner, tp, &ibox) ) { /* If the image size was obtained without error, */ /* correct its X and Y coordinates appropriately */ if (!idata[i].hadfirst) { int do_redraw = 1; idata[i].hadfirst = 1; /* Is the image size fixed in the HTML? If not, may need to do a */ /* reformat. */ if (!idata[i].fixedsize) { /* As above, this is the first time we've known how big the image is */ /* since it was encountered in the HTML, so record it in the image */ /* history. */ if (idata[i].istore) { /* Ignore errors, this isn't vital */ imghistory_record((const char **) &ddata, image_data_offset(idata[i].owner, i), idata[i].istore->width_os, idata[i].istore->height_os); } /* If the size changed, do a reformat */ if (idata[i].reformat) { idata[i].reformat = 0; do_redraw = 0; /* Reformatting - no extra redraws needed */ reformat_format_from(idata[i].owner, l - 1, 0, i); } /* If we don't need to reformat, we can redraw */ else idata[i].canredraw = 1; } /* If no reformat was needed, just do a redraw (which clears the */ /* background first; see the very large comment earlier on). */ if (do_redraw) { image_update_area(idata[i].owner, x, y, &partial, i, 1); } } /* Work out what region to redraw (may only require */ /* a partial area update) */ else if (partial.ymax < partial.ymin) { BBox ubox = partial; /* If the y max coordinate is less than the y min coordinate, */ /* redraw from ymin to the top of the image, and from ymax to */ /* the bottom. That way can miss out a middle section. */ ubox.ymin = partial.ymin; ubox.ymax = whole.ymax; image_update_area(idata[i].owner, x, y, &ubox, i, 0); ubox.ymax = partial.ymax; ubox.ymin = 0; image_update_area(idata[i].owner, x, y, &ubox, i, 0); } else if (partial.xmax < partial.xmin) { BBox ubox = partial; /* Similarly, if xmax is less than xmin, draw from xmin to the */ /* right hand side of the image and from xmax to the left, so */ /* a vertical strip in the middle may be untouched. */ ubox.xmin = partial.xmin; ubox.xmax = whole.xmax; image_update_area(idata[i].owner, x, y, &ubox, i, 0); ubox.xmax = partial.xmax; ubox.xmin = 0; image_update_area(idata[i].owner, x, y, &ubox, i, 0); } else { /* Otherwise, redraw the requested box */ image_update_area(idata[i].owner, x, y, &partial, i, 0); } /* Closure of 'if (!reformat_get_image_size(...))' */ } /* Closure of 'if (noplot)' - code immediately above is */ /* executed in the 'else' section. */ } /* Closure of 'if (i == image || idata[i].xref == image)' */ } /* Closure of loop running round all the images */ } #ifdef TRACE if (tl & (1u<<15)) Printf("image_update_image: Successful\n"); #endif return NULL; } /*************************************************/ /* image_animate_images() */ /* */ /* Checks all images to see if they need */ /* updating. Designed to be called on null */ /* events. */ /* */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ int image_animate_images(int eventcode, WimpPollBlock * block, IdBlock * idb, browser_data * handle) { int i, image, redraw; /* Scan round all images */ for (i = 0; i < nimages; i++) { /* Find the image with associated render data */ if (idata[i].xref >= 0) image = idata[i].xref; else image = i; /* Skip this image if it as already been dealt with */ if (image >= i) { /* If this image does have such data, is flagged as */ /* animated, and has a pointer to a NeedRedraw */ /* function, proceed. */ if ( idata[image].istore && idata[image].istore->animated && idata[image].istore->NeedRedraw ) { BBox extent; // Not actually used yet...! #ifdef STRICT_PARSER /* Report any errors as Continue-only in strict parser mode */ _kernel_oserror * e; e = idata[image].istore -> NeedRedraw(idata[image].istore, &redraw, &extent); if (e) { e->errnum = Utils_Error_Custom_Message; show_error_ret(e); } #else /* If not in Strict mode, ignore any errors */ idata[image].istore -> NeedRedraw(idata[image].istore, &redraw, &extent); #endif /* Redraw the image, if required */ if (redraw) image_update_image(idata[i].owner, i, NULL); } } } return 0; } /*************************************************/ /* image_register_filler() */ /* */ /* Registers the given image and it's owning */ /* browser with ImageLib. Must be called for */ /* cross referencing images only. */ /* */ /* Parameters: The image number to register. */ /*************************************************/ static _kernel_oserror * image_register_filler(int xref) { #ifdef STRICT_PARSER { _kernel_oserror * e; /* Report any errors as Continue-only in strict parser mode */ e = idata[xref].istore -> RegisterFiller(idata[xref].istore, (FillerFunction *) image_fill_background, idata[xref].owner, (int *) xref); if (e) { e->errnum = Utils_Error_Custom_Message; return e; } } #else /* If not in Strict mode, don't report errors */ idata[xref].istore -> RegisterFiller(idata[xref].istore, (FillerFunction *) image_fill_background, idata[xref].owner, (int *) xref); #endif idata[xref].hadfiller = 1; return NULL; } /*************************************************/ /* image_fill_background() */ /* */ /* Called as a FillerFunction for ImageLib, */ /* this redraws the background image. Output */ /* will probably have been redirected by */ /* ImageLib at this point. */ /* */ /* The way the function is registered should */ /* leave void * handle pointing to the browser */ /* who's background will be redrawn, with int * */ /* i actually being an int (not an int *) */ /* holding the image number for which the */ /* function was registered in the first place. */ /*************************************************/ _kernel_oserror * image_fill_background(void * handle, int * i) { WimpGetWindowStateBlock state; _kernel_oserror * e = NULL; browser_data * b = (browser_data *) handle; int image; int ximage = (int) i; int noplot = 0; int bimage = -1; int bgcolour = -1; int x, y, xorigin, yorigin; int w, h; #ifdef TRACE if (tl & (1u<<15)) Printf("image_fill_background: Called with %p, %d\n", handle, i); #endif state.window_handle = b->window_handle; e = wimp_get_window_state(&state); if (e) return e; /* Should never happen, but you never know... */ if (idata[ximage].xref >= 0) ximage = idata[ximage].xref; /* See the top of the file for details on browser_redrawing */ /* and image_redrawing */ if (browser_redrawing) b = browser_redrawing; if (image_redrawing >= -1) image = image_redrawing; else image = ximage; #ifdef TRACE if (tl & (1u<<15)) { Printf("image_fill_background: Decided to use %p, %d\n", b, image); Printf("image_fill_background: Image w, h = %d, %d\n",idata[image].currw,idata[image].currh); } #endif /* The image may exist in a table, and we should check what */ /* background colour to draw if this is the case. */ if (idata[image].token->parent) { table_headdata * head = idata[image].token->parent; table_row * row = NULL; table_stream * table = NULL; if (head) row = head->parent; if (row) table = row->parent; if (table) { if (TABLE_HAS_BGCOL(table)) bgcolour = TABLE_BGCOL(table), noplot = 1; /* Individual cells can override the table */ if (TD_HAS_BGCOL(head)) bgcolour = TD_BGCOL(head), noplot = 1; } } /* We may still have no defined background colour, in which */ /* case check the main page background. */ if (bgcolour == -1) { /* Can't plot a background image if there's no such image or */ /* it's not fully fetched. */ if (b->background_image >= 0) { bimage = b->background_image; if (idata[bimage].xref >= 0) bimage = idata[bimage].xref; } else bimage = -1, noplot = 1; #ifdef TRACE if (tl & (1u<<15)) { if (bimage >= 0) Printf("image_fill_background: Background image = %d\n",bimage); else Printf("image_fill_background: No background image\n"); } #endif if ( !noplot && ( !idata[bimage].fetched || !idata[bimage].istore ) ) noplot = 1; if ( !noplot && idata[bimage].istore && ( idata[bimage].istore->width_os <= 0 || idata[bimage].istore->height_os <= 0 ) ) noplot = 1; bgcolour = redraw_backcol(b); } xorigin = -idata[image].x; yorigin = -idata[image].y; /* Ensure dithered background colours have a dithering */ /* (ECF) pattern that starts in the right place */ e = _swix(OS_SetECFOrigin, _INR(0,1), -xorigin, -yorigin); if (e) return e; /* Do a blank rectangle if the image is transparent (has a mask) */ /* or the image itself can't be plotted, or the browser window */ /* set up not to plot background images, or we're printing and */ /* shouldn't plot backgrounds (we must plot something at this */ /* stage!). */ if ( noplot || idata[bimage].istore->transparent || !b->show_background || ( printing && !printstyle_show_all() ) ) { redraw_set_colour(bgcolour); bbc_rectanglefill(0, 0, idata[ximage].istore -> width_os, idata[ximage].istore -> height_os); } /* If there's no background image that can be plotted, exit here */ if ( !b->show_background || noplot || ( printing && !printstyle_show_all() ) ) { #ifdef TRACE if (tl & (1u<<15)) Printf("image_fill_background: No background image to plot, so successful (exitting)\n"); #endif return NULL; } /* Find the scale for the background plotting; if the foreground */ /* image is scaled, then plotting the background at 1:1 will */ /* make it look scaled by the same amount, and things won't */ /* match up. We could either prescale the foreground image and */ /* then plot into that prescaled image, but this takes a lot */ /* of memory, or we could try to stick with a post-scaled image. */ /* In that case, the background can either be ignored or we can */ /* try to scale it down so though it will look blocky and low */ /* resolution, it will at least roughly line up with the things */ /* around the foreground. This code does the last of the three. */ /* */ /* Note the faster, simpler routine for scalings of 1:1 - avoids */ /* floating point. */ if ( idata[image].currw != idata[ximage].istore->width_os || idata[image].currh != idata[ximage].istore->height_os ) { BBox redraw; float scalex, scaley; float tempx, tempy; float tempw, temph; int htop; #ifdef TRACE if (tl & (1u<<15)) Printf("image_fill_background: Plotting scaled background\n"); #endif tempx = (float) idata[ximage].istore->width_os, tempy = (float) idata[ximage].istore->height_os; tempw = (float) idata[image].currw, temph = (float) idata[image].currh; scalex = tempx / tempw; scaley = tempy / temph; /* Round the background image's width and height down to pixel boundaries */ tempw = idata[bimage].istore->width_os; temph = idata[bimage].istore->height_os; w = ((int) (tempw * scalex + (float) 0.5)) & ~(wimpt_dx() - 1); h = ((int) (temph * scaley + (float) 0.5)) & ~(wimpt_dy() - 1); /* Work out the coordinates over which to tile the image */ tempx = - (float) (idata[image].x); if (!controls.swap_bars) htop = toolbars_button_height(b) + toolbars_url_height(b); else htop = toolbars_status_height(b); if (htop) htop += wimpt_dy(); tempy = - (float) idata[image].y - (float) htop; xorigin = (int) (tempx * scalex + (float) 0.5) + 1; yorigin = (int) (tempy * scaley + (float) 0.5) + 1; /* Exit if the background size appears to be too small */ if (w < 1 || h < 1) return NULL; redraw.xmin = -((-xorigin) % w); redraw.ymin = -h; redraw.xmax = idata[ximage].istore->width_os; redraw.ymax = idata[ximage].istore->height_os + (yorigin - idata[ximage].istore->height_os) % h - 1; idata[bimage].istore -> ModeChange(idata[bimage].istore); /* Render the image over the redraw region, with a scaled plot */ for (y = redraw.ymax - h + 4; y >= redraw.ymin; y -= h) { for (x = redraw.xmin; x <= redraw.xmax; x += w) { e = (idata[bimage].istore -> Render(idata[bimage].istore, x, y, 100, w, h)); if (e) break; } if (e) break; } idata[bimage].istore -> ModeChange(idata[bimage].istore); } else { BBox redraw; int htop; #ifdef TRACE if (tl & (1u<<15)) Printf("image_fill_background: Plotting unscaled background\n"); #endif w = (idata[bimage].istore->width_os) & ~(wimpt_dx() - 1); h = (idata[bimage].istore->height_os) & ~(wimpt_dy() - 1); xorigin = -idata[image].x; if (!controls.swap_bars) htop = toolbars_button_height(b) + toolbars_url_height(b); else htop = toolbars_status_height(b); if (htop) htop += wimpt_dy(); yorigin = -idata[image].y - htop; /* Exit if the background size appears to be too small */ if (w < 1 || h < 1) return NULL; redraw.xmin = -((-xorigin) % w); redraw.ymin = -h; redraw.xmax = idata[ximage].istore->width_os; redraw.ymax = idata[ximage].istore->height_os + (yorigin - idata[ximage].istore->height_os) % h - 1; idata[bimage].istore -> ModeChange(idata[bimage].istore); /* Render the image over the redraw region, with an unscaled plot */ for (y = redraw.ymax - h + 4; y >= redraw.ymin; y -= h) { for (x = redraw.xmin; x <= redraw.xmax; x += w) { e = (idata[bimage].istore -> Render(idata[bimage].istore, x, y, 100, -1, -1)); if (e) break; } if (e) break; } idata[bimage].istore -> ModeChange(idata[bimage].istore); } #ifdef TRACE if (tl & (1u<<15)) Printf("image_fill_background: Successful\n"); #endif return e; } /*************************************************/ /* image_redraw() */ /* */ /* Does a high level redraw of an image, with */ /* an outline to show where the image should be */ /* if it isn't all plotted, or a slabbed box to */ /* display broken or unfetched images. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* Pointer to a RedrawWindowBlock */ /* struct which holds information */ /* about the current redraw session; */ /* */ /* Address of the token representing */ /* the image; */ /* */ /* The X offset in window coords (so */ /* OS units) of the left hand edge */ /* of the image; */ /* */ /* The Y offset in window coords (so */ /* OS units) of the bottom edge of */ /* the image. */ /*************************************************/ _kernel_oserror * image_redraw(browser_data * b, WimpRedrawWindowBlock * r, HStream * token, int x, int y) { int image, actual; int plotted = 0; #ifdef TRACE if (tl & (1u<<15)) Printf("image_redraw: Called with token %d\n", token); #endif /* Get the image number for the given token; 'actual' holds the actual */ /* image for the given token, which may cross reference 'image', which */ /* holds the actual render data. */ image = image_get_token_image_actual(b, token); if (image < 0) return NULL; actual = image; if (idata[actual].xref >= 0) image = idata[actual].xref; /* If the image has a fetched width and height, */ /* and the browser choices specify that images */ /* should be shown (or the image itself has a */ /* high priority show bit set), display part or */ /* all of the image. */ if ( image >= 0 && actual >= 0 && ( b->show_foreground || b->displayed == Display_External_Image || idata[actual].priority ) && idata[actual].canredraw && idata[image].istore /* Must check istore here, as if width / height are <= 0, */ && idata[image].istore->width > 0 /* the image library has no data for the image and will */ && idata[image].istore->height > 0 /* not have filled in (e.g.) the Render field. So you'd */ ) /* get a rather nasty abort trying to call it! */ { _kernel_oserror * e; browser_redrawing = b; image_redrawing = actual; e = idata[image].istore -> Render(idata[image].istore, x, y, 100, idata[actual].currw, idata[actual].currh); browser_redrawing = NULL; image_redrawing = -1; #ifdef STRICT_PARSER if (e) { e->errnum = Utils_Error_Custom_Message; show_error_ret(e); } #endif if (!e) plotted = 1; } if (!plotted) { /* If the image was not plotted, either due to error or because there's */ /* no data yet, plot a placeholder and any present ALT text instead. */ BBox box; const char * text; HStream * tp = NULL; image_get_image_size(b, actual, &box, 0); box.xmin += x; box.ymin += y; box.xmax += x - box.xmin; box.ymax += y - box.ymin; box.xmin &= ~(wimpt_dx() - 1); box.ymin &= ~(wimpt_dy() - 1); /* If an image, the ALT text wants to be from the actual image, not one */ /* that might be being cross referenced. */ tp = (ISOBJECT(tp)) ? token : idata[actual].token; text = (ISOBJECT(tp)) ? HtmlOBJECTstandby(tp) : tp->text; redraw_draw_placeholder(b, r, &box, tp, text); } #ifdef TRACE if (tl & (1u<<15)) Printf("image_redraw: Successful\n"); #endif return NULL; } /*************************************************/ /* image_tile_window() */ /* */ /* For a given redraw rectangle, tiles the */ /* background image within that rectangle. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window and image; */ /* */ /* Pointer to a WimpRedrawWindow- */ /* Block structure defining the area */ /* over which to tile the image; */ /* */ /* The X and Y coordinates to take */ /* as the tile origin, in OS units. */ /* */ /* Returns: 1 for success, 0 for failure. */ /*************************************************/ int image_tile_window(browser_data * b, WimpRedrawWindowBlock * r, int xorigin, int yorigin) { _kernel_oserror * e; int x, y, w, h; int xmin, ymax; int image; #ifdef TRACE if (tl & (1u<<15)) Printf("image_tile_window: Called, background_image = %d\n",b->background_image); #endif /* Can only tile if there's a background image defined and fully fetched */ if (b->background_image < 0) return 0; image = b->background_image; if (idata[image].xref >= 0) image = idata[image].xref; if (!idata[image].fetched || !idata[image].istore) return 0; /* Round the width and height to pixel boundaries */ w = idata[image].istore->width_os & ~(wimpt_dx() - 1); h = idata[image].istore->height_os & ~(wimpt_dy() - 1); if (w < 1 || h < 1) return 0; /* Work out the coordinates over which to tile the image */ xmin = coords_x_toworkarea(r->redraw_area.xmin,r); ymax = coords_y_toworkarea(r->redraw_area.ymax,r); xmin -= ((xmin - xorigin) % w); ymax -= ((ymax - yorigin) % h) + 1; xmin = coords_x_toscreen(xmin, r); ymax = coords_y_toscreen(ymax, r); /* Do a blank rectangle if the image is transparent (has a mask) */ if (idata[image].istore->transparent) { redraw_set_colour(redraw_backcol(b)); bbc_rectanglefill(r->redraw_area.xmin, r->redraw_area.ymin, r->redraw_area.xmax - r->redraw_area.xmin, r->redraw_area.ymax - r->redraw_area.ymin); } /* Render the image over the redraw region */ for (y = ymax - h + 4; y >= r->redraw_area.ymin - h; y -= h) { for (x = xmin; x <= r->redraw_area.xmax; x += w) { browser_redrawing = b; image_redrawing = image; e = idata[image].istore -> Render(idata[image].istore, x, y, 100, w, h); browser_redrawing = NULL; image_redrawing = -1; #ifdef STRICT_PARSER if (e) { e->errnum = Utils_Error_Custom_Message; show_error_ret(e); return 0; } #else if (e) return 0; #endif } } #ifdef TRACE if (tl & (1u<<15)) Printf("image_tile_window: Successful\n"); #endif return 1; } /*************************************************/ /* image_mark_as_redrawable() */ /* */ /* Flags that an image may now be redrawn (this */ /* is generally called because an unsized image */ /* has found its size, asked for a reformat, and */ /* that reformat is about to take place). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* The image number. */ /*************************************************/ void image_mark_as_redrawable(browser_data * b, int image) { /* Mark the image as redrawable */ if (idata[image].owner == b) idata[image].canredraw = 1; return; } /*************************************************/ /* image_token_reformatted() */ /* */ /* Called by a reformatter to signal an image is */ /* locked in a line array. Sets the image's */ /* canredraw flag and locs the image's size. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* Pointer to an HStream struct */ /* representing the image. */ /*************************************************/ void image_token_reformatted(browser_data * b, HStream * token) { int image; int xref; /* Get the image number */ image = image_get_token_image_actual(b, token); if (image < 0 || image > nimages) return; if (idata[image].xref >= 0) xref = idata[image].xref; else xref = image; /* Lock the image size */ image_lock_image_size(b, image); /* Exit if the image has no known real size yet - even if its size */ /* is fixed in the HTML, there's no point marking it as redrawable */ /* if there is no image data to redraw...! */ if ( !idata[xref].istore || idata[xref].istore->width_os <= 0 || idata[xref].istore->height_os <= 0 ) return; /* We do have some data and the reformatter has fixed the image in */ /* the line array, so mark it as redrawable now. */ image_mark_as_redrawable(b, image); return; } /*************************************************/ /* image_mode_change() */ /* */ /* Ensure images are up to date following a mode */ /* change. */ /*************************************************/ _kernel_oserror * image_mode_change(void) { #ifdef TRACE if (tl & (1u<<15)) Printf("image_mode_change: Called\n"); #endif if (nimages) { int i; /* For each image, call the image library's mode change handling function */ for (i = 0; i < nimages; i++) { if (idata[i].istore && idata[i].istore->width > 0 && idata[i].istore -> ModeChange) { #ifdef STRICT_PARSER /* Report any errors as Continue-only in strict parser mode */ _kernel_oserror * e; e = idata[i].istore -> ModeChange(idata[i].istore); #ifdef TRACE if (tl & (1u<<15)) Printf("image_mode_change: Exitting with error\n"); #endif if (e) return e; #else /* If not in Strict mode, ignore any errors */ idata[i].istore -> ModeChange(idata[i].istore); #endif } } } #ifdef TRACE if (tl & (1u<<15)) Printf("image_mode_change: Successful\n"); #endif return NULL; } /*************************************************/ /* image_data_offset() */ /* */ /* Returns the offset into the ddata block of */ /* the data associated with a given image. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image in */ /* question; */ /* */ /* An image number, from 0 to */ /* nimages - 1. */ /* */ /* Returns: Pointer to the data associated */ /* with that image (as a char *). */ /*************************************************/ static int image_data_offset(browser_data * b, int image) { int i, count = 0; #ifdef TRACE if (tl & (1u<<15)) Printf("image_data_offset: Called for image %d, and exitting\n",image); #endif if (image <= 0) return 0; /* Image 0's data offset will be zero... */ for (i = 0; i < image; count += idata[i].alloc, i++); return count; } /*************************************************/ /* image_count_fetches() */ /* */ /* Counts how many fetches are being performed */ /* for a given web page. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the page and images */ /* being fetched. */ /* */ /* Returns: The number of image fetches that */ /* are currently in progress (this */ /* may be zero, of course). */ /*************************************************/ static int image_count_fetches(browser_data * b) { int i, c = 0; #ifdef TRACE if (tl & (1u<<15)) Printf("image_count_fetches: Called\n"); #endif /* there is a fetch handle in their image_info structs. */ for (i = 0; i < nimages; i++) if (idata[i].handle && idata[i].owner == b) c++; #ifdef TRACE if (tl & (1u<<15)) Printf("image_count_fetches: Exitting with %d\n",c); #endif return c; } /*************************************************/ /* image_fetching() */ /* */ /* Returns 1 if there are any image fetches */ /* going on at all, else 0 (faster than using */ /* image_count_fetches above if you don't care */ /* how many fetches there are). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the inquiry. */ /* */ /* Returns: 1 if there are image fetches in */ /* progress, else 0. */ /*************************************************/ int image_fetching(browser_data * b) { int i; #ifdef TRACE if (tl & (1u<<15)) Printf("image_fetching: Called\n"); #endif for (i = 0; i < nimages; i++) { if (idata[i].handle && idata[i].owner == b) { #ifdef TRACE if (tl & (1u<<15)) Printf("image_fetching: Exitting, there is at least 1 fetch in progress\n"); #endif return 1; } } #ifdef TRACE if (tl & (1u<<15)) Printf("image_fetching: Exitting, no fetches in progress\n"); #endif return 0; } /*************************************************/ /* image_fetched() */ /* */ /* Determines if an image has been completely */ /* fetched or not. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* The image number, from 0 to */ /* nimages - 1. */ /* */ /* Returns: 1 if the image has been fetched */ /* completely, else 0. */ /*************************************************/ int image_fetched(browser_data * b, int image) { #ifdef TRACE if (tl & (1u<<15)) Printf("image_fetched: Called for image %d\n",image); #endif if (image >= 0) return (idata[image].fetched); #ifdef TRACE if (tl & (1u<<15)) Printf("image_fetched: Exitting with 0 (failed)\n"); #endif return 0; } /*************************************************/ /* image_token_fetched() */ /* */ /* Determines if an image has been completely */ /* fetched or not, based on a token representing */ /* the image. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* Pointer to a token representing */ /* the image. */ /* */ /* Returns: 1 if the image has been fetched */ /* completely, else 0. */ /*************************************************/ int image_token_fetched(browser_data * b, HStream * token) { int image; #ifdef TRACE if (tl & (1u<<15)) Printf("image_token_fetched: Called for token %p\n",token); #endif image = image_get_token_image_xref(b, token); #ifdef TRACE if (tl & (1u<<15)) { Printf("image_token_fetched: Represents image %d\n",image); Printf("image_token_fetched: Exitting through image_fetched\n"); } #endif return image_fetched(b, image); } /*************************************************/ /* image_total_bytes_fetched() */ /* */ /* Works out the total number of bytes fetched */ /* in images so far. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the images. */ /* */ /* Returns: The number of bytes fetched in */ /* image data at the moment of */ /* calling the function. */ /*************************************************/ int image_total_bytes_fetched(browser_data * b) { int count = 0; #ifdef TRACE if (tl & (1u<<15)) Printf("image_total_bytes_fetched: Called\n"); #endif /* Sum the fetched size of all images currently present, */ /* if there are any to count. */ if (nimages) { int image; for (image = 0; image < nimages; image++) { if (idata[image].owner == b) count += idata[image].bytesgot; } } #ifdef TRACE if (tl & (1u<<15)) Printf("image_total_bytes_fetched: Exitting with %d\n",count); #endif return count; } /*************************************************/ /* image_count_pending() */ /* */ /* Counts how many images are waiting to be */ /* fetched, but have been delayed for whatever */ /* reason. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the images. Any */ /* images in child windows are also */ /* included in the count. */ /* */ /* Returns: The number of images waiting to */ /* be fetched, counting any images */ /* in any child windows (if present) */ /*************************************************/ int image_count_pending(browser_data * b) { int i, c = 0; #ifdef TRACE if (tl & (1u<<15)) Printf("image_count_pending: Called for %p\n",b); #endif if (b->nchildren) { for (i = 0; i < b->nchildren; i++) { c += image_count_pending(b->children[i]); } } else { for (i = 0; i < nimages; i++) { if ( !idata[i].fetched && !idata[i].delayed && idata[i].xref < 0 && idata[i].owner == b ) c++; } } #ifdef TRACE if (tl & (1u<<15)) Printf("image_count_pending: Exitting with %d for %p\n",c,b); #endif return c; } /*************************************************/ /* image_count_specific_pending() */ /* */ /* Counts how many images are waiting to be */ /* fetched, but have been delayed for whatever */ /* reason, for just the specified browser (not */ /* including any child windows). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the images. */ /* */ /* Returns: The number of images waiting to */ /* be fetched in the given browser. */ /*************************************************/ int image_count_specific_pending(browser_data * b) { int i, c = 0; #ifdef TRACE if (tl & (1u<<15)) Printf("image_count_specific_pending: Called for %p\n",b); #endif for (i = 0; i < nimages; i++) { if ( !idata[i].fetched && !idata[i].delayed && idata[i].xref < 0 && idata[i].owner == b ) c++; } #ifdef TRACE if (tl & (1u<<15)) Printf("image_count_specific_pending: Exitting with %d for %p\n",c,b); #endif return c; } /*************************************************/ /* image_count_delayed() */ /* */ /* Returns the number of images which have been */ /* delayed (could be fetched, but have been set */ /* up to not fetch yet). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the images. */ /*************************************************/ int image_count_delayed(browser_data * b) { int count = 0; if (nimages) { int i; for(i = 0; i < nimages; i++) { if ( ( !idata[i].fetched && idata[i].delayed ) || ( idata[i].fetched && !idata[i].success && !idata[i].istore ) ) count++; } } return count; } /*************************************************/ /* image_plot_started() */ /* */ /* Returns 1 if a given image in a given browser */ /* has had some plotting done for it (so the */ /* placeholder box and possibly ALT text will no */ /* longer be visible for it). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* The number of the image. */ /* */ /* Returns: 1 if the image has been partially */ /* or completely plotted, else 0. */ /*************************************************/ int image_plot_started(browser_data * b, int image) { if (image < 0) return 0; if (!idata[image].istore) return 0; if (!idata[image].canredraw) return 0; if (idata[image].istore->width > 0) return 1; else return 0; } /*************************************************/ /* image_token_plot_started() */ /* */ /* Returns 1 if a given image in a given browser */ /* has had some plotting done for it (so the */ /* placeholder box and possibly ALT text will no */ /* longer be visible for it). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* Pointer to a token representing */ /* the image. */ /* */ /* Returns: 1 if the image has been partially */ /* or completely plotted, else 0. */ /*************************************************/ int image_token_plot_started(browser_data * b, HStream * token) { int image; image = image_get_token_image_xref(b, token); return image_plot_started(b, image); } /*************************************************/ /* image_get_token_image_xref() */ /* */ /* Finds the image number associated with a */ /* given token. The number will be the image */ /* that actually has the associated image data, */ /* not just one that cross references this data. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* The token address. */ /* */ /* Returns: The image number which references */ /* that token, or -1 if none found. */ /*************************************************/ static int image_get_token_image_xref(browser_data * b, HStream * token) { int i, found = -1; #ifdef TRACE if (tl & (1u<<15)) Printf("image_get_token_image_xref: Called\n"); #endif /* If there are any images to deal with... */ if (nimages) { /* ...then look through them all, seeing if the */ /* image is referred to by the given token. */ for (i = 0; i < nimages; i++) { if (idata[i].token == token && idata[i].owner == b) { found = i; break; } } /* If an appropriate image was found, ensure it */ /* is the one with render data attached, not */ /* just a cross reference image. */ if (found >= 0) { if (idata[found].xref >= 0) found = idata[found].xref; } } /* Return the image number, or -1 for failure */ #ifdef TRACE if (tl & (1u<<15)) Printf("image_get_token_image_xref: Exitting with %d\n", found); #endif return found; } /*************************************************/ /* image_get_token_image_actual() */ /* */ /* As image_get_token_image, but returns the */ /* exact image number represented by the given */ /* token rather than the cross referenced one */ /* with the pointers to the image data. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* The token address. */ /* */ /* Returns: The image number which references */ /* that token, or -1 if none found. */ /*************************************************/ static int image_get_token_image_actual(browser_data * b, HStream * token) { int i, found = -1; #ifdef TRACE if (tl & (1u<<15)) Printf("image_get_token_image_actual: Called\n"); #endif /* If there are any images to deal with... */ if (nimages) { /* ...then look through them all, seeing if the */ /* image is referred to by the given token. */ for (i = 0; i < nimages; i++) { if (idata[i].token == token && idata[i].owner == b) { found = i; break; } } } /* Return the image number, or -1 for failure */ #ifdef TRACE if (tl & (1u<<15)) Printf("image_get_token_image_actual: Exitting with %d\n", found); #endif return found; } /*************************************************/ /* image_lock_image_size() */ /* */ /* Ask image_get_image_size for the size of an */ /* image, making sure currw and currh are -1 so */ /* that it works the size out from first */ /* principles, then set that size jn currw and */ /* currh for future reference. */ /* */ /* Designed to be called by the reformatter when */ /* it encounters an image and stores it in a */ /* line array. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* Number of the image. */ /*************************************************/ static void image_lock_image_size(browser_data * b, int image) { BBox size; /* Rediscover the image size - so ignore any stored */ /* values in currw and currh. */ if (image_get_image_size(b, image, &size, 1)) return; /* Now lock the size by (re)storing it in currw and currh */ idata[image].currw = size.xmax; idata[image].currh = size.ymax; return; } /*************************************************/ /* image_lock_token_image_size() */ /* */ /* As image_lock_image_size, but takes a pointer */ /* to an HStream rather than an image number. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* Pointer to an HStream struct */ /* representing the image. */ /*************************************************/ void image_lock_token_image_size(browser_data * b, HStream * token) { int image = image_get_token_image_actual(b, token); if (image < 0) return; image_lock_image_size(b, image); return; } /*************************************************/ /* image_unlock_image_size() */ /* */ /* Set currw and currh for an image to -1, so */ /* that image_get_image_size will start working */ /* the size out from first principles rather */ /* than using any prestored value. */ /* */ /* Designed to be called by the reformatter when */ /* it destroys a line array with images in it. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* Number of the image. */ /*************************************************/ static void image_unlock_image_size(browser_data * b, int image) { idata[image].currw = -1; idata[image].currh = -1; return; } /*************************************************/ /* image_unlock_token_image_size() */ /* */ /* As image_unlock_image_size, but takes a */ /* pointer to an HStream rather than an image */ /* number. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* Pointer to an HStream struct */ /* representing the image. */ /*************************************************/ void image_unlock_token_image_size(browser_data * b, HStream * token) { int image = image_get_token_image_actual(b, token); if (image < 0) return; image_unlock_image_size(b, image); return; } /*************************************************/ /* image_get_image_size() */ /* */ /* Returns the size of an image in OS units. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* The image number, from 0 to */ /* nimages - 1, which may just be a */ /* cross referencing image (i.e. has */ /* no directly associated data); */ /* */ /* Pointer to a BBox into which the */ /* size information is placed (xmax */ /* and ymax fields filled with */ /* width and height currently - xmin */ /* and ymin always zero); */ /* */ /* 1 to ignore the image's currw and */ /* currh fields, else use those if */ /* they are present. */ /* */ /* Assumes: The BBox pointer is not NULL. */ /*************************************************/ static _kernel_oserror * image_get_image_size(browser_data * b, int image, BBox * box, int ignore_stored) { int awidth = 0; /* Available page / cell width */ int aheight = 0; /* Same, but height */ int real_width = 0; /* The actual image dimensions (OS units) */ int real_height = 0; /* from ImageLib */ int width_os = 0; /* The image size as it will appear on the page, taking */ int height_os = 0; /* account of any WIDTH or HEIGHT attributes in the HTML */ int actual; int subtract; HStream * tp = idata[image].token; #ifdef TRACE if (tl & (1u<<15)) Printf("image_get_image_size: Called for image %d\n",image); if (!box) { erb.errnum = Utils_Error_Custom_Normal; strcpy(erb.errmess,"Null pointer to BBox given to image_get_image_size"); return &erb; } if (image < 0) { erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess,"Invalid image number %d passed to image_get_image_size (nimages = %d)", image, nimages); return &erb; } if (image > nimages) { erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess,"Out of range image number %d passed to image_get_image_size (nimages = %d)", image, nimages); return &erb; } #else if (!box || image < 0 || image >= nimages) return NULL; #endif /* Zero contents of the BBox */ memset(box, 0, sizeof(BBox)); /* Can we return a prestored size? */ if (!ignore_stored && idata[image].currw >= 0 && idata[image].currh >= 0) { box->xmax = idata[image].currw; box->ymax = idata[image].currh; #ifdef TRACE if (tl & (1u<<15)) Printf("image_get_image_size: Successful\n"); #endif return NULL; } /* If this cross references another image, get its number */ if (idata[image].xref >= 0) actual = idata[image].xref; else actual = image; /* Work out the available width / height */ if (!tp->parent) { awidth = redraw_display_width (b, NULL); aheight = redraw_display_height(b, NULL); subtract = redraw_left_gap (b, b->cell, tp) + redraw_right_gap(b, b->cell, tp); } else { reformat_cell * cell = tokenutils_token_cell(b, tp); if (!cell) cell = b->cell; if ( TD_HAS_WIDTH (tp->parent) && TD_WIDTH_UNITS(tp->parent) == UNITS_PIXELS ) awidth = TD_WIDTH(tp->parent) * 2; /* 1 'web pixel' = 2 OS units */ if ( TD_HAS_HEIGHT (tp->parent) && TD_HEIGHT_UNITS(tp->parent) == UNITS_PIXELS ) aheight = TD_HEIGHT(tp->parent) * 2; /* 1 'web pixel' = 2 OS units */ subtract = redraw_left_gap (b, cell, tp) + redraw_right_gap(b, cell, tp); } convert_to_os(subtract, &subtract); awidth -= subtract; if (awidth < 0) awidth = 0; /* Try to get the image size from the actual image data (i.e. ImageLib) */ if (idata[actual].istore) { real_width = idata[actual].istore->width_os; real_height = idata[actual].istore->height_os; } /* If we don't seem to have anything, try the image history */ if (real_width <= 0 && real_height <= 0) { imghistory_return_size((const char **) &ddata, image_data_offset(idata[actual].owner, actual), &real_width, &real_height); } /* The image may have a specific width and height given in */ /* the HTML. */ if (tp->tagno != TAG_INPUT) { /* The OBJECT macros work for IMGs as well as OBJECTs */ if (OBJECT_HAS_WIDTH(tp)) { switch (OBJECT_WIDTH_UNITS(tp)) { default: case UNITS_PIXELS: width_os = OBJECT_WIDTH(tp) * 2; break; case UNITS_PERCENT: width_os = awidth * OBJECT_WIDTH(tp) / 100; break; } } if (OBJECT_HAS_HEIGHT(tp)) { switch (OBJECT_HEIGHT_UNITS(tp)) { default: case UNITS_PIXELS: height_os = OBJECT_HEIGHT(tp) * 2; break; case UNITS_PERCENT: height_os = aheight * OBJECT_HEIGHT(tp) / 100; break; } } } /* If width and height are both <= zero, try to find them out */ /* from the image data itself */ if (width_os <= 0 && height_os <= 0) { if (real_width > 0 && real_height > 0) { width_os = real_width; height_os = real_height; } } /* If width and height are still both <= zero, must go for a */ /* default placeholder size instead. */ if (width_os <= 0 && height_os <= 0) goto image_get_image_size_placeholder; /* We may have final values... */ if (width_os > 0 && height_os > 0) { box->xmax = width_os; box->ymax = height_os; #ifdef TRACE if (tl & (1u<<15)) Printf("image_get_image_size: Successful\n"); #endif return NULL; } /* Otherwise, scale one from the other if possible - for this we must */ /* have a real image size to get the proportions from */ if (real_width <= 0 || real_height <= 0) goto image_get_image_size_placeholder; /* We know the true image size and can thus scale whichever dimension is */ /* missing for the on-page size from the proportions of the real image. */ if (width_os <= 0) { /* Work out width based on the height */ width_os = (height_os * real_width) / real_height; } else { /* Work out height based on the width */ height_os = (width_os * real_height) / real_width; } /* We should now have both values, but if not, drop through to the */ /* placeholder size routine. */ if (width_os > 0 && height_os > 0) { box->xmax = width_os; box->ymax = height_os; #ifdef TRACE if (tl & (1u<<15)) Printf("image_get_image_size: Successful\n"); #endif return NULL; } image_get_image_size_placeholder: { /* Don't have precise image dimensions, so work it out from */ /* any ALT text present or go for a default value. */ HStream * tp = NULL; const char * text; if (image >= 0) tp = idata[image].token; text = (ISOBJECT(tp)) ? HtmlOBJECTstandby(tp) : tp->text; reformat_get_placeholder_size(b, tp, text, box); } #ifdef TRACE if (tl & (1u<<15)) Printf("image_get_image_size: Successful\n"); #endif return NULL; } /*************************************************/ /* image_get_token_image_size() */ /* */ /* As image_get_image_size above, but takes a */ /* token address rather than an image number. */ /* The default 'unknown' image size will be */ /* returned if no image could be associated with */ /* the token (no error will be raised). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* A token address; */ /* */ /* Pointer to a BBox into which the */ /* size information is placed. */ /* */ /* Assumes: The BBox pointer is not NULL. */ /*************************************************/ _kernel_oserror * image_get_token_image_size(browser_data * b, HStream * token, BBox * box) { int image; #ifdef TRACE if (tl & (1u<<15)) Printf("image_get_token_image_size: Called\n"); #endif image = image_get_token_image_actual(b, token); #ifdef TRACE if (tl & (1u<<15)) Printf("image_get_token_image_size: Exitting through image_get_image_size\n"); #endif return image_get_image_size(b, image, box, 0); } /*************************************************/ /* image_set_image_size() */ /* */ /* Sets the size of an image (currw, currh */ /* fields) in OS units. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* The image number, from 0 to */ /* nimages - 1, which may just be a */ /* cross referencing image (i.e. has */ /* no directly associated data); */ /* */ /* Pointer to a BBox from which the */ /* size information is read (xmax - */ /* xmin, ymax - ymin). */ /* */ /* Assumes: The BBox pointer is not NULL. */ /*************************************************/ static _kernel_oserror * image_set_image_size(browser_data * b, int image, BBox * box) { #ifdef TRACE if (tl & (1u<<15)) Printf("image_set_image_size: Called for image %d\n",image); if (!box) { erb.errnum = Utils_Error_Custom_Normal; strcpy(erb.errmess,"Null pointer to BBox given to image_set_image_size"); return &erb; } if (image < 0) { erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess,"Invalid image number %d passed to image_set_image_size (nimages = %d)", image, nimages); return &erb; } if (image > nimages) { erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess,"Out of range image number %d passed to image_set_image_size (nimages = %d)", image, nimages); return &erb; } #else if (image < 0 || image >= nimages) return NULL; #endif idata[image].currw = box->xmax - box->xmin; idata[image].currh = box->ymax - box->ymin; #ifdef TRACE if (tl & (1u<<15)) Printf("image_set_image_size: Successful\n"); #endif return NULL; } /*************************************************/ /* image_set_token_image_size() */ /* */ /* As image_set_image_size above, but takes a */ /* token address rather than an image number. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* A token address; */ /* */ /* Pointer to a BBox from which the */ /* size information is read (xmax - */ /* xmin, ymax - ymin). */ /* */ /* Assumes: The BBox pointer is not NULL. */ /*************************************************/ _kernel_oserror * image_set_token_image_size(browser_data * b, HStream * token, BBox * box) { int image; #ifdef TRACE if (tl & (1u<<15)) Printf("image_set_token_image_size: Called\n"); #endif image = image_get_token_image_actual(b, token); #ifdef TRACE if (tl & (1u<<15)) Printf("image_set_token_image_size: Exitting through image_set_image_size\n"); #endif return image_set_image_size(b, image, box); } /*************************************************/ /* image_get_token_actual_size() */ /* */ /* Returns the width and height of an image, in */ /* pixels, or 0 and 0 if it the size isn't known */ /* yet. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* A token address; */ /* */ /* Pointer to an int, in which the */ /* width is written; */ /* */ /* Pointer to an int, in which the */ /* height is written. */ /* */ /* Assumes: All pointers are non-NULL and */ /* valid. */ /*************************************************/ void image_get_token_actual_size(browser_data * b, HStream * token, int * w, int * h) { int image; #ifdef TRACE if (tl & (1u<<15)) Printf("image_get_token_actual_size: Called\n"); #endif image = image_get_token_image_xref(b, token); if (idata[image].istore) { *w = idata[image].istore -> width_os / 2; *h = idata[image].istore -> height_os / 2; } else { *w = 0; *h = 0; } #ifdef TRACE if (tl & (1u<<15)) Printf("image_get_token_actual_size: Exitting with width %d, height %d\n", *w, *h); #endif return; } /*************************************************/ /* image_get_back_image_size() */ /* */ /* Finds out the size of the background image in */ /* OS units. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the background image; */ /* Pointer to a BBox into which the */ /* size information is placed. */ /* */ /* Assumes: That neither pointer is NULL. */ /*************************************************/ _kernel_oserror * image_get_back_image_size(browser_data * b, BBox * box) { #ifdef TRACE if (tl & (1u<<15)) Printf("image_get_back_image_size: Called, exitting through image_get_image_size\n"); #endif return (image_get_image_size(b, b->background_image, box, 1)); } /*************************************************/ /* image_get_token_image_position() */ /* */ /* Returns the x and y fields of the image_info */ /* structure for a given image. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* Pointer to the token representing */ /* the image; */ /* */ /* Pointer to an int, in which the */ /* X coordinate is returned; */ /* */ /* Pointer to an int, in which the */ /* Y coordinate is returned. */ /* */ /* Returns: 1 if the image could not be found */ /* from the given token, or 0 for */ /* success. */ /* */ /* Assumes: Neither pointer is NULL. */ /*************************************************/ int image_get_token_image_position(browser_data * b, HStream * t, int * x, int * y) { int image = image_get_token_image_actual(b, t); if (image < 0) return 1; *x = idata[image].x; *y = idata[image].y; return 0; } /*************************************************/ /* image_set_token_image_position() */ /* */ /* Sets the x and y fields of the image_info */ /* structure for a given image, so that it may */ /* be [partially] plotted during a fetch. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* Pointer to the token representing */ /* the image; */ /* */ /* X coordinate (window coords); */ /* */ /* Y coordinate (window coords). */ /* */ /* Returns: Number of the image that was */ /* changed, or -1 if none could be */ /* found for the given token. */ /*************************************************/ int image_set_token_image_position(browser_data * b, HStream * t, int x, int y) { int image = image_get_token_image_actual(b, t); /* If an image was found, set the x and y coordinates */ if (image >= 0) { idata[image].x = x; idata[image].y = y; } return image; } /*************************************************/ /* image_get_background_image_url() */ /* */ /* Returns the URL of a browser's current */ /* background image. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the background; */ /* */ /* Pointer to a buffer to take the */ /* URL; */ /* */ /* Size of the buffer. */ /*************************************************/ void image_get_background_image_url(browser_data * b, char * buffer, int size) { int offset; int old_budge; int image = b ? b->background_image : -1; if (!buffer || size < 1) return; *buffer = 0; if (size < 2 || image < 0 || image >= nimages) return; /* Find the offset of the URL in the 'ddata' block */ offset = image_data_offset(b, image); /* Lock flex */ old_budge = flex_set_budge(0); /* Copy the URL in, ensuring termination */ strncpy(buffer, ddata + offset, size); buffer[size - 1] = 0; /* Restore flex's previous budge state */ flex_set_budge(old_budge); /* Finished */ return; } /*************************************************/ /* image_convert_to_pixels() */ /* */ /* Converts a given OS unit X and Y size to */ /* pixel size, for a given image. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* Pointer to the token representing */ /* the image; */ /* */ /* Pointer to an int which already */ /* contains the X size in OS units, */ /* which will have the pixel size */ /* returned into it; */ /* */ /* A similar pointer for the Y size. */ /* */ /* Assumes: That no pointer is NULL. */ /*************************************************/ void image_convert_to_pixels(browser_data * b, HStream * token, int * x, int * y) { int image; #ifdef TRACE if (tl & (1u<<15)) Printf("image_convert_to_pixels: Called\n"); #endif image = image_get_token_image_xref(b, token); /* If the library has got enough information to fill in the */ /* width of the image, proceed with the conversion */ if (image >= 0 && idata[image].istore->width > 0) { *x = *x * idata[image].istore->width / idata[image].istore->width_os; *y = *y * idata[image].istore->height / idata[image].istore->height_os; } else { /* Can't find image - maybe it's not loaded... Assume 2 OS units per pixel. */ *x = *x / 2; *y = *y / 2; } #ifdef TRACE if (tl & (1u<<15)) Printf("image_convert_to_pixels: Successful\n"); #endif } /*************************************************/ /* image_return_click_offset() */ /* */ /* Calculate the coordinates of a click on an */ /* image in pixels from the top left corner, as */ /* required by image maps and image input */ /* fields. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* Pointer to a token representing */ /* the image; */ /* */ /* A WimpGetPointerInfo block */ /* pointer describing the mouse */ /* pointer's position; */ /* */ /* Pointer to an int, in which the */ /* x offset is returned; */ /* */ /* Pointer to an int, in which the */ /* y offset is returned. */ /* */ /* Assumes: That the int pointers are not */ /* NULL. */ /*************************************************/ _kernel_oserror * image_return_click_offset(browser_data * b, HStream * t, WimpGetPointerInfoBlock * i, int * x, int * y) { _kernel_oserror * e; WimpGetWindowStateBlock s; BBox box; /* Get the image's size and position on screen */ s.window_handle = b->window_handle; e = wimp_get_window_state(&s); if (e) return e; e = image_get_token_image_size(b, t, &box); if (e) return e; if (image_get_token_image_position(b, t, x, y)) return NULL; *x = coords_x_toscreen(*x, (WimpRedrawWindowBlock *) &s); *y = coords_y_toscreen(*y, (WimpRedrawWindowBlock *) &s); /* Get the offset of the pointer position from the top left */ /* of the image in ox and oy */ *x = i->x - *x; *y = *y + (box.ymax - box.ymin) - i->y; image_convert_to_pixels(b, t, x, y); return NULL; } /*************************************************/ /* image_can_be_saved_as_sprite */ /* */ /* Returns 1 if a given image has data and can */ /* be saved (the DumpSprite function is */ /* present), else 0. */ /* */ /* The image may cross reference another with */ /* the actual data. If so, the return value will */ /* be referring to that cross referenced image. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* owning the image; */ /* */ /* Number of the image. */ /* */ /* Returns: 1 if the image can be saved as a */ /* sprite, else 0. */ /*************************************************/ static int image_can_be_saved_as_sprite(browser_data * b, int i) { if ( idata[i].istore && idata[i].istore->DumpSprite && idata[i].istore->width_os > 0 && idata[i].istore->height_os > 0 ) return 1; return 0; } /*************************************************/ /* image_token_can_be_saved_as_sprite */ /* */ /* Returns 1 if a given image has data and can */ /* be saved (the DumpSprite function is */ /* present), else 0. */ /* */ /* The image may cross reference another with */ /* the actual data. If so, the return value will */ /* be referring to that cross referenced image. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* owning the image; */ /* */ /* Pointer to the token representing */ /* the image, or NULL for the page's */ /* background image. */ /* */ /* Returns: 1 if the image can be saved as a */ /* sprite, else 0. */ /*************************************************/ int image_token_can_be_saved_as_sprite(browser_data * b, HStream * image) { int i; /* Find the image number */ if (!image) i = b->background_image; else i = image_get_token_image_xref(b, image); /* Complain if we can't in TRACE builds */ if (i < 0) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "In image_token_can_be_saved_as_sprite, can't find an image that is represented by the token %p for the browser %p.", image, b); show_error_ret(&erb); #endif return 0; } /* Can it be saved? */ return image_can_be_saved_as_sprite(b, i); } /*************************************************/ /* image_export_sprite() */ /* */ /* Saves an image represented by the given token */ /* as a sprite at the given path. */ /* */ /* Parameters: Pointer to the path to save to; */ /* */ /* Pointer to a browser_data struct */ /* that owns the image; */ /* */ /* Pointer to the token representing */ /* the image, or NULL for the page's */ /* background image. */ /*************************************************/ _kernel_oserror * image_export_sprite(char * path, browser_data * b, HStream * image) { int i; if (!path || !*path) return NULL; save_record_path(path); /* Find the image number */ if (!image) i = b->background_image; else i = image_get_token_image_xref(b, image); /* Must have image data... */ if (!image_can_be_saved_as_sprite(b, i)) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "There is no data for this image in image_export_sprite."); return &erb; #endif return NULL; } /* Export the image */ return idata[i].istore -> DumpSprite(idata[i].istore, path, -1); } /*************************************************/ /* image_sprite_size() */ /* */ /* Returns the size that the given image would */ /* be as a sprite on disc. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* that owns the image; */ /* */ /* Pointer to the token representing */ /* the image, or NULL for the page's */ /* background image. */ /* */ /* Returns: The size the image would be as a */ /* sprite file saved to disc, in */ /* bytes (a guess only). */ /*************************************************/ int image_sprite_size(browser_data * b, HStream * image) { /* ...And for now, we don't know. */ return 4096; } /*************************************************/ /* image_export_original() */ /* */ /* Saves an image represented by the given token */ /* in its original format to the given path. */ /* */ /* Parameters: Pointer to the path to save to; */ /* */ /* Pointer to a browser_data struct */ /* that owns the image; */ /* */ /* Pointer to the token representing */ /* the image, or NULL for the page's */ /* background image. */ /*************************************************/ _kernel_oserror * image_export_original(char * path, browser_data * b, HStream * image) { char url[Limits_URL]; int i, offset; if (!path || !*path) return NULL; save_record_path(path); /* Find the image number */ if (!image) i = b->background_image; else i = image_get_token_image_xref(b, image); /* Must have image data... */ offset = image_data_offset(b, i); if (!ddata[offset]) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "There is no URL for this image in image_export_original."); return &erb; #endif return NULL; } /* Start a fetch for this URL */ strncpy(url, ddata + offset, sizeof(url)); url[sizeof(url) - 1] = 0; RetError(windows_create_browser(url, NULL, NULL, NULL, Windows_CreateBrowser_SaveToFile)); /* Set the title to the save pathname */ { char title[Limits_Title]; StrNCpy0(title, path); /* Don't treat any errors here as fatal */ show_error_ret(window_set_title(0, last_browser->self_id, title)); } /* Open the file */ last_browser->save_file = fopen(path, "wb"); if (!last_browser->save_file) { fetch_stop(last_browser, 0); RetLastE; } else { int bytes; /* Set the filetype to DEADDEAD, to represent an incomplete */ /* file (particularly good on later Filers, which display */ /* a special sprite for this). */ _swix(OS_File, _INR(0,2), 2, /* Set load address */ path, 0xdeaddead); _swix(OS_File, _INR(0,1) | _IN(3), 3, /* Set exec address */ path, 0xdeaddead); if (last_browser->source) { /* Any data in the source store represents already */ /* fetched bits of the file. Must lock flex down */ /* over the save to make sure the heap doesn't */ /* shift over the call to fwrite. */ flex_set_budge(0); bytes = fwrite(last_browser->source, 1, flex_size((flex_ptr) &last_browser->source), last_browser->save_file); flex_set_budge(1); /* If we didn't transfer as much as we expected, complain */ if (bytes != flex_size((flex_ptr) &last_browser->source)) { /* Report any errors */ fetch_stop(last_browser, 0); RetLastE; } else { /* Otherwise, get rid of the data in the source store */ /* as it's been written to the file. */ flex_free((flex_ptr) &last_browser->source); last_browser->source = NULL; } } } return NULL; } /*************************************************/ /* image_original_size() */ /* */ /* Returns the size of a given image in its */ /* original format. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* that owns the image; */ /* */ /* Pointer to the token representing */ /* the image, or NULL for the page's */ /* background image. */ /* */ /* Returns: The size of the original image, */ /* in bytes (a guess only). */ /*************************************************/ int image_original_size(browser_data * b, HStream * image) { int size = image_draw_file_size(b, image, 0); if (size <= 0) return 0; else return size + 12; } /*************************************************/ /* image_to_draw_file() */ /* */ /* As image_redraw, but will output a sprite */ /* to the Draw file being output by SaveDraw.c */ /* (which must be active at the time this is */ /* called). Assumes that the appropriate Draw */ /* file object header has already been written. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* Pointer to a RedrawWindowBlock */ /* struct which holds information */ /* about the current redraw session; */ /* */ /* Address of the token representing */ /* the image; */ /* */ /* The X offset in window coords (so */ /* OS units) of the left hand edge */ /* of the image; */ /* */ /* The Y offset in window coords (so */ /* OS units) of the bottom edge of */ /* the image; */ /* */ /* 1 to assume the image is already */ /* present as a sprite in */ /* 'unique_name' else create the */ /* image first. */ /* */ /* Returns: 0 if failed, 1 if successful; if */ /* failed, caller should try some */ /* alternative representation (e.g. */ /* write out a placeholder). */ /*************************************************/ int image_to_draw_file(browser_data * b, WimpRedrawWindowBlock * r, HStream * token, int x, int y, int dont_create) { int image, actual; int plotted = 0; #ifdef TRACE if (tl & (1u<<15)) Printf("image_to_draw_file: Called with token %d\n", token); #endif /* Get the image number for the given token; 'actual' holds the actual */ /* image for the given token, which may cross reference 'image', which */ /* holds the actual render data. */ image = image_get_token_image_actual(b, token); if (image < 0) return NULL; actual = image; if (idata[actual].xref >= 0) image = idata[actual].xref; /* If the image has a fetched width and height, */ /* and the browser choices specify that images */ /* should be shown (or the image itself has a */ /* high priority show bit set), display part or */ /* all of the image. */ if ( image >= 0 && actual >= 0 && ( b->show_foreground || b->displayed == Display_External_Image || idata[actual].priority ) && idata[actual].canredraw && idata[image].istore /* Must check istore here, as if width / height are <= 0, */ && idata[image].istore->width > 0 /* the image library has no data for the image and will */ && idata[image].istore->height > 0 /* not have filled in (e.g.) the Render field. So you'd */ && idata[image].istore->DumpSprite /* get a rather nasty abort trying to call it! */ && image_can_be_saved_as_sprite(b, image) // && idata[image].istore->StartExport // && idata[image].istore->Export // && idata[image].istore->EndExport ) { FILE * input = NULL; _kernel_oserror * e = NULL; int written, ok; char buffer[10240]; // int image_size = 0; browser_redrawing = b; image_redrawing = actual; if (!dont_create || !unique_name) { unique_name = malloc(Limits_OS_Pathname); if (unique_name) { protocols_util_make_unique_name(unique_name, Limits_OS_Pathname); /* Output the sprite, skipping the first 12 bytes of sprite area header */ e = idata[image].istore -> DumpSprite(idata[image].istore, unique_name, -1); } } if (e || !unique_name) ok = 0; else { ok = 1; input = fopen(unique_name, "rb"); if (!input || !input->__file) ok = 0; } if (ok) { e = _swix(OS_Args, _INR(0,2), 1, input->__file, 12); if (e) ok = 0; } if (ok) { do { e = _swix(OS_GBPB, _INR(0,3) | _OUT(3), 4, input->__file, buffer, sizeof(buffer), &written); if (!e && written < sizeof(buffer)) ok = savedraw_write_bytes(buffer, sizeof(buffer) - written); else ok = e ? 0 : 1; } while (!e && written < sizeof(buffer)); } if (input) { fclose(input); if (unique_name) remove(unique_name); free(unique_name); unique_name = NULL; } // e = idata[image].istore -> StartExport(idata[image].istore, &image_size); // ok = 1; // // written = 12; // e = idata[image].istore -> Export(idata[image].istore, buffer, &written); // // while (!e && ok && written) // { // written = sizeof(buffer); // // e = idata[image].istore -> Export(idata[image].istore, buffer, &written); // // if (!e) ok = savedraw_write_bytes(buffer, written); // else ok = 0; // } // // e = idata[image].istore -> EndExport(idata[image].istore); /* Flag if we succeeded */ browser_redrawing = NULL; image_redrawing = -1; if (!e && ok) plotted = 1; } #ifdef TRACE if (tl & (1u<<15)) Printf("image_to_draw_file: Exitting with %d\n", plotted); #endif return plotted; } /*************************************************/ /* image_draw_file_size() */ /* */ /* Returns the amount of data that */ /* image_to_draw_file would write to the output */ /* file. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* Address of the token representing */ /* the image; */ /* */ /* 1 to not delete the scrap file */ /* in 'unique_name' that this */ /* writes, else 0. */ /* */ /* Returns: 0 if failed, else the amount of */ /* data that image_to-draw_file */ /* would write to the output file. */ /*************************************************/ int image_draw_file_size(browser_data * b, HStream * token, int dont_delete) { int image, actual; /* Get the image number for the given token; 'actual' holds the actual */ /* image for the given token, which may cross reference 'image', which */ /* holds the actual render data. */ image = image_get_token_image_actual(b, token); if (image < 0) return NULL; actual = image; if (idata[actual].xref >= 0) image = idata[actual].xref; /* If the image has a fetched width and height, */ /* and the browser choices specify that images */ /* should be shown (or the image itself has a */ /* high priority show bit set), display part or */ /* all of the image. */ if ( image >= 0 && actual >= 0 && ( b->show_foreground || b->displayed == Display_External_Image || idata[actual].priority ) && idata[actual].canredraw && idata[image].istore /* Must check istore here, as if width / height are <= 0, */ && idata[image].istore->width > 0 /* the image library has no data for the image and will */ && idata[image].istore->height > 0 /* not have filled in (e.g.) the Render field. So you'd */ && idata[image].istore->DumpSprite /* get a rather nasty abort trying to call it! */ && image_can_be_saved_as_sprite(b, image) // && idata[image].istore->StartExport // && idata[image].istore->Export // && idata[image].istore->EndExport ) { _kernel_oserror * e = NULL; int image_size = 0; if (unique_name) { remove(unique_name); free(unique_name); unique_name = NULL; } unique_name = malloc(Limits_OS_Pathname); if (unique_name) { protocols_util_make_unique_name(unique_name, Limits_OS_Pathname); e = idata[image].istore -> DumpSprite(idata[image].istore, unique_name, -1); } if (!e && unique_name) { e = _swix(OS_File, _INR(0,1) | _OUT(4), 17, unique_name, &image_size); if (!dont_delete) { remove(unique_name); free(unique_name); unique_name = NULL; } } // e = idata[image].istore -> StartExport(idata[image].istore, &image_size); // // if (!e) idata[image].istore -> EndExport(idata[image].istore); /* Remember, we'd skip the first 12 bytes of sprite area header */ /* in the output Draw file. */ if (!e && image_size > 12) return image_size - 12; } return 0; } /*************************************************/ /* image_tile_to_draw() */ /* */ /* For a given redraw rectangle, tiles the */ /* background image within that rectangle to a */ /* Draw file. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window and image; */ /* */ /* Pointer to a WimpRedrawWindow- */ /* Block structure defining the area */ /* over which to tile the image; */ /* */ /* The X and Y coordinates to take */ /* as the tile origin, in OS units; */ /* */ /* Pointer to an int if wanting to */ /* know the size of the item only, */ /* else NULL (NB the int contents */ /* are not updated - the pointer is */ /* acting like a flag, basically). */ /* */ /* Returns: 0 if failed, else the amount of */ /* data that was output. */ /*************************************************/ int image_tile_to_draw(browser_data * b, WimpRedrawWindowBlock * r, int xorigin, int yorigin, int * size) { char name[Limits_OS_Pathname]; _kernel_oserror * e; FILE * input = NULL; void * block = NULL; int image; int tsize = 0; int x, y, w, h; int xmin, ymax; int image_size; /* Can only tile if there's a background image defined and fully fetched */ if (b->background_image < 0) return 0; image = b->background_image; if (idata[image].xref >= 0) image = idata[image].xref; if (!idata[image].fetched || !idata[image].istore) return 0; /* Round the width and height to pixel boundaries */ w = idata[image].istore->width_os & ~1; h = idata[image].istore->height_os & ~1; if (w < 1 || h < 1) return 0; /* Work out the coordinates over which to tile the image */ xmin = coords_x_toworkarea(r->redraw_area.xmin,r); ymax = coords_y_toworkarea(r->redraw_area.ymax,r); xmin -= ((xmin - xorigin) % w); ymax -= ((ymax - yorigin) % h) + 1; xmin = coords_x_toscreen(xmin, r); ymax = coords_y_toscreen(ymax, r); /* Do a blank rectangle if the image is transparent (has a mask) */ if (idata[image].istore->transparent) { redraw_set_colour(redraw_backcol(b)); tsize += DSIZE_FRECT; if (!size) { if (!savedraw_rectangle_fill(OTD(r->redraw_area.xmin), OTD(r->redraw_area.ymin), OTD(r->redraw_area.xmax - r->redraw_area.xmin), OTD(r->redraw_area.ymax - r->redraw_area.ymin), redraw_backcol(b))) return 0; } } /* How big is each image going to be? */ protocols_util_make_unique_name(name, sizeof(name)); e = idata[image].istore -> DumpSprite(idata[image].istore, name, -1); if (!e) { e = _swix(OS_File, _INR(0,1) | _OUT(4), 17, name, &image_size); } if (e || image_size <= 12) return 0; /* Subtract the sprite file header size */ image_size -= 12; input = fopen(name, "rb"); block = malloc(image_size); if (block && input && input->__file) { /* Load the image */ e = _swix(OS_Args, _INR(0,2), 1, input->__file, 12); if (e) goto image_tile_to_draw_exit; e = _swix(OS_GBPB, _INR(0,3), 4, input->__file, block, image_size); if (e) goto image_tile_to_draw_exit; fclose(input); input = NULL; /* Render the image over the redraw region */ for (y = ymax - h + 4; y >= r->redraw_area.ymin - h; y -= h) { for (x = xmin; x <= r->redraw_area.xmax; x += w) { if (!size) { draw_spristrhdr hdr; int ok; /* Write the item header */ hdr.tag = draw_OBJSPRITE; hdr.size = image_size + sizeof(hdr); hdr.bbox.xmin = OTD(x); hdr.bbox.ymin = OTD(y); hdr.bbox.xmax = OTD(x + w); hdr.bbox.ymax = OTD(y + h); if (!savedraw_write_bytes((char *) &hdr, sizeof(hdr))) goto image_tile_to_draw_exit; /* Write out the image */ browser_redrawing = b; image_redrawing = image; ok = savedraw_write_bytes(block, image_size); browser_redrawing = NULL; image_redrawing = -1; if (!ok) goto image_tile_to_draw_exit; #ifdef STRICT_PARSER if (e) { e->errnum = Utils_Error_Custom_Message; show_error_ret(e); goto image_tile_to_draw_exit; } #else if (e) goto image_tile_to_draw_exit; #endif } tsize += image_size + sizeof(draw_spristrhdr); } } } /* Successful exit */ if (input) fclose(input); if (block) free(block); remove(name); return tsize; /* Error condition exit */ image_tile_to_draw_exit: if (input) fclose(input); if (block) free(block); remove(name); return 0; }