diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-12-07 02:53:31 -0800 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-12-07 02:53:31 -0800 |
commit | cbb6286cb92020dd7ae88798ed831ed76fd2130e (patch) | |
tree | 782a01c00d5e064aa67ea3f9241a8ef1de1060c6 /img.c | |
download | links-cbb6286cb92020dd7ae88798ed831ed76fd2130e.tar.gz links-cbb6286cb92020dd7ae88798ed831ed76fd2130e.tar.bz2 links-cbb6286cb92020dd7ae88798ed831ed76fd2130e.zip |
Imported Upstream version 2.6upstream/2.6upstream
Diffstat (limited to 'img.c')
-rw-r--r-- | img.c | 1366 |
1 files changed, 1366 insertions, 0 deletions
@@ -0,0 +1,1366 @@ +/* img.c + * Generic image decoding and PNG and JPG decoders. + * (c) 2002 Karel 'Clock' Kulhavy + * This is a part of the Links program, released under GPL. + + * Used in graphics mode of Links only + TODO: odstranit zbytecne ditherovani z strip_optimized header_dimensions_known, + protoze pozadi obrazku musi byt stejne jako pozadi stranky, a to se nikdy + neditheruje, protoze je to vzdy jednolita barva. Kdyz uz to nepujde + odstranit tak tam aspon dat fixne zaokrouhlovani. + TODO: pouzit get_filled_bitmap az bude napsany k optimalizaci zadavani + grafickych dat do X serveru z hlediska zaalokovane pameti. + TODO: dodelat stripy do jpegu a png a tiff. + */ + +#include "links.h" + +#ifdef G + +#ifdef HAVE_ENDIAN_H +/* Max von Sydow */ +#include <endian.h> +#endif + +#ifdef HAVE_JPEG +#include <jpeglib.h> +#endif + +struct decoded_image { + int bla; +}; + +#define RESTART_SIZE 8192 +/* Size of biggest chunk of compressed data that is processed in one run */ + + +/* End of decoder structs */ + +static struct g_object_image *global_goi; +struct cached_image *global_cimg; +int end_callback_hit; +#endif /* #ifdef G */ +int dither_images=1; +#ifdef G + +static int is_image_size_sane(int x, int y) +{ + unsigned a = (unsigned)x * (unsigned)y * 6; + if (y && a / (unsigned)y / 6 != (unsigned)x) return 0; + return a < MAXINT; +} + +/* This is a dummy */ +void img_draw_decoded_image(struct graphics_device *dev, struct decoded_image *d, int x, int y, int xw, int yw, int xo, int yo) +{ +} + +/* This is a dummy */ +void img_release_decoded_image(struct decoded_image *d) +{ + mem_free(d); +} + +/* mem_free(cimg->decoder) */ +static void destroy_decoder (struct cached_image *cimg) +{ +#ifdef HAVE_JPEG + struct jpg_decoder *jd; +#endif /* #ifdef HAVE_JPEG */ + struct png_decoder *pd; +#ifdef HAVE_TIFF + struct tiff_decoder *td; +#endif /* #ifdef HAVE_TIFF */ + + if (cimg->decoder){ + switch(cimg->image_type){ + case IM_PNG: + pd=(struct png_decoder *)cimg->decoder; + png_destroy_read_struct( + &pd->png_ptr, + &pd->info_ptr, + NULL); + break; +#ifdef HAVE_JPEG + case IM_JPG: + jd=(struct jpg_decoder *)cimg->decoder; + + jpeg_destroy_decompress(jd->cinfo); + mem_free(jd->cinfo); + mem_free(jd->jerr); + if (jd->jdata) mem_free(jd->jdata); + break; +#endif /* #ifdef HAVE_JPEG */ + case IM_GIF: + gif_destroy_decoder(cimg); + break; + case IM_XBM: + /* do nothing */ + break; +#ifdef HAVE_TIFF + case IM_TIFF: + td=(struct tiff_decoder *)cimg->decoder; + if (td->tiff_open) + { + if (td->tiff_data)mem_free(td->tiff_data); + td->tiff_open=0; + } + break; +#endif + } + mem_free(cimg->decoder); + } +} + +static void img_destruct_image(struct g_object *object) +{ + struct g_object_image *goi=(struct g_object_image *)object; + + if (goi->orig_src)mem_free(goi->orig_src); + if (goi->alt)mem_free(goi->alt); + if (goi->name)mem_free(goi->name); + if (goi->src)mem_free(goi->src); + release_image_map(goi->map); + if (goi->image_list.next)del_from_list(&goi->image_list); + if (goi->xw&&goi->yw){ + /* At least one dimension is zero */ + goi->cimg->refcount--; + } + mem_free(goi); +} + +/* Frees all data allocated by cached_image including cached image itself */ +void img_destruct_cached_image(struct cached_image *cimg) +{ + switch (cimg->state){ + case 0: + case 1: + case 2: + case 3: + case 9: + case 11: + break; + + case 12: + case 14: + if (cimg->gamma_table) mem_free(cimg->gamma_table); + if (cimg->bmp.user){ + drv->unregister_bitmap(&(cimg->bmp)); + } + if (cimg->strip_optimized){ + if (cimg->dregs) mem_free(cimg->dregs); + }else{ + mem_free(cimg->buffer); + } + case 8: + case 10: + destroy_decoder(cimg); + break; + + case 13: + case 15: + drv->unregister_bitmap(&(cimg->bmp)); + break; + +#ifdef DEBUG + default: + fprintf(stderr,"img_destruct_cached_image: state=%d\n",cimg->state); + internal("Invalid state in struct cached_image"); +#endif /* #ifdef DEBUG */ + } + mem_free(cimg->url); + mem_free(cimg); +} + +/* You throw in a vertical dimension of image and it returns + * new dimension according to the aspect ratio and user-set image + * scaling factor. When scaling factor is 100% and screen pixels + * are non-square, the picture will be always in one dimension + * untouched and in the second _ENLARGED_. So that no information + * in the picture will be lost. + * Input may be <0. In this case output=input + * Input may be 0. In this case output=0. + * If input is >0 the output is also >0. + */ +static int img_scale_h(unsigned scale, int in){ + int out; + /* We assume unsigned long holds at least 32 bits */ + unsigned long pre; + + if (in<=0) return in; + pre=((unsigned long)(aspect<65536UL?65536UL:aspect)*scale+128)>>8; + out=((unsigned long)in*pre+12800UL)/25600UL; + if (out<1) out=1; + return out; +} + +static int img_scale_v(unsigned scale, int in){ + int out; + unsigned long divisor; + + if (in<=0) return in; + divisor=(100*(aspect>=65536UL?65536UL:aspect)+128)>>8; + out=((unsigned long)in*(scale*256)+(divisor>>1))/divisor; + if (out<1) out=1; + return out; +} + +/* Returns height (pixels) for prescribed width (pixels). Honours aspect. */ +static int width2height(float width_px, float width_mm, float height_mm) +{ + int height_px; + + if (width_px<=0) return width_px; + height_px=(height_mm*width_px*65536)/(aspect*width_mm); + if (height_px<1) height_px=1; + return height_px; + +} + +/* Returns width (pixels) for prescribed height (pixels). Honours aspect. */ +static int height2width(float height_px, float width_mm, float height_mm) +{ + int width_px; + + if (height_px<=0) return height_px; + width_px=(width_mm*height_px*aspect)/(65536*height_mm); + if (width_px<1) width_px=1; + return width_px; + +} + +/* Compute 8-bit background for filling buffer with cimg->*_gamma + * (performs rounding) */ +void compute_background_8(unsigned char *rgb, struct cached_image *cimg) +{ + unsigned short red, green, blue; + + round_color_sRGB_to_48(&red, &green, &blue + , cimg->background_color); + rgb[0]=apply_gamma_single_16_to_8(red + ,cimg->red_gamma/user_gamma); + rgb[1]=apply_gamma_single_16_to_8(green + ,cimg->green_gamma/user_gamma); + rgb[2]=apply_gamma_single_16_to_8(blue + ,cimg->blue_gamma/user_gamma); +} + +/* updates cimg state when header dimensions are know. Only allowed to be called + * in state 8 and 10. + * Allocates right amount of memory into buffer, formats it (with background or + * zeroes, depens on buffer_bytes_per_pixel). Updates dimensions (xww and yww) + * according to newly known header dimensions. Fills in gamma_stamp, bmp.user + * (NULL because we not bother with generating bitmap here) + * and rows_added. + * Resets strip_optimized if image will be scaled or + * Allocates dregs if on exit strip_optimized is nonzero. + * Allocates and computes gamma_table, otherwise + * sets gamma_table to NULL. Also doesn't make gamma table if image contains less + * than 1024 pixels (it would be probably a waste of time). + * Output state is always 12 (from input state 8) or 14 (from input state 10). + * + * The caller must have set the following elements of cimg: + * width + * height + * buffer_bytes_per_pixel + * red_gamma + * green_gamma + * blue_gamma + * strip_optimized + */ +int header_dimensions_known(struct cached_image *cimg) +{ + unsigned short red, green, blue; + +#ifdef DEBUG + if ((cimg->state^8)&13){ + fprintf(stderr,"cimg->state=%d\n",cimg->state); + internal("Invalid state in header_dimensions_known"); + } +#endif /* #ifdef DEBUG */ + if (cimg->width<1||cimg->height<1){ + /*fprintf(stderr,"width=%d height=%d\n",cimg->width, cimg->height);*/ + return 1; + } + if (!is_image_size_sane(cimg->width, cimg->height)) { + return 1; + } + if (cimg->wanted_xw<0){ + /* Unspecified width */ + if (cimg->wanted_yw<0){ + /* Unspecified neither width nor height */ + cimg->xww=img_scale_h(cimg->scale, cimg->width); + cimg->yww=img_scale_v(cimg->scale, cimg->height); + }else{ + /* Unspecified width specified height */ + cimg->xww=height2width(cimg->yww, + cimg->width, cimg->height); + if (cimg->xww<=0) cimg->xww=1; + + } + }else{ + /* Specified width */ + if (cimg->wanted_yw<0){ + /* Unspecified height, specified width */ + cimg->yww=width2height(cimg->xww, + cimg->width, cimg->height); + if (cimg->yww<=0) cimg->yww=1; + }else if (cimg->wanted_xyw_meaning==MEANING_AUTOSCALE){ + /* Specified height and width and autoscale meant */ + /* Try first to nail the height */ + cimg->yww=cimg->wanted_yw; + cimg->xww=height2width(cimg->yww, + cimg->width, cimg->height); + if (cimg->xww>cimg->wanted_xw) + { + /* Width too much, we nail the width */ + cimg->xww=cimg->wanted_xw; + cimg->yww=width2height(cimg->xww, + cimg->width, cimg->height); + } + + /* Some sanity checks */ + if (cimg->xww<=0) cimg->xww=1; + if (cimg->yww<=0) cimg->yww=1; + } + } + if (!is_image_size_sane(cimg->xww, cimg->yww)) { + cimg->xww = cimg->width; + cimg->yww = cimg->height; + } + if (cimg->width!=cimg->xww||cimg->height!=cimg->yww) cimg->strip_optimized=0; + cimg->gamma_stamp=gamma_stamp; + if (cimg->strip_optimized){ + struct bitmap tmpbmp; + unsigned short *buf_16; + int i; + + tmpbmp.x=cimg->width; + tmpbmp.y=1; + /* No buffer, bitmap is valid from the very beginning */ + cimg->bmp.x=cimg->width; + cimg->bmp.y=cimg->height; + if (drv->get_empty_bitmap(&(cimg->bmp))) { + cimg->dregs = NULL; + goto skip_img; + } + if ((unsigned)cimg->width > MAXINT / sizeof(*buf_16) / 3) overalloc(); + buf_16=mem_alloc(sizeof(*buf_16)*3*cimg->width); + round_color_sRGB_to_48(&red, &green, &blue + , cimg->background_color); + mix_one_color_48(buf_16,cimg->width, red, green, blue); +#ifdef DEBUG + if (cimg->height<=0){ + fprintf(stderr,"cimg->height=%d\n",cimg->height); + internal("Invalid cimg->height in strip_optimized section of\ + header_dimensions_known"); + } +#endif /* #ifdef DEBUG */ + /* The skip is uninitialized here and is read by dither_start + * but is not used in any malicious way so it doesn't matter + */ + tmpbmp.data=cimg->bmp.data; + cimg->dregs=dither_images?dither_start(buf_16,&tmpbmp):NULL; + tmpbmp.data=(unsigned char *)tmpbmp.data+cimg->bmp.skip; + if (cimg->dregs) + for (i=cimg->height-1;i;i--){ + dither_restart(buf_16,&tmpbmp,cimg->dregs); + tmpbmp.data=(unsigned char *)tmpbmp.data+cimg->bmp.skip; + } + else + for (i=cimg->height-1;i;i--){ + (*round_fn)(buf_16,&tmpbmp); + tmpbmp.data=(unsigned char *)tmpbmp.data+cimg->bmp.skip; + } + mem_free(buf_16); + skip_img: + drv->register_bitmap(&(cimg->bmp)); + if(cimg->dregs) memset(cimg->dregs,0,cimg->width*sizeof(*cimg->dregs)*3); + cimg->bmp.user=(void *)&end_callback_hit; /* Nonzero value */ + /* This ensures the dregs are none and because strip + * optimization is unusable in interlaced pictures, + * this saves the zeroing out at the beginning of the + * decoder itself. + */ + }else { + cimg->rows_added=1; + cimg->bmp.user=NULL; + if (cimg->width && (unsigned)cimg->width * (unsigned)cimg->height / (unsigned)cimg->width != (unsigned)cimg->height) overalloc(); + if ((unsigned)cimg->width * (unsigned)cimg->height > (unsigned)MAXINT / cimg->buffer_bytes_per_pixel) overalloc(); + cimg->buffer=mem_alloc(cimg->width*cimg->height + *cimg->buffer_bytes_per_pixel); + if (cimg->buffer_bytes_per_pixel==4 + ||cimg->buffer_bytes_per_pixel==4 + *sizeof(unsigned short)) + { + /* Make the buffer contain full transparency */ + memset(cimg->buffer,0,cimg->width*cimg->height + *cimg->buffer_bytes_per_pixel); + }else{ + /* Fill the buffer with background color */ + if (cimg->buffer_bytes_per_pixel>4){ + /* 16-bit */ + unsigned short red, green, blue; + + round_color_sRGB_to_48(&red, &green, &blue + , cimg->background_color); + + red=apply_gamma_single_16_to_16(red + ,cimg->red_gamma/user_gamma); + green=apply_gamma_single_16_to_16(green + ,cimg->green_gamma/user_gamma); + blue=apply_gamma_single_16_to_16(blue + ,cimg->blue_gamma / user_gamma); + mix_one_color_48((unsigned short *)cimg->buffer + ,cimg->width*cimg->height,red + ,green, blue); + }else{ + unsigned char rgb[3]; + + /* 8-bit */ + compute_background_8(rgb,cimg); + mix_one_color_24(cimg->buffer + ,cimg->width*cimg->height + ,rgb[0],rgb[1],rgb[2]); + } + } + } + if (cimg->buffer_bytes_per_pixel<=4&&cimg->width*cimg->height>=1024){ + make_gamma_table(cimg); + }else if (cimg->buffer_bytes_per_pixel>=6&&cimg->width*cimg->height>=262144){ + make_gamma_table(cimg); + }else cimg->gamma_table=NULL; + cimg->state|=4; /* Update state */ + return 0; +} + +/* Fills "tmp" buffer with the resulting data and does not free the input + * buffer. May be called only in states 12 and 14 of cimg + */ +static unsigned short *buffer_to_16(unsigned short *tmp, struct cached_image *cimg + ,unsigned char *buffer, int height) +{ + unsigned short red, green,blue; + +#ifdef DEBUG + if (cimg->state!=12&&cimg->state!=14){ + fprintf(stderr,"cimg->state=%d\n",cimg->state); + internal("invalid state in buffer_to_16"); + } +#endif /* #ifdef DEBUG */ + switch (cimg->buffer_bytes_per_pixel){ + case 3: + if (cimg->gamma_table){ + apply_gamma_exponent_24_to_48_table(tmp, buffer, + cimg->width*height + ,cimg->gamma_table); + } + else{ + apply_gamma_exponent_24_to_48(tmp,buffer,cimg->width + *height + ,user_gamma/cimg->red_gamma + ,user_gamma/cimg->green_gamma + ,user_gamma/cimg->blue_gamma); + } + break; + + case 3*sizeof(unsigned short): + if (cimg->gamma_table){ + apply_gamma_exponent_48_to_48_table(tmp + ,(unsigned short *)buffer + ,cimg->width*height, cimg->gamma_table); + }else{ + apply_gamma_exponent_48_to_48(tmp,(unsigned short *)buffer + ,cimg->width*height, + user_gamma/cimg->red_gamma, + user_gamma/cimg->green_gamma, + user_gamma/cimg->blue_gamma); + } + break; + + /* Alpha's: */ + case 4: + { + + round_color_sRGB_to_48(&red,&green,&blue,cimg->background_color); + if (cimg->gamma_table){ + apply_gamma_exponent_and_undercolor_32_to_48_table( + tmp, buffer, cimg->width *height, + cimg->gamma_table, red, green, blue); + }else{ + + apply_gamma_exponent_and_undercolor_32_to_48(tmp,buffer + ,cimg->width*height, + user_gamma/cimg->red_gamma, + user_gamma/cimg->green_gamma, + user_gamma/cimg->blue_gamma, + red, green, blue); + } + } + break; + + case 4*sizeof(unsigned short): + { + round_color_sRGB_to_48(&red, &green, &blue, + cimg->background_color); + if (cimg->gamma_table){ + apply_gamma_exponent_and_undercolor_64_to_48_table + (tmp, (unsigned short *)buffer, cimg->width*height + ,cimg->gamma_table, red, green, blue); + }else{ + apply_gamma_exponent_and_undercolor_64_to_48(tmp + ,(unsigned short*)buffer,cimg->width*height, + user_gamma/cimg->red_gamma, + user_gamma/cimg->green_gamma, + user_gamma/cimg->blue_gamma, + red,green,blue); + } + } + break; + +#ifdef DEBUG + default: + internal("buffer_to_16: unknown mem organization"); +#endif /* #ifdef DEBUG */ + + } + return tmp; +} + +/* Returns allocated buffer with the resulting data and does not free the input + * buffer. May be called only in states 12 and 14 of cimg + * use_strip: 1 if the image is already registered and prepare_strip and + * commit_strip is to be used + * 0: if the image is not yet registered and instead one big register_bitmap + * will be used eventually + * dregs must be externally allocated and contain required value or must be + * NULL. + * if !dregs then rounding is performed instead of dithering. + * dregs are not freed. + * bottom dregs are placed back into dregs. + * Before return the bitmap will be in registered state and changes will be + * commited. + * height must be >=1 !!! + */ +void buffer_to_bitmap_incremental(struct cached_image *cimg + ,unsigned char *buffer, int height, int yoff, int *dregs, int use_strip) +{ +#define max_height 16 +/* max_height must be at least 1 */ + unsigned short *tmp; + struct bitmap tmpbmp; + int add1=0, add2; + +#ifdef DEBUG + if (cimg->state!=12&&cimg->state!=14){ + fprintf(stderr,"cimg->state=%d\n",cimg->state); + internal("Invalid state in buffer_to_bitmap_incremental\n"); + } + if (height<1){ + fprintf(stderr,"height=%d\n",height); + internal("Invalid height in buffer_to_bitmap_incremental\n"); + } + if (cimg->width<1||cimg->height<1){ + fprintf(stderr,"cimg->width=%d, cimg->height=%d\n",cimg->width, + cimg->height); + internal("Invalid cimg->width x cimg->height in\ +buffer_to_bitmap_incremental"); + } +#endif /* #ifdef DEBUG */ + if ((unsigned)cimg->width > MAXINT / max_height / 3 / sizeof(*tmp)) overalloc(); + tmp=mem_alloc(cimg->width*(height<max_height?height:max_height)*3*sizeof(*tmp)); + /* Prepare a fake bitmap for dithering */ + tmpbmp.x=cimg->width; + if (!use_strip){ + tmpbmp.data=(unsigned char *)cimg->bmp.data+cimg->bmp.skip*yoff; + add1=cimg->bmp.skip*max_height; + } + add2=cimg->buffer_bytes_per_pixel*cimg->width*max_height; +not_enough: + tmpbmp.y=height<max_height?height:max_height; + if (use_strip) { + tmpbmp.data=drv->prepare_strip(&(cimg->bmp),yoff,tmpbmp.y); + if (!tmpbmp.data) goto prepare_failed; + } + tmpbmp.skip=cimg->bmp.skip; + buffer_to_16(tmp, cimg, buffer, tmpbmp.y); + if (dregs){ + dither_restart(tmp, &tmpbmp, dregs); + } + else { + + (*round_fn)(tmp, &tmpbmp); + } + if (use_strip) { + prepare_failed: + drv->commit_strip(&(cimg->bmp),yoff,tmpbmp.y); + } + height-=tmpbmp.y; + if (!height) goto end; + buffer+=add2; + yoff+=tmpbmp.y; + tmpbmp.data=(unsigned char *)tmpbmp.data+add1; + /* This has no effect if use_strip but it's faster + * to add to bogus value than to play with + * conditional jumps. + */ + goto not_enough; +end: + mem_free(tmp); + if (!use_strip) drv->register_bitmap(&(cimg->bmp)); +} + +/* Takes the buffer and resamples the data into the bitmap. Automatically + * destroys the previous bitmap. Must be called only when cimg->buffer is valid. + * Sets bmp->user to non-NULL + * If gamma_table is used, it must be still allocated here (take care if you + * want to destroy gamma table and call buffer_to_bitmap, first call buffer_to_bitmap + * and then destroy gamma_table). + */ +static void buffer_to_bitmap(struct cached_image *cimg) +{ + unsigned short *tmp, *tmp1; + int ix, iy, ox, oy, gonna_be_smart; + int *dregs; + +#ifdef DEBUG + if(cimg->state!=12&&cimg->state!=14){ + fprintf(stderr,"cimg->state=%d\n",cimg->state); + internal("buffer_to_bitmap called in invalid state"); + } + if (cimg->strip_optimized) internal("strip_optimized in buffer_to_bitmap"); + if (cimg->width<1||cimg->height<1){ + fprintf(stderr,"cimg->width=%d, cimg->height=%d\n",cimg->width, + cimg->height); + internal("Invalid cimg->width x cimg->height in\ +buffer_to_bitmap"); + } +#endif /* #ifdef DEBUG */ + + + if (!cimg->rows_added) return; + + /* Here of course width and height must be already filled */ + cimg->rows_added=0; + ix=cimg->width; + iy=cimg->height; + ox=cimg->xww; + oy=cimg->yww; + if (ix==ox&&iy==oy) gonna_be_smart=1; + else{ + gonna_be_smart=0; + if (ix && (unsigned)ix * (unsigned)iy / (unsigned)ix != (unsigned)iy) overalloc(); + if ((unsigned)ix * (unsigned)iy > MAXINT / sizeof(*tmp) / 3) overalloc(); + tmp=mem_alloc(ix*iy*3*sizeof(*tmp)); + buffer_to_16(tmp,cimg,cimg->buffer,iy); + if (!cimg->decoder){ + mem_free(cimg->buffer); + cimg->buffer=NULL; + } + + /* Scale the image to said size */ +#ifdef DEBUG + if (ox<=0||oy<=0){ + internal("ox or oy <=0 before resampling image"); + } +#endif /* #ifdef DEBUG */ + if (ix!=ox||iy!=oy){ + /* We must really scale */ + tmp1=tmp; + scale_color(tmp1,ix,iy,&tmp,ox,oy); + } + } + if (cimg->bmp.user) drv->unregister_bitmap(&cimg->bmp); + cimg->bmp.x=ox; + cimg->bmp.y=oy; + if (drv->get_empty_bitmap(&(cimg->bmp))) { + if (!gonna_be_smart) { + mem_free(tmp); + } + goto bitmap_failed; + } + if (gonna_be_smart){ + if (dither_images) { + if ((unsigned)cimg->width > MAXINT / 3 / sizeof(*dregs)) overalloc(); + dregs = mem_calloc(sizeof(*dregs)*3*cimg->width); + } else { + dregs = NULL; + } + buffer_to_bitmap_incremental(cimg, cimg->buffer, cimg->height, + 0, dregs, 0); + if (dregs) mem_free(dregs); + }else{ + if (dither_images) + dither(tmp,&(cimg->bmp)); + else + (*round_fn)(tmp,&(cimg->bmp)); + mem_free(tmp); + bitmap_failed: + drv->register_bitmap(&(cimg->bmp)); + } + cimg->bmp.user=(void *)&end_callback_hit; + /* Indicate that the bitmap is valid. The value is just any + nonzero value */ + cimg->rows_added=0; + /* Indicate the bitmap is up-to-date */ +} + +/* Performs state transition for end of stream or error in image or + * end of image */ +void img_end(struct cached_image *cimg) +{ + switch(cimg->state){ + case 12: + case 14: + if (cimg->strip_optimized){ + if (cimg->dregs) mem_free(cimg->dregs); + } + else{ + buffer_to_bitmap(cimg); + mem_free(cimg->buffer); + } + if (cimg->gamma_table) mem_free(cimg->gamma_table); + case 8: + case 10: + destroy_decoder(cimg); + case 0: + case 1: + case 2: + case 3: + case 9: + case 11: + case 13: + case 15: + break; +#ifdef DEBUG + default: + fprintf(stderr,"state=%d\n",cimg->state); + internal("Invalid state encountered in end"); +#endif /* #ifdef DEBUG */ + } + cimg->state|=1; +} + +static void r3l0ad(struct cached_image *cimg, struct g_object_image *goi) +{ + cimg->eof_hit=0; + cimg->last_count=goi->af->rq->ce->count; + cimg->last_count2=goi->af->rq->ce->count2; + cimg->gamma_stamp=gamma_stamp; + switch(cimg->state){ + case 8: + case 10: + destroy_decoder(cimg); + case 1: + case 3: + case 9: + case 11: + case 0: + case 2: + break; + + case 12: + if (cimg->gamma_table) mem_free(cimg->gamma_table); + destroy_decoder(cimg); + if (cimg->strip_optimized){ + if (cimg->dregs) mem_free(cimg->dregs); + }else{ + mem_free(cimg->buffer); + } + if (cimg->bmp.user){ + case 13: + drv->unregister_bitmap(&cimg->bmp); + } + cimg->xww=img_scale_h(cimg->scale, cimg->wanted_xw<0?32:cimg->wanted_xw); + cimg->yww=img_scale_v(cimg->scale, cimg->wanted_yw<0?32:cimg->wanted_yw); + break; + + case 14: + if (cimg->gamma_table) mem_free(cimg->gamma_table); + destroy_decoder(cimg); + if (cimg->strip_optimized){ + if (cimg->dregs) mem_free(cimg->dregs); + }else{ + mem_free(cimg->buffer); + } + if (cimg->bmp.user){ + case 15: + drv->unregister_bitmap(&cimg->bmp); + } + break; + +#ifdef DEBUG + default: + fprintf(stderr,"cimg->state=%d\n",cimg->state); + internal("Invalid state in r3l0ad()"); +#endif /* #ifdef DEBUG */ + } + cimg->state&=2; +} + +/* Returns 1 if match. If returns 1 then test is mem_free'd. + * If doesn't return 1 then returns 0 + * dtest - Destructive TEST + */ +static inline int dtest(unsigned char *template, unsigned char *test) +{ + if (strcasecmp(template,test)) return 0; + else{ + mem_free(test); + return 1; + } +} + +/* content_type will be mem_free'd before return from this function. + * This may be called only in state 0 or 2 */ +static void type(struct cached_image *cimg, unsigned char *content_type) +{ +#ifdef DEBUG + if (cimg->state!=0&&cimg->state!=2){ + fprintf(stderr,"cimg->state=%d\n",cimg->state); + internal("Invalid state encountered in type()"); + } +#endif /* #ifdef DEBUG */ +#ifdef HAVE_JPEG + if (dtest("image/jpeg",content_type)){ + cimg->image_type=IM_JPG; + jpeg_start(cimg); + }else if (dtest("image/jpg",content_type)){ + cimg->image_type=IM_JPG; + jpeg_start(cimg); + }else if (dtest("image/jpe",content_type)){ + cimg->image_type=IM_JPG; + jpeg_start(cimg); + }else if (dtest("image/pjpe",content_type)){ + cimg->image_type=IM_JPG; + jpeg_start(cimg); + }else if (dtest("image/pjpeg",content_type)){ + cimg->image_type=IM_JPG; + jpeg_start(cimg); + }else if (dtest("image/pjpg",content_type)){ + cimg->image_type=IM_JPG; + jpeg_start(cimg); + }else +#endif /* #ifdef HAVE_JPEG */ + if (dtest("image/png",content_type)){ + cimg->image_type=IM_PNG; + png_start(cimg); + }else if (dtest("image/x-png",content_type)){ + cimg->image_type=IM_PNG; + png_start(cimg); + }else if (dtest("image/gif",content_type)){ + cimg->image_type=IM_GIF; + gif_start(cimg); + }else if (dtest("image/x-xbitmap",content_type)){ + cimg->image_type=IM_XBM; + xbm_start(cimg); +#ifdef HAVE_TIFF + }else if (dtest("image/tiff",content_type)){ + cimg->image_type=IM_TIFF; + tiff_start(cimg); + }else if (dtest("image/tif",content_type)){ + cimg->image_type=IM_TIFF; + tiff_start(cimg); +#endif /* #ifdef HAVE_TIFF */ + }else{ + /* Error */ + mem_free(content_type); + img_end(cimg); + return; + } + cimg->state|=8; /* Advance the state according to the table in + links-doc.html */ + cimg->last_length=0; +} + +/* Doesn't print anything. Downloads more data if available. + * Sets up cimg->reparse and cimg->xww and cimg->yww accordingly to + * the state of the decoder. When changing xww and yww also changes xw and yw + * in g_object_image. + * return value 1 means the data were chopped and the caller shall not redraw + * (because it would be too slow and because we are probably choked + * up with the data) + */ +static int img_process_download(struct g_object_image *goi, struct f_data_c *fdatac) +{ + unsigned char *data, *dataend, *ctype; + int length; + struct cached_image *cimg = goi->cimg; + int chopped=0; + +#ifdef DEBUG + if (!goi->af) internal("NULL goi->af in process_download\n"); + if (cimg->state>=16){ /* Negative don't occur becaus it's unsigned char */ + fprintf(stderr,"cimg->state=%d\n",cimg->state); + internal("Invalid cimg->state in img_process_download\n"); + } +#endif /* #ifdef DEBUG */ + if (!goi->af->rq) return 0; + if (!goi->af->rq->ce) goto end; + /*fprintf(stderr, "processing: %s\n", goi->af->rq->ce->url);*/ + if (goi->af->rq->ce->count2!=cimg->last_count2|| + (goi->af->rq->ce->count!=cimg->last_count && cimg->eof_hit) || + (cimg->state>=12&&gamma_stamp!=cimg->gamma_stamp)){ + /* Reload */ + r3l0ad(cimg,goi); + } + /*if (!goi->af->rq->ce->head) goto end;*/ /* Mikulas: head muze byt NULL*/ /* Mikulas: tak se to zpracuje a nebude se skakat na konec, kdyz je to NULL */ + + if (cimg->state==0||cimg->state==2){ + /* Type still unknown */ + ctype=get_content_type(goi->af->rq->ce->head, + goi->af->rq->url); + if (!ctype) ctype = stracpy("application/octet-stream"); + type(cimg,ctype); + } + + /* Now, if we are in state where decoder is running (8, 10, 12, 14), we may feed + * some data into it. + */ + + if (!((cimg->state^8)&9)){ + if (get_file(goi->af->rq, &data, &dataend)) goto end; + length = dataend - data; + if (length<=cimg->last_length) goto end; /* No new data */ + + data+=cimg->last_length; + length-=cimg->last_length; + if (length>RESTART_SIZE){ + length=RESTART_SIZE; + chopped=1; + if (fdatac) { + refresh_image(fdatac,(struct g_object *)goi,1); + } + } + /* Decoder has been already started */ + switch(cimg->image_type){ + case IM_PNG: + png_restart(cimg,data,length); + break; +#ifdef HAVE_JPEG + case IM_JPG: + jpeg_restart(cimg,data,length); + break; +#endif /* #ifdef HAVE_JPEG */ + case IM_XBM: + xbm_restart(cimg,data,length); + break; + case IM_GIF: + gif_restart(data,length); + break; +#ifdef HAVE_TIFF + case IM_TIFF: + tiff_restart(cimg,data,length); + break; +#endif /* #ifdef HAVE_TIFF */ +#ifdef DEBUG + default: + fprintf(stderr,"cimg->image_type=%d\n",cimg->state); + internal("Invalid image_type encountered when processing data in\ +img_process_download.\n"); +#endif /* #ifdef DEBUG */ + } + cimg->last_length+=length; + } + end: + + /* Test end */ + if (!is_entry_used(goi->af->rq->ce) && (goi->af->rq->state < 0 + ||(goi->af->rq->ce&&goi->af->rq->stat.state<0))){ + /* We must not perform end with chopped because some + * unprocessed data still wait for us :) + */ + if (!chopped){ +#ifdef HAVE_TIFF + if (!((cimg->state^8)&9)&&cimg->image_type==IM_TIFF) + tiff_finish(cimg); +#endif + cimg->eof_hit=1; + if (goi->af->rq->ce) + cimg->last_count=goi->af->rq->ce->count; + img_end(cimg); + } + } else if (!chopped) { + if (fdatac && f_is_finished(fdatac->f_data)) { + refresh_image(fdatac,(struct g_object *)goi,2000); + } + } + return chopped; +} + +/* Input: rgb (sRGB) triplet (0...255) + * Returns a color that is very contrasty on that background sRGB color + */ +int get_foreground(int rgb) +{ + int r,g,b; + + r=(rgb>>16)&255; + g=(rgb>>8)&255; + b=rgb&255; + + r=r<128?255:0; + g=g<128?255:0; + b=b<128?255:0; + + return (r<<16)|(g<<8)|b; +} + +static void draw_frame_mark (struct graphics_driver *drv, struct + graphics_device *dev, int x, int y, int xw, int yw + , long bg, long fg, int broken) +{ +#ifdef DEBUG + if (xw<1||yw<1) internal("zero dimension in draw_frame_mark"); +#endif /* #ifdef DEBUG */ + if (broken == 1){ + /* Draw between ( 0 and 1/4 ) and ( 3/4 and 1 ) of each + * side (0-1) + */ + int xl, xh, yl, yh; + + xh=xw-(xl=xw>>2); + yh=yw-(yl=yw>>2); + /* Draw full sides and the box inside */ + drv->draw_hline(dev,x,y,x+xl,fg); + drv->draw_hline(dev,x+xl,y,x+xh,bg); + drv->draw_hline(dev,x+xh,y,x+xw,fg); + if (yw>=1){ + if (yw>=2){ + drv->draw_vline(dev,x,y+1,y+yl,fg); + drv->draw_vline(dev,x,y+yl,y+yh,bg); + drv->draw_vline(dev,x,y+yh,y+yw-1,fg); + if (xw>=1){ + if (xw>=2){ + drv->fill_area(dev, + x+1,y+1,x+xw-1,y+yw-1, + bg); + } + drv->draw_vline(dev,x+xw-1,y+1,y+yl,fg); + drv->draw_vline(dev,x+xw-1,y+yl,y+yh,bg); + drv->draw_vline(dev,x+xw-1,y+yh,y+yw-1,fg); + } + } + drv->draw_hline(dev,x,y+yw-1,x+xl,fg); + drv->draw_hline(dev,x+xl,y+yw-1,x+xh,bg); + drv->draw_hline(dev,x+xh,y+yw-1,x+xw,fg); + } + }else { + /* Draw full sides and the box inside */ + drv->draw_hline(dev,x,y,x+xw,fg); + if (yw>=1){ + if (yw>=2){ + drv->draw_vline(dev,x,y+1,y+yw-1,fg); + if (xw>=1){ + if (xw>=2){ + if (broken < 2) drv->fill_area(dev, + x+1,y+1,x+xw-1,y+yw-1, + bg); + } + drv->draw_vline(dev,x+xw-1,y+1, + y+yw-1,fg); + } + } + drv->draw_hline(dev,x,y+yw-1,x+xw,fg); + } + if (broken == 2 && xw > 2 && yw > 2) { + draw_frame_mark(drv, dev, x + 1, y + 1, xw - 2, yw - 2, bg, fg, 3); + } + } +} + +/* Entry is allowed only in states 12, 13, 14, 15 + * Draws the picture from bitmap. + * Before doing so, ensures that bitmap is present and if not, converts it from + * the buffer. + */ +static void draw_picture(struct f_data_c *fdatac, struct g_object_image *goi, + int x, int y, int bg) +{ + struct graphics_device *dev=fdatac->ses->term->dev; + struct cached_image *cimg=goi->cimg; + struct rect saved; + +#ifdef DEBUG + if (goi->cimg->state<12||goi->cimg->state>=16){ + fprintf(stderr,"cimg->state=%d\n",cimg->state); + internal("Invalid cimg->state in draw_picture"); + } +#endif /* #ifdef DEBUG */ + if (!(cimg->state&1)){ + if (!cimg->bmp.user) + buffer_to_bitmap(cimg); + } +#ifdef DEBUG + else if (!cimg->bmp.user){ + fprintf(stderr,"cimg->state=%d\n",cimg->state); + internal("Nonexistent bitmap in said cimg->state in draw_picture"); + } +#endif /* #ifdef DEBUG */ + restrict_clip_area(dev, &saved, x, y, x+goi->xw, y+goi->yw); + drv->draw_bitmap(dev,&cimg->bmp,x,y); + drv->fill_area(dev, x+cimg->bmp.x, y, x+goi->xw,y+cimg->bmp.y, bg); + drv->fill_area(dev, x,y+cimg->bmp.y,x+goi->xw, y+goi->yw,bg); + drv->set_clip_area(dev,&saved); +} + +/* Ensures in buffer there is not newer picture than in bitmap. Allowed to be + * called only in state 12, 13, 14, 15. + */ +static void update_bitmap(struct cached_image *cimg) +{ +#ifdef DEBUG + if (cimg->state<12||cimg->state>=16){ + fprintf(stderr,"cimg->state=%d\n",cimg->state); + internal("Invalid state in update_bitmap"); + } +#endif /* #ifdef DEBUG */ + if (!(cimg->state&1)&& + !cimg->strip_optimized + &&cimg->rows_added) buffer_to_bitmap(cimg); +} + +/* Draws the image at x,y. Is called from other C sources. */ +static void img_draw_image (struct f_data_c *fdatac, struct g_object_image *goi, + int x, int y) +{ + long color_bg, color_fg; + struct cached_image *cimg = goi->cimg; + struct rect r; + /* refresh_image(fdatac, goi, 1000); To sem asi napsal mikulas jako + * navod, jak se vola to refresh_image. Nicmene ja jsem milostive + * usoudil, ze zadnejch 1000, ale 0. + */ + + if (cimg) { + color_bg=dip_get_color_sRGB(cimg->background_color); + color_fg=dip_get_color_sRGB(get_foreground(cimg->background_color)); + } else { + color_bg = dip_get_color_sRGB(0x00c0c0c0); + color_fg = dip_get_color_sRGB(0x00000000); + } + + if (!(goi->xw&&goi->yw)) return; /* At least one dimension is zero */ + + + memcpy(&r, &fdatac->ses->term->dev->clip, sizeof(struct rect)); + if (fdatac->vs->g_display_link && fdatac->active && fdatac->vs->current_link != -1 && fdatac->vs->current_link == goi->link_num) { + draw_frame_mark(drv, fdatac->ses->term->dev,x,y,goi->xw, + goi->yw,color_bg,color_fg,2); + restrict_clip_area(fdatac->ses->term->dev, &r, x + 2, y + 2, x + goi->xw - 2, y + goi->yw - 2); + } + + global_goi=goi; + global_cimg=goi->cimg; + if (img_process_download(goi, fdatac)) goto ret; /* Choked with data, will not + * draw. */ + /* Now we will only draw... */ + if (cimg->state<12){ + draw_frame_mark(drv, fdatac->ses->term->dev,x,y,goi->xw, + goi->yw,color_bg,color_fg,cimg->state&1); + }else +#ifdef DEBUG + if (cimg->state<16){ +#else + { +#endif /* #ifdef DEBUG */ + update_bitmap(cimg); + draw_picture(fdatac,goi,x,y,color_bg); + } +#ifdef DEBUG + else{ + fprintf(stderr,"cimg->state=%d\n",cimg->state); + internal("Invalid state in img_draw_image"); + } +#endif /* #ifdef DEBUG */ + ret:; + drv->set_clip_area(fdatac->ses->term->dev, &r); +#ifdef LINKS_TESTMODE_IMAGE_AUTO_EXIT + if (cimg->state & 1) + terminate_loop = 1; +#endif +} + +/* Prior to calling this function you have to fill out + * image -> xw (<0 means not known) + * image -> yw (<0 means not known) + * image -> xyw meaning (MEANING_AUTOSCALE or MEANING_DIMS) + * image -> background + * + * The URL will not be freed. + */ +static void find_or_make_cached_image(struct g_object_image *image, unsigned char *url, + int scale) +{ + struct cached_image *cimg; + + if (!(cimg = find_cached_image(image->background, url, image->xw, + image->yw, image->xyw_meaning,scale, aspect))){ + /* We have to make a new image cache entry */ + cimg = mem_alloc(sizeof(*cimg)); + cimg->refcount = 1; + cimg->background_color=image->background; +#ifdef DEBUG + if (!url) + internal ("NULL url as argument of\ +find_or_make_cached_image"); +#endif /* #ifdef DEBUG */ + cimg->scale = scale; + cimg->aspect = aspect; + cimg->url = stracpy(url); + cimg->wanted_xw = image->xw; + cimg->wanted_yw = image->yw; + cimg->wanted_xyw_meaning=image->xyw_meaning; + cimg->xww = image->xw<0?img_scale_h(cimg->scale, 32):cimg->wanted_xw; + cimg->yww = image->yw<0?img_scale_v(cimg->scale, 32):cimg->wanted_yw; + cimg->state=0; + /* width, height, image_type, buffer, buffer_bytes_per_pixel, red_gamma, + * green_gamma, blue_gamma, gamma_stamp, bitmap, last_length, rows_added, + * and decoder is invalid in both state 0 and state 2. Thus is need no to + * be filled in. + */ + + /* last_count2 is unitialized */ + cimg->eof_hit=0; + cimg->last_count=-1; + cimg->last_count2=-1; + if (cimg->wanted_xw>=0&&cimg->wanted_yw>=0) cimg->state|=2; + add_image_to_cache(cimg); + } + global_cimg=image->cimg=cimg; +} + +/* The original (unscaled, in pixels pace) size is requested in im->xsize and im->ysize. + * <0 means unknown. Autoscale is requested in autoscale. When autoscale is on, + * the requested dimensions are not scaled and they mean maximum allowed + * dimensions. */ +struct g_object_image *insert_image(struct g_part *p, struct image_description *im) +{ + struct g_object_image *image; + struct cached_image *cimg; + int retval; + + image=mem_calloc(sizeof(struct g_object_image)); + global_goi=image; + image->mouse_event=&g_text_mouse; + image->draw=&img_draw_image; + image->destruct=&img_destruct_image; + image->get_list=NULL; + image->link_num = im->link_num; + image->link_order = im->link_order; + image->map = NULL; + /* + image->x is already filled + image->y is already filled + */ + if (im->align == AL_MIDDLE) image->y = G_OBJ_ALIGN_MIDDLE; + if (im->align == AL_TOP) image->y = G_OBJ_ALIGN_TOP; + + if (im->autoscale_x&&im->autoscale_y) + { + /* Autoscale requested */ + image->xw=im->autoscale_x; + image->yw=im->autoscale_y; + image->xyw_meaning=MEANING_AUTOSCALE; + }else{ + /* Autoscale not requested */ + image->xw=img_scale_h(d_opt->image_scale, im->xsize); + image->yw=img_scale_v(d_opt->image_scale, im->ysize); + image->xyw_meaning=MEANING_DIMS; + } + if (image->xw >= 0 && image->yw >= 0) { + if (!is_image_size_sane(image->xw, image->yw)) { + mem_free(image); + return NULL; + } + } + + /* Put the data for javascript inside */ + image->id=(current_f_data->n_images)++; + image->name=stracpy(im->name); + image->alt=stracpy(im->alt); + image->orig_src=stracpy(im->src); + image->border=im->border; + image->vspace=im->vspace; + image->hspace=im->hspace; + image->src=stracpy(im->url); + + if (!(image->xw&&image->yw)){ + /* At least one is zero */ + if (image->xw<0) image->xw=0; + if (image->yw<0) image->yw=0; + if (im->insert_flag)add_to_list(current_f_data->images,&image->image_list); + else image->image_list.prev=NULL,image->image_list.next=NULL; + return image; + } + /* + image->parent is already filled + */ + image->af=request_additional_file(current_f_data,im->url); + if (image->xw < 0 || image->yw < 0) image->af->unknown_image_size = 1; + image->background=p->root->bg->u.sRGB; + + /* This supplies the result into image->cimg and global_cimg */ + find_or_make_cached_image(image, im->url, d_opt->image_scale); + cimg=global_cimg; + +next_chunk: + retval=img_process_download(image,NULL); + if (retval&&!(cimg->state&4)) goto next_chunk; + image->xw=image->cimg->xww; + image->yw=image->cimg->yww; + if (cimg->state==0||cimg->state==8||(!image->af->rq->ce && image->af->unknown_image_size)) if (image->af->need_reparse != -1) image->af->need_reparse = 1; + if (im->insert_flag)add_to_list(current_f_data->images,&image->image_list); + else image->image_list.prev=NULL,image->image_list.next=NULL; + return image; +} + +#ifdef JS + +void change_image (struct g_object_image *goi, unsigned char *url, unsigned char *src, struct f_data + *fdata) +{ + /*struct cached_image *cimg;*/ + + global_goi=goi; + mem_free(goi->src); + goi->src=stracpy(url); + if (goi->orig_src)mem_free(goi->orig_src); + goi->orig_src=stracpy(src); + if (!(goi->xw&&goi->yw)) return; + goi->cimg->refcount--; + goi->af=request_additional_file(fdata,url); + goi->af->need_reparse = -1; + + find_or_make_cached_image(goi, url, fdata->opt.image_scale); + /* Automatically sets up global_cimg */ + + refresh_image(fdata->fd,(struct g_object*)goi,1); +} + +#endif + +#endif + +int known_image_type(unsigned char *type) +{ +#ifdef G + if (!strcasecmp(type, "image/png")) return 1; + if (!strcasecmp(type, "image/x-png")) return 1; + if (!strcasecmp(type, "image/gif")) return 1; + if (!strcasecmp(type, "image/x-xbitmap")) return 1; +#ifdef HAVE_JPEG + if (!strcasecmp(type, "image/jpeg")) return 1; + if (!strcasecmp(type, "image/jpg")) return 1; + if (!strcasecmp(type, "image/jpe")) return 1; + if (!strcasecmp(type, "image/pjpe")) return 1; + if (!strcasecmp(type, "image/pjpeg")) return 1; + if (!strcasecmp(type, "image/pjpg")) return 1; +#endif +#ifdef HAVE_TIFF + if (!strcasecmp(type, "image/tiff")) return 1; + if (!strcasecmp(type, "image/tif")) return 1; +#endif +#endif + return 0; +} |