/**************************************************************************** * NCSA Mosaic for the X Window System * * Software Development Group * * National Center for Supercomputing Applications * * University of Illinois at Urbana-Champaign * * 605 E. Springfield, Champaign IL 61820 * * mosaic@ncsa.uiuc.edu * * * * Copyright (C) 1993, Board of Trustees of the University of Illinois * * * * NCSA Mosaic software, both binary and source (hereafter, Software) is * * copyrighted by The Board of Trustees of the University of Illinois * * (UI), and ownership remains with the UI. * * * * The UI grants you (hereafter, Licensee) a license to use the Software * * for academic, research and internal business purposes only, without a * * fee. Licensee may distribute the binary and source code (if released) * * to third parties provided that the copyright notice and this statement * * appears on all copies and that no charge is associated with such * * copies. * * * * Licensee may make derivative works. However, if Licensee distributes * * any derivative work based on or derived from the Software, then * * Licensee will (1) notify NCSA regarding its distribution of the * * derivative work, and (2) clearly notify users that such derivative * * work is a modified version and not the original NCSA Mosaic * * distributed by the UI. * * * * Any Licensee wishing to make commercial use of the Software should * * contact the UI, c/o NCSA, to negotiate an appropriate license for such * * commercial use. Commercial use includes (1) integration of all or * * part of the source code into a product for sale or license by or on * * behalf of Licensee to third parties, or (2) distribution of the binary * * code or source code to third parties that need it to utilize a * * commercial product sold or licensed by or on behalf of Licensee. * * * * UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR * * ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED * * WARRANTY. THE UI SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE * * USERS OF THIS SOFTWARE. * * * * By using or copying this Software, Licensee agrees to abide by the * * copyright law and all other applicable laws of the U.S. including, but * * not limited to, export control laws, and the terms of this license. * * UI shall have the right to terminate this license immediately by * * written notice upon Licensee's breach of, or non-compliance with, any * * of its terms. Licensee may be held legally responsible for any * * copyright infringement that is caused or encouraged by Licensee's * * failure to abide by the terms of this license. * * * * Comments and questions are welcome and can be sent to * * mosaic-x@ncsa.uiuc.edu. * ****************************************************************************/ #include "../config.h" #include "../libhtmlw/HTML.h" #include "mosaic.h" #include "gui.h" #include "img.h" #include "mo-www.h" #include "globalhist.h" #include "picread.h" #include "cci.h" extern int cci_event; #ifndef DISABLE_TRACE extern int srcTrace; #endif #include "bitmaps/gopher_image.xbm" #include "bitmaps/gopher_movie.xbm" #include "bitmaps/gopher_menu.xbm" #include "bitmaps/gopher_text.xbm" #include "bitmaps/gopher_sound.xbm" #include "bitmaps/gopher_index.xbm" #include "bitmaps/gopher_telnet.xbm" #include "bitmaps/gopher_binary.xbm" #include "bitmaps/gopher_unknown.xbm" ImageInfo *scaleImage(ImageInfo *img_data,char *width,char *height); static ImageInfo *gopher_image = NULL; static ImageInfo *gopher_movie = NULL; static ImageInfo *gopher_menu = NULL; static ImageInfo *gopher_text = NULL; static ImageInfo *gopher_sound = NULL; static ImageInfo *gopher_index = NULL; static ImageInfo *gopher_telnet = NULL; static ImageInfo *gopher_binary = NULL; static ImageInfo *gopher_unknown = NULL; /* for selective image loading */ #define blank_width 8 #define blank_height 8 static unsigned char blank_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static ImageInfo *blank = NULL; extern char **imagekill_sites; /*******************************/ /* Defined in gui.c */ extern char *cached_url; extern mo_window *current_win; /* Defined in gui-documents.c */ extern int interrupted; extern int loading_inlined_images; extern int installed_colormap; extern Colormap installed_cmap; /* (dsp, DefaultRootWindow (dsp), \ */ #if defined(__STDC__) || defined(__sgi) #define MAKE_IMGINFO_FROM_BITMAP(name) \ if (!name) \ { \ name = (ImageInfo *)malloc (sizeof (ImageInfo)); \ name->ismap = 0; \ name->width = name##_width; \ name->height = name##_height; \ name->image_data = NULL; \ /* Bandaid for bug in Eric's code. */ \ name->internal = 1; \ name->transparent = 0; \ name->image = \ XCreatePixmapFromBitmapData \ (XtDisplay(swin), XtWindow(view), \ name##_bits, \ name##_width, name##_height, \ fg_pixel, bg_pixel, DefaultDepth(dsp, DefaultScreen(dsp))); \ } \ if (!name->image) \ { \ name->image = \ XCreatePixmapFromBitmapData \ (XtDisplay(swin), XtWindow(view), \ name##_bits, \ name##_width, name##_height, \ fg_pixel, bg_pixel, DefaultDepth(dsp, DefaultScreen(dsp))); \ } #else #define MAKE_IMGINFO_FROM_BITMAP(name) \ if (!name) \ { \ name = (ImageInfo *)malloc (sizeof (ImageInfo)); \ name->ismap = 0; \ name->width = name/**/_width; \ name->height = name/**/_height; \ name->image_data = NULL; \ /* Bandaid for bug in Eric's code. */ \ name->internal = 1; \ name->transparent = 0; \ name->image = \ XCreatePixmapFromBitmapData \ (XtDisplay(swin), XtWindow(view), \ name/**/_bits, \ name/**/_width, name/**/_height, \ fg_pixel, bg_pixel, DefaultDepth(dsp, DefaultScreen(dsp))); \ } \ if (!name->image) \ { \ name->image = \ XCreatePixmapFromBitmapData \ (XtDisplay(swin), XtWindow(view), \ name/**/_bits, \ name/**/_width, name/**/_height, \ fg_pixel, bg_pixel, DefaultDepth(dsp, DefaultScreen(dsp))); \ } #endif /* __STDC__ */ #define RETURN_IMGINFO_FROM_BITMAP(x) \ { \ MAKE_IMGINFO_FROM_BITMAP(x); \ if (cci_event) MoCCISendEventOutput(IMAGE_LOADED); \ return x; \ } /* ------------------------------------------------------------------------ */ ImageInfo *scaleImage(ImageInfo *img_data,char *width,char *height) { /* int widthInt,heightInt; if (!width) { widthInt=img_data->width; } else { if (strchr(width,'%')) { widthInt=(atoi(width)/100)*img_data->width; } else { widthInt=atoi(width); } } if (!height) { heightInt=img_data->height; } else { heightInt=atoi(height); } for (i=0; i<256; i++) { convertRed[i]=(byte)img_data->reds[i]; convertGreen[i]=(byte)img_data->greens[i]; convertBlue[i]=(byte)img_data->blues[i]; } img_data->image_data=SmoothResize(img_data->image_data,0, img_data->width,img_data->height, widthInt,heightInt, convertRed,convertGreen,convertBlue, newRed,newGreen,newBlue,256); */ return NULL; } /* ------------------------------------------------------------------------ */ unsigned char nums[]={ 1, 2, 4, 8, 16, 32, 64, 128 }; /* Cache Load Hack: (BJS) char *ir_hack_string, *diskfile, *url; sprintf(ir_hack_string,"%s\0%s",diskfile,url); ImageResolve(NULL,ir_hack_string,0,NULL,NULL); */ /* Width and Height Hack: (SWP) Added "width" and "height" to the parameter list. These are used to resize the image when we are done decoding. This'll be fun when we start processing on a line-by-line basis. If NULLs are passed, the width and height are not used. */ /* Image resolution function. */ /*static*/ ImageInfo *ImageResolve (Widget w, char *src, int noload, char *wid, char *hei) { extern Widget view; /*hw->html.view*/ Widget swin = current_win->scrolled_win; int i, cnt; unsigned char *data; unsigned char *bg_map; unsigned char *bgptr; unsigned char *cptr; unsigned char *ptr; int width, height; int Used[256]; XColor colrs[256]; ImageInfo *img_data; /* char *txt;*/ int widthbyheight = 0; char *fnam; int rc; int bg, bg_red, bg_green, bg_blue; #ifdef HAVE_HDF int ishdf = 0; char *hdfref = NULL; #endif mo_window *win = NULL; extern int Vclass; static Pixel fg_pixel, bg_pixel; static int done_fetch_colors = 0; int j,bcnt; /* Cache Load Hack - BJS 1/18/96 */ /* Fixed - SWP 02/01/96 */ if(!w) { fnam = src; while(*src != '\n') src++; *src='\0'; src++; goto stuffcache; } #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "[ImageResolve] I'm entering, src '%s', noload %d!\n", src, noload); #endif if (!src) return NULL; if (current_win && current_win->scrolled_win == w) { #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "[ImageResolve] already have current_win\n"); #endif goto foundit; } while (win = mo_next_window (win)) { if (win->scrolled_win == w) { mo_set_current_cached_win (win); cached_url = win->current_node ? win->current_node->url : "lose"; win->cached_url = cached_url; goto foundit; } } /* Shit outta luck. */ #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "[ImageResolve] aaaaaaaaaaaaaaaaaaaaagh\n"); #endif return NULL; foundit: #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "[ImageResolve] hello! win 0x%08x\n", win); #endif /* Here is one place we do image_kill */ if(src){ if(imagekill_sites != NULL) { for(i = 0; imagekill_sites[i]; i++) { if(strstr(cached_url, imagekill_sites[i])) { extern Widget view; /*hw->html.view*/ Widget swin = current_win->scrolled_win; if (!done_fetch_colors){ if (!view) return NULL; /* First, go fetch the pixels. */ XtVaGetValues(view, XtNforeground, &fg_pixel, XtNbackground, &bg_pixel, NULL); done_fetch_colors = 1; } RETURN_IMGINFO_FROM_BITMAP(blank); } } } } /* Internal images. */ if (strncmp (src, "internal-", 9) == 0) { if (!done_fetch_colors) { if (!view) return NULL; /* First, go fetch the pixels. */ XtVaGetValues(view, XtNforeground, &fg_pixel, XtNbackground, &bg_pixel, NULL); done_fetch_colors = 1; } if (strcmp (src, "internal-gopher-image") == 0) RETURN_IMGINFO_FROM_BITMAP(gopher_image); if (strcmp (src, "internal-gopher-movie") == 0) RETURN_IMGINFO_FROM_BITMAP(gopher_movie); if (strcmp (src, "internal-gopher-menu") == 0) RETURN_IMGINFO_FROM_BITMAP(gopher_menu); if (strcmp (src, "internal-gopher-text") == 0) RETURN_IMGINFO_FROM_BITMAP(gopher_text); if (strcmp (src, "internal-gopher-sound") == 0) RETURN_IMGINFO_FROM_BITMAP(gopher_sound); if (strcmp (src, "internal-gopher-index") == 0) RETURN_IMGINFO_FROM_BITMAP(gopher_index); if (strcmp (src, "internal-gopher-telnet") == 0) RETURN_IMGINFO_FROM_BITMAP(gopher_telnet); if (strcmp (src, "internal-gopher-binary") == 0) RETURN_IMGINFO_FROM_BITMAP(gopher_binary); if (strcmp (src, "internal-gopher-unknown") == 0) RETURN_IMGINFO_FROM_BITMAP(gopher_unknown); } #ifdef HAVE_HDF /* Incoming HDF image references will only be anchors, so we can determine that they are in fact HDF image references by doing strncmp here. */ if (strncmp (src, "#hdfref;", 8) == 0) ishdf = 1; /* OK, src is the URL we have to go hunt down. First, we go get it. */ /* We can use cached_url here, since we set it in do_window_text. */ if (ishdf) { hdfref = &(src[8]); src = mo_url_canonicalize_keep_anchor (src, cached_url); } else #endif stuffcache: src = mo_url_canonicalize (src, cached_url); /* Go see if we already have the image info hanging around. */ img_data = mo_fetch_cached_image_data (src); if (img_data && img_data->image_data) { unsigned long bg_pixel; XColor tmpcolr; if (img_data->bg_index>=0) { /* This code copied from xpmread.c. I could almost * delete the code from there, but I suppose an XPM * file could pathalogially have multiple transparent * colour indicies. -- GWP */ XtVaGetValues(view, XtNbackground, &bg_pixel, NULL); tmpcolr.pixel = bg_pixel; XQueryColor(XtDisplay(view), (installed_colormap ? installed_cmap : DefaultColormap(XtDisplay(view), DefaultScreen(XtDisplay(view)))), &tmpcolr); img_data->reds[img_data->bg_index]=tmpcolr.red; img_data->greens[img_data->bg_index]=tmpcolr.green; img_data->blues[img_data->bg_index]=tmpcolr.blue; } img_data->src = strdup(src); free (src); if (cci_event) MoCCISendEventOutput(IMAGE_LOADED); return (ImageInfo *)img_data; /* if (!wid && !hei) { return (ImageInfo *)img_data; } return(scaleImage(img_data,wid,hei)); */ } /* If we don't have the image cached and noload is high, then just return NULL to avoid doing a network load. */ /* Also return if interrupted is high. */ if (noload || interrupted) { #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "RETURNING Null noload %d interrupted %d\n", noload, interrupted); #endif free (src); return NULL; } #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "GOING ON THROUGH...\n"); #endif /* * No transparent background by default */ bg = -1; bg_map = NULL; #ifdef HAVE_HDF if (ishdf) { img_data = (ImageInfo *)hdfGrokImage (mo_hdf_fetch_local_filename (src), hdfref, &bg); #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "[ImageResolve] Did hdfGrokImage, got back 0x%08x\n", img_data); #endif if (!img_data) return NULL; /* Yet another bandaid... */ img_data->internal = 0; /* Fill out colrs array. */ for (i = 0; i < 256; i++) { colrs[i].red = img_data->reds[i]; colrs[i].green = img_data->greens[i]; colrs[i].blue = img_data->blues[i]; colrs[i].pixel = i; colrs[i].flags = DoRed|DoGreen|DoBlue; } if (bg >= 0) { bg_red = colrs[bg].red; bg_green = colrs[bg].green; bg_blue = colrs[bg].blue; bg_map = (unsigned char *)malloc(img_data->width * img_data->height); } } else #endif /* HAVE_HDF */ { /* if w is NULL we're stuffing the cache with our own info... BJS */ if(w) { /* We have to load the image. */ fnam = mo_tmpnam(src); interrupted = 0; rc = mo_pull_er_over_virgin (src, fnam); if (!rc) { #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "mo_pull_er_over_virgin returned %d; bonging\n", rc); #endif free (fnam); return NULL; } #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "[ImageResolve] Got through mo_pull_er_over_virgin, rc %d\n", rc); #endif #if 0 /* This causes problems. */ { /* Check use_this_url_instead from HTAccess.c. */ extern char *use_this_url_instead; if (use_this_url_instead) { free (src); src = use_this_url_instead; } } #endif /*********************************************/ /* Send it through CCI if need be */ MoCCISendBrowserViewFile(src, "unknown", fnam); } data = ReadBitmap(fnam, &width, &height, colrs, &bg); #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "[ImageResolve] Did ReadBitmap, got 0x%08x\n", data); #endif /* if we have a transparent background, prepare for it */ if ((bg >= 0)&&(data != NULL)) { unsigned long bg_pixel; XColor tmpcolr; /* This code copied from xpmread.c. I could almost * delete the code from there, but I suppose an XPM * file could pathalogially have multiple transparent * colour indicies. -- GWP */ XtVaGetValues(view, XtNbackground, &bg_pixel, NULL); tmpcolr.pixel = bg_pixel; XQueryColor(XtDisplay(view), (installed_colormap ? installed_cmap : DefaultColormap(XtDisplay(view), DefaultScreen(XtDisplay(view)))), &tmpcolr); bg_red = colrs[bg].red = tmpcolr.red; bg_green = colrs[bg].green = tmpcolr.green; bg_blue = colrs[bg].blue = tmpcolr.blue; colrs[bg].flags = DoRed|DoGreen|DoBlue; bg_map = (unsigned char *)malloc(width * height); } /* Now delete the file. */ unlink(fnam); { char *hfnam = (char *)malloc ((strlen (fnam) + strlen(".html") + 5) * sizeof (char)); sprintf (hfnam, "%s.html", fnam); unlink(hfnam); free(hfnam); } if (w) { free (fnam); } if (data == NULL) { #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "[ImageResolve] data == NULL; punting...\n"); #endif return NULL; } { int found_bg=0; if (data!=NULL) { for (i=0; i= 0)&&(data != NULL) && get_pref_boolean(eCLIPPING) && (get_pref_int(eMAX_CLIPPING_SIZE_X)==(-1) || get_pref_int(eMAX_CLIPPING_SIZE_X)>=width) && (get_pref_int(eMAX_CLIPPING_SIZE_Y)==(-1) || get_pref_int(eMAX_CLIPPING_SIZE_Y)>=height)) { img_data->transparent=1; img_data->clip_data=(unsigned char *)malloc(width * height); memset(img_data->clip_data,0,(width*height)); img_data->bg_index=bg; } else { img_data->transparent=0; img_data->clip_data=NULL; img_data->bg_index=(-1); } img_data->width = width; img_data->height = height; img_data->image_data = data; img_data->image = 0; img_data->clip = 0; img_data->src = strdup(src); /* Bandaid for bug afflicting Eric's code, apparently. */ img_data->internal = 0; } widthbyheight = img_data->width * img_data->height; /* Fill out used array. */ for (i=0; i < 256; i++) { Used[i] = 0; } cnt = 1; bgptr = bg_map; cptr = img_data->clip_data; ptr = img_data->image_data; /*This sets the bg map and also creates bitmap data for the clip mask when there is a bg image */ for (i=0; iheight; i++) { for (j=0,bcnt=0; jwidth; j++) { if (Used[(int)*ptr] == 0) { Used[(int)*ptr] = cnt; cnt++; } if (bg>=0) { if (*ptr == bg) { *bgptr = 1; } else { *bgptr = 0; if (img_data->transparent) { *cptr += nums[(bcnt % 8)]; } } if (img_data->transparent && ((bcnt % 8)==7 || j==(img_data->width-1))) { cptr++; } bgptr++; bcnt++; } ptr++; } } cnt--; /* * If the image has too many colors, apply a median cut algorithm to * reduce the color usage, and then reprocess it. * Don't cut colors for direct mapped visuals like TrueColor. */ if ((cnt > get_pref_int(eCOLORS_PER_INLINED_IMAGE))&&(Vclass != TrueColor)) { MedianCut(img_data->image_data, &img_data->width, &img_data->height, colrs, 256, get_pref_int(eCOLORS_PER_INLINED_IMAGE)); for (i=0; i < 256; i++) Used[i] = 0; cnt = 1; ptr = img_data->image_data; for (i=0; i < widthbyheight; i++) { if (Used[(int)*ptr] == 0) { Used[(int)*ptr] = cnt; cnt++; } ptr++; } cnt--; /* if we had a transparent bg, MedianCut used it. Get a new one */ if (bg >= 0) { cnt++; bg = 256; } } img_data->num_colors = cnt; #ifdef HAVE_HDF if (!ishdf) #endif { img_data->reds = (int *)malloc(sizeof(int) * cnt); img_data->greens = (int *)malloc(sizeof(int) * cnt); img_data->blues = (int *)malloc(sizeof(int) * cnt); } /* bg is not set in here if it gets munged by MedCut */ for (i=0; i < 256; i++) { int indx; if (Used[i] != 0) { indx = Used[i] - 1; img_data->reds[indx] = colrs[i].red; img_data->greens[indx] = colrs[i].green; img_data->blues[indx] = colrs[i].blue; /* squeegee in the background color */ if ((bg >= 0)&&(i == bg)) { img_data->reds[indx] = bg_red; img_data->greens[indx] = bg_green; img_data->blues[indx] = bg_blue; img_data->bg_index=indx; } } } /* if MedianCut ate our background, add the new one now. */ if (bg == 256) { img_data->reds[cnt - 1] = bg_red; img_data->greens[cnt - 1] = bg_green; img_data->blues[cnt - 1] = bg_blue; img_data->bg_index=(cnt-1); } bgptr = bg_map; cptr = img_data->clip_data; ptr = img_data->image_data; for (i=0; i < widthbyheight; i++) { *ptr = (unsigned char)(Used[(int)*ptr] - 1); /* if MedianCut ate the background, enforce it here */ if (bg == 256) { if (*bgptr) { *ptr = (unsigned char)(cnt - 1); } bgptr++; } ptr++; } /* free the background map if we have one */ if (bg_map != NULL) { free (bg_map); } #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "[ImageResolve] Doing mo_cache_data on '%s', 0x%08x\n", src, img_data); #endif mo_cache_data (src, (void *)img_data, 0); free (src); #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "[ImageResolve] Leaving...\n"); #endif if (cci_event) MoCCISendEventOutput(IMAGE_LOADED); return img_data; /* if (!wid && !hei) { return img_data; } return(scaleImage(img_data,wid,hei)); */ } static ImageInfo *DelayedImageResolve (Widget w, char *src) { ImageInfo *img; #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "[DelayedImageResolve] I'm entering, src '%s'\n", src); #endif interrupted = 0; loading_inlined_images = 1; img = ImageResolve (w, src, 0, NULL, NULL); loading_inlined_images = 0; if (interrupted == 1) { #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "[DelayedImageResolve] Interrupted...\n"); #endif interrupted = 0; } #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "[DelayedImageResolve] Returning 0x%08x\n", img); #endif /* need this here now...but may make other stuff look cheeeezy -- SWP */ mo_gui_done_with_icon (); mo_gui_check_icon(-1); return img; } mo_status mo_free_image_data (void *ptr) { ImageInfo *img = (ImageInfo *)ptr; Widget swin=current_win->scrolled_win; #ifndef DISABLE_TRACE if (srcTrace) fprintf (stderr, "[mo_free_image_info] Freeing 0x%08x\n", img); #endif if (!img) return mo_fail; if (img->reds) { free (img->reds); img->reds = NULL; } if (img->greens) { free (img->greens); img->greens = NULL; } if (img->blues) { free (img->blues); img->blues = NULL; } if (img->image_data) { free (img->image_data); img->image_data = NULL; } if (img->clip_data) { free (img->clip_data); img->clip_data = NULL; } if (img->src) { free(img->src); img->src=NULL; } if (img->image!=None) { XFreePixmap(XtDisplay(swin),img->image); img->image=None; } if (img->clip!=None) { XFreePixmap(XtDisplay(swin),img->clip); img->clip=None; } return mo_succeed; } mo_status mo_register_image_resolution_function (mo_window *win) { XmxSetArg (WbNresolveImageFunction, (long)ImageResolve); XmxSetArg (WbNresolveDelayedImage, (long)DelayedImageResolve); XmxSetValues (win->scrolled_win); return mo_succeed; }