📄 img.c
字号:
/* 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>#endifstruct 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 */struct g_object_image *global_goi;struct cached_image *global_cimg;int end_callback_hit;#endif /* #ifdef G */int dither_images=1;#ifdef G/* prototypes */int is_image_size_sane(int x, int y);void destroy_decoder (struct cached_image *);void img_destruct_image(struct g_object *);int img_scale_h(unsigned scale, int);int img_scale_v(unsigned scale, int);unsigned short *buffer_to_16(unsigned short *, struct cached_image *, unsigned char *, int);void r3l0ad(struct cached_image *, struct g_object_image *);void type(struct cached_image *, unsigned char *);int img_process_download(struct g_object_image *, struct f_data_c *);int get_foreground(int);void draw_frame_mark (struct graphics_driver *, struct graphics_device *, int, int, int, int, long, long, int);void update_bitmap(struct cached_image *);void img_draw_image (struct f_data_c *, struct g_object_image *, int, int);void find_or_make_cached_image(struct g_object_image *, unsigned char *, int);void buffer_to_bitmap(struct cached_image *);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) */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); }}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. */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;}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 */void 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"); } if (cimg->width<1||cimg->height<1){ fprintf(stderr,"width=%d height=%d\n",cimg->width, cimg->height); internal("Zero dimensions in header_dimensions_known"); }#endif /* #ifdef DEBUG */ 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; drv->get_empty_bitmap(&(cimg->bmp)); 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); 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 */}/* 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 */unsigned short *buffer_to_16(unsigned short *tmp, struct cached_image *cimg ,unsigned char *buffer, int height){ unsigned short red, green,blue;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -