home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / modules / libimg / src / if.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  75.4 KB  |  2,284 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /* -*- Mode: C; tab-width: 4 -*-
  20.  *   if.c --- Top-level image library routines
  21.  */
  22.  
  23. /*
  24. #include "xp.h"
  25. */
  26. #include "if.h"
  27.  
  28. #include "merrors.h"
  29. #include "shist.h"
  30. #include "xpgetstr.h"
  31.  
  32. #include "il_strm.h"            /* Stream converters. */
  33.  
  34. extern int XP_MSG_IMAGE_PIXELS;
  35. extern int MK_UNABLE_TO_LOCATE_FILE;
  36. extern int MK_OUT_OF_MEMORY;
  37.  
  38. #define HOWMANY(x, r)     (((x) + ((r) - 1)) / (r))
  39. #define ROUNDUP(x, r)     (HOWMANY(x, r) * (r))
  40.  
  41. #ifdef PROFILE
  42. #pragma profile on
  43. #endif
  44.  
  45. int il_debug=0;
  46.  
  47. /* Global list of image group contexts. */
  48. static IL_GroupContext *il_global_img_cx_list = NULL;
  49.  
  50.  
  51. /*********************** Image Observer Notification. *************************
  52. *
  53. * These functions are used to send messages to registered observers of an
  54. * individual image client i.e. an IL_ImageReq.
  55. *
  56. ******************************************************************************/
  57.  
  58. /* Fabricate a descriptive title for the image and notify observers.  Used for
  59.    calls to the image viewer. */
  60. static void
  61. il_description_notify(il_container *ic)
  62. {
  63.     char buf[36], buf2[12];
  64.     IL_MessageData message_data;
  65.     IL_ImageReq *image_req;
  66.     NI_PixmapHeader *img_header = &ic->image->header;
  67.     
  68.     XP_BZERO(&message_data, sizeof(IL_MessageData));
  69.  
  70.     switch (ic->type) {
  71.         /* XXX - This needs to be fixed for Image Plugins. */
  72.     case IL_GIF : XP_STRCPY(buf2, "GIF"); break;
  73.     case IL_XBM : XP_STRCPY(buf2, "XBM"); break;
  74.     case IL_JPEG : XP_STRCPY(buf2, "JPEG"); break;
  75.     case IL_PNG : XP_STRCPY(buf2, "PNG"); break;
  76.  
  77.     default : XP_STRCPY(buf2, "");
  78.     }
  79.     XP_SPRINTF(buf, XP_GetString(XP_MSG_IMAGE_PIXELS), buf2,
  80.                img_header->width, img_header->height);
  81.  
  82.     XP_ASSERT(ic->clients);
  83.     for (image_req = ic->clients; image_req; image_req = image_req->next) {
  84.         if (image_req->is_view_image) {
  85.             message_data.image_instance = image_req;
  86.             message_data.description = buf; /* Note scope limitation.  Observer
  87.                                                must copy if necessary. */
  88.             XP_NotifyObservers(image_req->obs_list, IL_DESCRIPTION, &message_data);
  89.         }
  90.     }
  91. }
  92.  
  93. /* Notify observers of the target dimensions of the image. */
  94. static void
  95. il_dimensions_notify(il_container *ic, int dest_width, int dest_height)
  96. {
  97.     IL_MessageData message_data;
  98.     IL_ImageReq *image_req;
  99.     
  100.     XP_BZERO(&message_data, sizeof(IL_MessageData));
  101.  
  102.     XP_ASSERT(ic->clients);
  103.     for (image_req = ic->clients; image_req; image_req = image_req->next) {
  104.         message_data.image_instance = image_req;
  105.         message_data.width = dest_width;   /* Note: these are stored as */
  106.         message_data.height = dest_height; /* uint16s. */
  107.         XP_NotifyObservers(image_req->obs_list, IL_DIMENSIONS, &message_data);
  108.     }
  109. }
  110.  
  111. /* Notify observers that the image is transparent and has a mask. */
  112. static void
  113. il_transparent_notify(il_container *ic)
  114. {
  115.     IL_MessageData message_data;
  116.     IL_ImageReq *image_req;
  117.     
  118.     XP_BZERO(&message_data, sizeof(IL_MessageData));
  119.  
  120.     XP_ASSERT(ic->clients);
  121.     for (image_req = ic->clients; image_req; image_req = image_req->next) {
  122.         message_data.image_instance = image_req;
  123.         XP_NotifyObservers(image_req->obs_list, IL_IS_TRANSPARENT,
  124.                            &message_data);
  125.     }
  126. }
  127.  
  128. /* Notify observers that the image pixmap has been updated. */
  129. void
  130. il_pixmap_update_notify(il_container *ic)
  131. {
  132.     IL_MessageData message_data;
  133.     IL_Rect *update_rect = &message_data.update_rect;
  134.     IL_ImageReq *image_req;
  135.  
  136.     XP_BZERO(&message_data, sizeof(IL_MessageData));
  137.  
  138.     update_rect->x_origin = 0;
  139.     update_rect->y_origin = (uint16)ic->update_start_row;
  140.     update_rect->width = (uint16)ic->image->header.width;
  141.     update_rect->height = (uint16)(ic->update_end_row-ic->update_start_row+1);
  142.  
  143.     XP_ASSERT(ic->clients);
  144.     for(image_req = ic->clients; image_req; image_req = image_req->next) {
  145.         /* The user aborted the image loading.  Don't display any more image
  146.            data */
  147.         if (image_req->stopped)
  148.             continue;
  149.  
  150.         message_data.image_instance = image_req;
  151.         XP_NotifyObservers(image_req->obs_list, IL_PIXMAP_UPDATE,
  152.                            &message_data);
  153.     }
  154. }
  155.  
  156. /* Notify observers that the image has finished decoding. */
  157. void
  158. il_image_complete_notify(il_container *ic)
  159. {
  160.     IL_MessageData message_data;
  161.     IL_ImageReq *image_req;
  162.     
  163.     XP_BZERO(&message_data, sizeof(IL_MessageData));
  164.  
  165.     XP_ASSERT(ic->clients);
  166.     for (image_req = ic->clients; image_req; image_req = image_req->next) {
  167.         message_data.image_instance = image_req;
  168.         XP_NotifyObservers(image_req->obs_list, IL_IMAGE_COMPLETE,
  169.                            &message_data);
  170.     }
  171. }
  172.  
  173. /* Notify observers that a frame of an image animation has finished
  174.    decoding. */
  175. void
  176. il_frame_complete_notify(il_container *ic)
  177. {
  178.     IL_MessageData message_data;
  179.     IL_ImageReq *image_req;
  180.     
  181.     XP_BZERO(&message_data, sizeof(IL_MessageData));
  182.  
  183.     XP_ASSERT(ic->clients);
  184.     for (image_req = ic->clients; image_req; image_req = image_req->next) {
  185.         message_data.image_instance = image_req;
  186.         XP_NotifyObservers(image_req->obs_list, IL_FRAME_COMPLETE,
  187.                            &message_data);
  188.     }
  189. }
  190.  
  191.  
  192. /* Notify observers of image progress. */
  193. void
  194. il_progress_notify(il_container *ic)
  195. {
  196.     uint percent_done;
  197.     static uint last_percent_done = 0;
  198.     int row = ic->update_end_row;
  199.     IL_MessageData message_data;
  200.     IL_ImageReq *image_req;
  201.     NI_PixmapHeader *img_header = &ic->image->header;
  202.     
  203.     XP_BZERO(&message_data, sizeof(IL_MessageData));
  204.  
  205.     /* No more progress bars for GIF animations after initial load. */
  206.     if (ic->is_looping)
  207.         return;
  208.  
  209.     /* Calculate the percentage of image decoded (not displayed) */
  210.     if(ic->content_length) {
  211.         percent_done =
  212.             (uint32)100 * ic->bytes_consumed / ((uint32)ic->content_length);
  213.  
  214.     /* Some protocols, e.g. gopher, don't support content-length, so
  215.      * show the percentage of the image displayed instead
  216.      */
  217.     } else {
  218.         /* Could be zero if before il_size() is called. */
  219.         if (img_header->height == 0)
  220.             return;
  221.  
  222.         /* Interlaced GIFs are weird */
  223.         if (ic->type == IL_GIF) {
  224.             percent_done = il_gif_compute_percentage_complete(row, ic);
  225.         }
  226.         else
  227.         {
  228.             /* This isn't correct for progressive JPEGs, but there's
  229.              * really nothing we can do about that because the number
  230.              * of scans in a progressive JPEG isn't known until the
  231.              * whole file has been read.
  232.              */
  233.             percent_done = (uint)(row * (uint32)100 / img_header->height);
  234.  
  235.         }
  236.     }
  237.  
  238.     if (percent_done != last_percent_done) {
  239.         XP_ASSERT(ic->clients);
  240.         for (image_req = ic->clients; image_req; image_req = image_req->next) {
  241.             if (image_req->is_view_image) { /* XXXM12N Eliminate this test? */
  242.                 message_data.image_instance = image_req;
  243.                 message_data.percent_progress = percent_done;
  244.                 
  245.                 XP_NotifyObservers(image_req->obs_list, IL_PROGRESS,
  246.                                    &message_data);
  247.             }
  248.         }
  249.         last_percent_done = percent_done;
  250.     }
  251. }
  252.  
  253.  
  254. /* Notify observers of information pertaining to a cached image.  Note that
  255.    this notification is per image request and not per image container. */
  256. void
  257. il_cache_return_notify(IL_ImageReq *image_req)
  258. {
  259.     IL_MessageData message_data;
  260.     il_container *ic = image_req->ic;
  261.  
  262.     XP_BZERO(&message_data, sizeof(IL_MessageData));
  263.     message_data.image_instance = image_req;
  264.  
  265.     /* This function should only be called if the dimensions are known. */
  266.     XP_ASSERT(ic->state >= IC_SIZED);
  267.  
  268.     /* First notify observers of the image dimensions. */
  269.     message_data.width = ic->dest_width;   /* Note: these are stored as */
  270.     message_data.height = ic->dest_height; /* uint16s. */
  271.     XP_NotifyObservers(image_req->obs_list, IL_DIMENSIONS, &message_data);
  272.     message_data.width = message_data.height = 0;
  273.  
  274.     /* Fabricate a title for the image and notify observers.  Image Plugins
  275.        will need to supply information on the image type. */
  276.     il_description_notify(ic);
  277.  
  278.     /* If the image is transparent and has a mask, notify observers
  279.        accordingly. */
  280.     if (ic->mask)
  281.         XP_NotifyObservers(image_req->obs_list, IL_IS_TRANSPARENT,
  282.                            &message_data);
  283.  
  284.     /* Now send observers a pixmap update notification for the displayable
  285.        area of the image. */
  286.     XP_BCOPY(&ic->displayable_rect, &message_data.update_rect,
  287.              sizeof(IL_Rect));
  288.     XP_NotifyObservers(image_req->obs_list, IL_PIXMAP_UPDATE, &message_data);
  289.     XP_BZERO(&message_data.update_rect, sizeof(IL_Rect));
  290.  
  291.     if (ic->state == IC_COMPLETE) {
  292.         /* Send the observers a frame complete message. */
  293.         XP_NotifyObservers(image_req->obs_list, IL_FRAME_COMPLETE,
  294.                            &message_data);
  295.  
  296.         /* Finally send the observers a complete message. */
  297.         XP_NotifyObservers(image_req->obs_list, IL_IMAGE_COMPLETE,
  298.                            &message_data);
  299.     }
  300. }
  301.  
  302.  
  303. /* Notify observers that the image request is being destroyed.  This
  304.    provides an opportunity for observers to remove themselves from the
  305.    observer callback list and to do any related cleanup. */
  306. void
  307. il_image_destroyed_notify(IL_ImageReq *image_req)
  308. {
  309.     IL_MessageData message_data;
  310.     
  311.     XP_BZERO(&message_data, sizeof(IL_MessageData));
  312.     message_data.image_instance = image_req;
  313.     XP_NotifyObservers(image_req->obs_list, IL_IMAGE_DESTROYED, &message_data);
  314.  
  315.     /* Now destroy the observer list itself. */
  316.     XP_DisposeObserverList(image_req->obs_list);
  317. }
  318.  
  319.  
  320. /* Notify observers that an error has occured.  The observer should call
  321.    IL_DisplaySubImage in order to display the icon.  Note that this function
  322.    is called per image request and not per image. */
  323. static void
  324. il_icon_notify(IL_ImageReq *image_req, int icon_number,
  325.                XP_ObservableMsg message)
  326. {
  327.     IL_GroupContext *img_cx = image_req->img_cx;
  328.     int icon_width, icon_height;
  329.     IL_MessageData message_data;
  330.  
  331.     XP_BZERO(&message_data, sizeof(IL_MessageData));
  332.   
  333.     /* Obtain the dimensions of the icon. */
  334.     IMGCBIF_GetIconDimensions(img_cx->img_cb, img_cx->dpy_cx, &icon_width,
  335.                               &icon_height, icon_number);
  336.  
  337.     /* Fill in the message data and notify observers. */
  338.     message_data.image_instance = image_req;
  339.     message_data.icon_number = icon_number;
  340.     message_data.icon_width = icon_width;
  341.     message_data.icon_height = icon_height;
  342.     XP_NotifyObservers(image_req->obs_list, message, &message_data);
  343. }
  344.  
  345. /********************* Image Group Observer Notification. *********************
  346. *
  347. * This function is used to send messages to registered observers of an image
  348. * group i.e. an IL_GroupContext.
  349. *
  350. ******************************************************************************/
  351.  
  352. /* Notify observers that images have started/stopped loading in the context,
  353.    or started/stopped looping in the context. */
  354. void
  355. il_group_notify(IL_GroupContext *img_cx, XP_ObservableMsg message)
  356. {
  357.     IL_GroupMessageData message_data;
  358.     
  359.     XP_BZERO(&message_data, sizeof(IL_GroupMessageData));
  360.     
  361.     /* Fill in the message data and notify observers. */
  362.     message_data.display_context = img_cx->dpy_cx;
  363.     message_data.image_context = img_cx;
  364.     XP_NotifyObservers(img_cx->obs_list, message, &message_data);
  365. }
  366.  
  367.  
  368. static int
  369. il_init_scaling(il_container *ic)
  370. {
  371.     int scaled_width = ic->image->header.width;
  372.         
  373.     /* Allocate temporary scale space */
  374.     if (scaled_width != ic->src_header->width)
  375.     {
  376.         if (ic->scalerow)
  377.             XP_FREE(ic->scalerow);
  378.         
  379.         if (!(ic->scalerow = (unsigned char *)XP_ALLOC(scaled_width * 3)))
  380.         {
  381.             ILTRACE(0,("il: MEM scale row"));
  382.             return MK_OUT_OF_MEMORY;
  383.         }
  384.     }
  385.     return 0;
  386. }
  387.  
  388. /* Allocate and initialize the destination image's transparent_pixel with
  389.    the Image Library's preferred transparency color i.e. the background color
  390.    passed into IL_GetImage.  The image decoder is encouraged to use this
  391.    color, but may opt not to do so. */
  392. int
  393. il_init_image_transparent_pixel(il_container *ic)
  394. {
  395.     IL_IRGB *img_trans_pixel = ic->image->header.transparent_pixel;
  396.     
  397.     if (!img_trans_pixel) {
  398.         img_trans_pixel = XP_NEW_ZAP(IL_IRGB);
  399.         if (!img_trans_pixel)
  400.             return FALSE;
  401.  
  402.         if (ic->background_color) {
  403.             XP_MEMCPY(img_trans_pixel, ic->background_color, sizeof(IL_IRGB));
  404.         }
  405.         else {
  406.             /* A mask will always be used if no background color was
  407.                requested. */
  408.         }
  409.             
  410.         ic->image->header.transparent_pixel = img_trans_pixel;
  411.     }
  412.  
  413.     return TRUE;
  414. }
  415.  
  416. /* Destroy the destination image's transparent pixel. */
  417. void
  418. il_destroy_image_transparent_pixel(il_container *ic)
  419. {
  420.     NI_PixmapHeader *img_header = &ic->image->header;
  421.  
  422.     if (img_header->transparent_pixel)
  423.         XP_FREE(img_header->transparent_pixel);
  424.     img_header->transparent_pixel = NULL;
  425. }
  426.  
  427. /* Inform the Image Library of the source image's dimensions.  This function
  428.    determines the size of the destination image, and calls the front end to
  429.    allocate storage for the destination image's bits.  If the source image's
  430.    transparent pixel is set, and a background color was not specified for this
  431.    image request, then a mask will also be allocated for the destination
  432.    image. */
  433. int
  434. il_size(il_container *ic)
  435. {
  436.     float aspect;
  437.     int status;
  438.     uint8 img_depth;
  439.     uint32 src_width, src_height;
  440.     int32 image_bytes, old_image_bytes;
  441.     IL_GroupContext *img_cx = ic->img_cx;
  442.     NI_PixmapHeader *src_header = ic->src_header; /* Source image header. */
  443.     NI_PixmapHeader *img_header = &ic->image->header; /* Destination image
  444.                                                          header. */
  445.     
  446.     /* Get the dimensions of the source image. */
  447.     src_width = src_header->width;
  448.     src_height = src_header->height;
  449.  
  450.     /* Ensure that the source image has a sane area. */
  451.     if (!src_width || !src_height            ||
  452.         (src_width > MAX_IMAGE_WIDTH)        ||
  453.         (src_height > MAX_IMAGE_HEIGHT)) {            
  454.         ILTRACE(1,("il: bad image size: %dx%d", src_width, src_height));
  455.         return MK_IMAGE_LOSSAGE;
  456.     }
  457.  
  458.     ic->state = IC_SIZED;
  459.     if (ic->state == IC_MULTI)
  460.         return 0;
  461.  
  462.     /* For now, we don't allow an image to change output size on the
  463.      * fly, but we do allow the source image size to change, and thus
  464.      * we may need to change the scaling factor to fit it into the
  465.      * same size container on the display.
  466.      */
  467.     if (ic->sized) {
  468.         status = il_init_scaling(ic);
  469.         return status;
  470.     }
  471.  
  472.     /* This must appear before il_init_img_header. */
  473.     old_image_bytes = (int32)img_header->widthBytes * img_header->height;
  474.     
  475.     /* Initialize the dimensions of the destination image header structure
  476.        to be the dimensions of the source image.  The Display Front End will
  477.        reset these dimensions to be the target dimensions of the image if
  478.        it does not handle scaling. */
  479.     img_header->width = src_width;
  480.     img_header->height = src_height;
  481.  
  482.     /* Determine the target dimensions of the image.  */
  483.     aspect = (float)src_width/(float)src_height;
  484.     if (!ic->dest_width && !ic->dest_height) {
  485.         /* Both target dimensions were unspecified, so use the dimensions of
  486.            the source image. */
  487.         ic->dest_width  = src_width;
  488.         ic->dest_height = src_height;
  489.     }
  490.     else if (ic->dest_width && ic->dest_height) {
  491.         /* Both target dimensions were specified; determine if this causes
  492.            aspect ratio distortion. */
  493.         if (ic->dest_width * src_height != ic->dest_height * src_width)
  494.             ic->aspect_distorted = PR_TRUE;
  495.     }
  496.     else if (ic->dest_width) {
  497.         /* Only the target width was specified.  Determine the target height
  498.            which preserves aspect ratio. */
  499.         ic->dest_height = (int)((float)ic->dest_width / aspect + 0.5);
  500.     }
  501.     else {
  502.         /* Only the target height was specified.  Determine the target width
  503.            which preserves aspect ratio. */
  504.         ic->dest_width  = (int)(aspect * (float)ic->dest_height + 0.5);
  505.     }
  506.     if (ic->dest_width == 0) ic->dest_width = 1;
  507.     if (ic->dest_height == 0) ic->dest_height = 1;
  508.  
  509.     /* Determine if the image will be displayed at its natural size. */
  510.     if (ic->dest_width == src_width && ic->dest_height == src_height)
  511.         ic->natural_size = PR_TRUE;
  512.  
  513.     /* Check that the target image dimensions are reasonable. */
  514.     if ((ic->dest_width > MAX_IMAGE_WIDTH)     ||
  515.         (ic->dest_height > MAX_IMAGE_HEIGHT)) {
  516.         return MK_IMAGE_LOSSAGE;
  517.     }
  518.  
  519.     ILTRACE(2,("il: size %dx%d, scaled from %dx%d, ic = 0x%08x\n",
  520.                ic->dest_width, ic->dest_height, src_width, src_height, ic));
  521.  
  522.     /* Determine the number of bytes per scan-line.  Image data must be
  523.        quadlet aligned for optimizations. */
  524.     img_depth = img_header->color_space->pixmap_depth;
  525.     img_header->widthBytes = (img_header->width * img_depth + 7) / 8;
  526.     img_header->widthBytes = ROUNDUP(img_header->widthBytes, 4);
  527.  
  528.     /* Create and initialize the mask pixmap structure, if required.  A mask
  529.        is allocated only if the image is transparent and no background color
  530.        was specified for this image request. */
  531.     if (src_header->transparent_pixel && !ic->background_color) {
  532.         if (!ic->mask) {
  533.             NI_PixmapHeader *mask_header;
  534.     
  535.             if (!(ic->mask = XP_NEW_ZAP(IL_Pixmap))) {
  536.                 return MK_OUT_OF_MEMORY;
  537.             }
  538.  
  539.             mask_header = &ic->mask->header;
  540.             mask_header->color_space = IL_CreateGreyScaleColorSpace(1, 1);
  541.             if (!mask_header->color_space)
  542.                 return MK_OUT_OF_MEMORY;
  543.             mask_header->width = img_header->width;
  544.             mask_header->height = img_header->height;
  545.             mask_header->widthBytes = (mask_header->width + 7) / 8;
  546.  
  547.             /* Mask data must be quadlet aligned for optimizations */
  548.             mask_header->widthBytes = ROUNDUP(mask_header->widthBytes, 4);
  549.         }
  550.  
  551.         /* Notify observers that the image is transparent and has a mask. */
  552.         il_transparent_notify(ic);
  553.     }
  554.     else {                      /* Mask not required. */
  555.  
  556.         /*  for png alpha*/
  557.         if (ic->mask) {
  558.                 il_transparent_notify(ic);
  559.                 if(ic->background_color){
  560. /*
  561.                     src_header->transparent_pixel = ic->background_color;
  562.                     img_header->transparent_pixel = ic->background_color;
  563.                
  564.                     XP_MEMCPY(img_trans_pixel, ic->background_color, sizeof(IL_IRGB)); 
  565. */
  566.         }
  567. /*
  568.             il_destroy_pixmap(ic->img_cb, ic->mask);
  569.             ic->mask = NULL;
  570. */
  571.         }
  572.         
  573.     }
  574.  
  575.     ic->sized = 1;
  576.     /* Notify observers of the target dimensions of the image. */
  577.     il_dimensions_notify(ic, ic->dest_width, ic->dest_height);
  578.  
  579.     /* Fabricate a title for the image and notify observers.  Image Plugins
  580.        will need to supply information on the image type. */
  581.     il_description_notify(ic);
  582.  
  583.  
  584.     /* If the display front-end doesn't support scaling, IMGCBIF_NewPixmap will
  585.        set the image and mask dimensions to scaled_width and scaled_height. */
  586.     IMGCBIF_NewPixmap(img_cx->img_cb, img_cx->dpy_cx, ic->dest_width,
  587.                       ic->dest_height, ic->image, ic->mask);
  588.     
  589.     if (!ic->image->bits)
  590.         return MK_OUT_OF_MEMORY;
  591.  
  592.     if (ic->mask && !ic->mask->bits)
  593.         return MK_OUT_OF_MEMORY;
  594.  
  595.     /* Adjust the total cache byte count to reflect any departure from the
  596.        original predicted byte count for this image. */
  597.     image_bytes = (int32)img_header->widthBytes * img_header->height;
  598.     if (image_bytes - old_image_bytes)
  599.         il_adjust_cache_fullness(image_bytes - old_image_bytes);
  600.  
  601.     /* If we have a mask, initialize its bits. */
  602.     if (ic->mask) {
  603.         NI_PixmapHeader *mask_header = &ic->mask->header;
  604.         uint32 mask_size = mask_header->widthBytes * mask_header->height;
  605.  
  606.         IMGCBIF_ControlPixmapBits(img_cx->img_cb, img_cx->dpy_cx, ic->mask,
  607.                                   IL_LOCK_BITS);
  608.                                 
  609.         memset (ic->mask->bits, ~0, mask_size);
  610.         
  611.         IMGCBIF_ControlPixmapBits(img_cx->img_cb, img_cx->dpy_cx, ic->mask,
  612.                                   IL_UNLOCK_BITS);
  613.     }
  614.     
  615.     if ((status = il_init_scaling(ic)) < 0)
  616.         return status;
  617.  
  618.  
  619.     /* XXXM12N Clean this up */
  620.     /* Get memory for quantizing.  (required amount depends on image width) */
  621.     if (img_header->color_space->type == NI_PseudoColor) {
  622.         if (!il_init_quantize(ic)) {
  623.             ILTRACE(0,("il: MEM il_init_quantize"));
  624.             return MK_OUT_OF_MEMORY;
  625.         }
  626.     }
  627.  
  628.     return 0;
  629. }
  630.  
  631.  
  632. #ifdef XP_OS2
  633. #define IL_SIZE_CHUNK   16384
  634. #else
  635. #define IL_SIZE_CHUNK    128
  636. #endif
  637. #ifdef XP_MAC
  638. #    if GENERATINGPOWERPC
  639. #        define IL_PREFERRED_CHUNK 8192
  640. #        define IL_OFFSCREEN_CHUNK 128
  641. #    else    /* normal mac */
  642. #        define IL_PREFERRED_CHUNK 4096
  643. #        define IL_OFFSCREEN_CHUNK 128
  644. #    endif
  645. #else /* !XP_MAC */
  646. #ifdef XP_OS2
  647. #    define IL_PREFERRED_CHUNK (12*1024)
  648. #    define IL_OFFSCREEN_CHUNK ( 6*1024)
  649. #else
  650. #    define IL_PREFERRED_CHUNK  2048 
  651. #    define IL_OFFSCREEN_CHUNK 128 
  652. #endif
  653. #endif
  654.  
  655. unsigned int
  656. il_write_ready(NET_StreamClass *stream)
  657. {
  658.     il_container *ic = (il_container *)stream->data_object;
  659.     uint request_size = 1;
  660.  
  661.     if (ic->write_ready)
  662.         request_size = (*ic->write_ready)(ic);
  663.  
  664.     if (!request_size)
  665.         return 0;
  666.  
  667.     /*
  668.      * It could be that layout aborted image loading by calling IL_FreeImage
  669.      * before the netlib finished transferring data.  Don't do anything.
  670.      */
  671.     if (ic->state == IC_ABORT_PENDING)
  672.         return IL_OFFSCREEN_CHUNK;
  673.  
  674.     if (!ic->sized)
  675.         /* A (small) default initial chunk */
  676.         return IL_SIZE_CHUNK;
  677.  
  678.     return IL_PREFERRED_CHUNK;
  679. }
  680.  
  681. /* Given the first few bytes of a stream, identify the image format */
  682. static int
  683. il_type(int suspected_type, const char *buf, int32 len)
  684. {
  685.     int i;
  686.  
  687.     if (len >= 4 && !strncmp(buf, "GIF8", 4)) 
  688.     {
  689.         return IL_GIF;
  690.     }
  691.  
  692.       /* for PNG */
  693.     if (len >= 4 && ((unsigned char)buf[0]==0x89 &&
  694.                      (unsigned char)buf[1]==0x50 &&
  695.                      (unsigned char)buf[2]==0x4E &&
  696.                      (unsigned char)buf[3]==0x47))
  697.     { 
  698.         return IL_PNG;
  699.     }
  700.  
  701.  
  702.     /* JFIF files start with SOI APP0 but older files can start with SOI DQT
  703.      * so we test for SOI followed by any marker, i.e. FF D8 FF
  704.      * this will also work for SPIFF JPEG files if they appear in the future.
  705.      *
  706.      * (JFIF is 0XFF 0XD8 0XFF 0XE0 <skip 2> 0X4A 0X46 0X49 0X46 0X00)
  707.      */
  708.     if (len >= 3 &&
  709.        ((unsigned char)buf[0])==0xFF &&
  710.        ((unsigned char)buf[1])==0xD8 &&
  711.        ((unsigned char)buf[2])==0xFF)
  712.     {
  713.         return IL_JPEG;
  714.     }
  715.  
  716.     /* no simple test for XBM vs, say, XPM so punt for now */
  717.     if (len >= 8 && !strncmp(buf, "#define ", 8) ) 
  718.     {
  719.         /* Don't contradict the given type, since this ID isn't definitive */
  720.         if ((suspected_type == IL_UNKNOWN) || (suspected_type == IL_XBM))
  721.             return IL_XBM;
  722.     }
  723.  
  724.     if (len < 35) 
  725.     {
  726.         ILTRACE(1,("il: too few bytes to determine type"));
  727.         return suspected_type;
  728.     }
  729.  
  730.     /* all the servers return different formats so root around */
  731.     for (i=0; i<28; i++)
  732.     {
  733.         if (!strncmp(&buf[i], "Not Fou", 7))
  734.             return IL_NOTFOUND;
  735.     }
  736.     
  737.     return suspected_type;
  738. }
  739.  
  740. /*
  741.  *    determine what kind of image data we are dealing with
  742.  */
  743. int
  744. IL_Type(const char *buf, int32 len)
  745. {
  746.     return il_type(IL_UNKNOWN, buf, len);
  747. }
  748.  
  749. static int 
  750. il_write(NET_StreamClass *stream, const unsigned char *str, int32 len)
  751. {
  752.     il_container *ic = (il_container *)stream->data_object;
  753.     int err = 0;
  754.  
  755.     ILTRACE(4, ("il: write with %5d bytes for %s\n", len, ic->url_address));
  756.  
  757.     /*
  758.      * Layout may have decided to abort this image in mid-stream,
  759.      * but netlib doesn't know about it yet and keeps sending
  760.      * us data.  Force the netlib to abort.
  761.      */
  762.     if (ic->state == IC_ABORT_PENDING)
  763.         return -1;
  764.  
  765.     /* Has user hit the stop button ? */
  766.     if (il_image_stopped(ic))
  767.         return -1;
  768.  
  769.     ic->bytes_consumed += len;
  770.     
  771.     if (len)
  772.         err = (*ic->write)(ic, (unsigned char *)str, len);
  773.  
  774.     /* Notify observers of image progress. */
  775.     il_progress_notify(ic);
  776.  
  777.     if (err < 0)
  778.         return err;
  779.     else
  780.         return len;
  781. }
  782.  
  783.  
  784. int
  785. il_first_write(NET_StreamClass *stream, const unsigned char *str, int32 len)
  786. {
  787.     void *dobj=stream->data_object;
  788.     il_container *ic = (il_container *)dobj;
  789.     int (*init)(il_container *);
  790.  
  791.     XP_ASSERT(ic);
  792.     XP_ASSERT(dobj == ic->stream->data_object);
  793.     XP_ASSERT(ic->image);
  794.  
  795.     /* If URL redirection occurs, the url stored in the
  796.     image container is the redirect url not the image file url.
  797.     If the image is animated, the imglib will never match the
  798.     file name in the cache unless you update ic->url_address.
  799.     ic->fetch_url keeps the actual url for you.
  800.      */    
  801.     if((ic->url)&&(ic->url->address)){
  802.         ic->fetch_url = XP_STRDUP(ic->url->address);
  803.     }
  804.     else{
  805.     if(ic->url_address) /* check needed because of mkicons.c */
  806.             ic->fetch_url = XP_STRDUP(ic->url_address);
  807.     else
  808.         ic->fetch_url = NULL;
  809.     }
  810.  
  811.     /* Figure out the image type, possibly overriding the given MIME type */
  812.     ic->type = il_type(ic->type, (const char*) str, len);
  813.     ic->write_ready = NULL;
  814.  
  815.     /* Grab the URL's expiration date */
  816.     if (ic->url)
  817.       ic->expires = ic->url->expires;
  818.  
  819.     switch (ic->type) 
  820.     {
  821.         case IL_GIF:           
  822.             init = il_gif_init;
  823.             ic->write = il_gif_write;
  824.             ic->complete = il_gif_complete;
  825.                     ic->write_ready = il_gif_write_ready;
  826.                     ic->abort = il_gif_abort;
  827.             break;
  828.  
  829.         case IL_XBM:
  830.             init = il_xbm_init;
  831.             ic->write = il_xbm_write;
  832.             ic->abort = il_xbm_abort;
  833.             ic->complete = il_xbm_complete;
  834.             break;
  835.  
  836.         case IL_JPEG:
  837.             init = il_jpeg_init;
  838.             ic->write = il_jpeg_write;
  839.             ic->abort = il_jpeg_abort;
  840.                     ic->complete = il_jpeg_complete;
  841.             break;
  842.  
  843.            case IL_PNG:
  844.             init = il_png_init;
  845.             ic->write = il_png_write;
  846.             ic->abort = il_png_abort;
  847.                     ic->complete = il_png_complete;
  848.             break;
  849.  
  850.  
  851.         case IL_NOTFOUND:
  852.             ILTRACE(1,("il: html image"));
  853.             return MK_IMAGE_LOSSAGE;
  854.  
  855.         default: 
  856.             ILTRACE(1,("il: ignoring unknown image type (%d)", ic->type));
  857.             return MK_IMAGE_LOSSAGE;
  858.     }
  859.  
  860.     if (!(*init)(ic))
  861.     {
  862.         ILTRACE(0,("il: image init failed"));
  863.         return MK_OUT_OF_MEMORY;
  864.     }
  865.  
  866.     ic->stream->put_block = (MKStreamWriteFunc)il_write;
  867.  
  868.     /* do first write */
  869.     return ic->stream->put_block(stream, (const char*) str, len);
  870. }
  871.  
  872. /* Called when a container is aborted by Netlib. */
  873. static void
  874. il_container_aborted(il_container *ic)
  875. {
  876.     IL_ImageReq *image_req;
  877.     IL_GroupContext *img_cx;
  878.     il_context_list *current;
  879.  
  880.     /* Keep track of the fact that this container was aborted by Netlib,
  881.        and guard against multiple calls to this routine. */
  882.     if (ic->is_aborted)
  883.         return;
  884.     ic->is_aborted = PR_TRUE;
  885.  
  886.     /* Notify image observers that the image was aborted. */
  887.     for (image_req = ic->clients; image_req; image_req = image_req->next) {
  888.         il_icon_notify(image_req, IL_IMAGE_DELAYED, IL_ABORTED);
  889.     }
  890.  
  891.     /* Now handle image group observers. */
  892.     for (current = ic->img_cx_list; current; current = current->next) {
  893.         /* Increment the count of images in this context which were aborted
  894.            by Netlib.  Observer notification takes place if there were
  895.            previously no aborted images in this context. */
  896.         img_cx = current->img_cx;
  897.         if (!img_cx->num_aborted)
  898.             il_group_notify(img_cx, IL_ABORTED_LOADING);
  899.         img_cx->num_aborted++;
  900.  
  901. #ifdef DEBUG_GROUP_OBSERVER
  902.         XP_TRACE(("1 img_cx=%x total=%d loading=%d looping=%d aborted=%d",
  903.                   img_cx, img_cx->num_containers, img_cx->num_loading,
  904.                   img_cx->num_looping, img_cx->num_aborted));
  905. #endif /* DEBUG_GROUP_OBSERVER */
  906.     }
  907. }
  908.  
  909.  
  910. static void
  911. il_bad_container(il_container *ic)
  912. {
  913.       IL_ImageReq *image_req;
  914.  
  915.       ILTRACE(4,("il: bad container, sending icon"));
  916.       if (ic->type == IL_NOTFOUND) {
  917.           ic->state = IC_MISSING;
  918.           for (image_req = ic->clients; image_req; image_req = image_req->next)
  919.               il_icon_notify(image_req, IL_IMAGE_NOT_FOUND, IL_ERROR_NO_DATA);
  920.       }
  921.       else {
  922.           if (ic->state == IC_INCOMPLETE) {
  923.               il_container_aborted(ic);
  924.           }
  925.           else {
  926.               for (image_req = ic->clients; image_req;
  927.                    image_req = image_req->next)
  928.                   il_icon_notify(image_req, IL_IMAGE_BAD_DATA,
  929.                                  IL_ERROR_IMAGE_DATA_TRUNCATED);
  930.           }
  931.       }
  932.  
  933.       /* Image container gets deleted later. */
  934. }
  935.  
  936. /* NET_GetURL completion callback. */
  937. static void
  938. il_netgeturldone (URL_Struct *urls, int status, OPAQUE_CONTEXT *cx)
  939. {
  940.     IL_ImageReq *image_req;
  941.     il_container *ic = (il_container*)urls->fe_data;
  942.     XP_ASSERT(ic);
  943.  
  944.     /* Record the fact that NetLib is done loading. */
  945.     ic->is_url_loading = PR_FALSE;
  946.  
  947.     /*
  948.      * It could be that layout aborted image loading by calling IL_DestroyImage
  949.      * before the netlib finished transferring data.  If so, really do the
  950.      * freeing of the data that was deferred there.
  951.      */
  952.     if (ic->state == IC_ABORT_PENDING) {
  953.         il_delete_container(ic);
  954.         NET_FreeURLStruct(urls);
  955.         return;
  956.     }
  957.     
  958.     if (status < 0) {
  959.         ILTRACE(2,("il:net done ic=0x%08x, status=%d, ic->state=0x%02x\n",
  960.                    ic, status, ic->state));
  961.  
  962.         /* Netlib detected failure before a stream was even created. */
  963.         if (ic->state < IC_BAD)    {
  964.             if (status == MK_OBJECT_NOT_IN_CACHE)
  965.                 ic->state = IC_NOCACHE;
  966.             else if (status == MK_UNABLE_TO_LOCATE_FILE)
  967.                 ic->state = IC_MISSING;
  968.             else {
  969.                 /* status is probably MK_INTERRUPTED */
  970.                 ic->state = IC_INCOMPLETE; /* try again on reload */
  971.             }
  972.  
  973.             if (!ic->sized)    {
  974.                 if (status == MK_OBJECT_NOT_IN_CACHE) {
  975.                     for (image_req = ic->clients; image_req;
  976.                          image_req = image_req->next)
  977.                         il_icon_notify(image_req, IL_IMAGE_DELAYED,
  978.                                        IL_NOT_IN_CACHE); 
  979.                 }
  980.                 else if (status == MK_INTERRUPTED) {
  981.                     il_container_aborted(ic);
  982.                 }
  983.                 else if (status == MK_UNABLE_TO_LOCATE_FILE) {
  984.                     for (image_req = ic->clients; image_req;
  985.                          image_req = image_req->next)
  986.                         il_icon_notify(image_req, IL_IMAGE_NOT_FOUND,
  987.                                        IL_ERROR_NO_DATA);
  988.                 }
  989.                 else {
  990.                     for (image_req = ic->clients; image_req;
  991.                          image_req = image_req->next)
  992.                         il_icon_notify(image_req, IL_IMAGE_BAD_DATA,
  993.                                        IL_ERROR_IMAGE_DATA_ILLEGAL);
  994.                 }
  995.             }
  996.         }
  997.  
  998.         /* for mac */
  999.         if (status == MK_OUT_OF_MEMORY)
  1000.             NET_InterruptWindow(cx);
  1001.     }
  1002.     else {
  1003.         /* It is possible for NetLib to call the exit routine with a success status,
  1004.            even though the stream was never created (this can happen when fetching
  1005.            a mime image part which contains no image data.)  Show a missing image
  1006.            icon. */ 
  1007.         if (ic->state < IC_STREAM) {
  1008.             ic->state = IC_MISSING;
  1009.             for (image_req = ic->clients; image_req; image_req = image_req->next)
  1010.                 il_icon_notify(image_req, IL_IMAGE_NOT_FOUND, IL_ERROR_NO_DATA);
  1011.         }
  1012.     }
  1013.  
  1014.     NET_FreeURLStruct(urls);
  1015. }
  1016.  
  1017.  
  1018. void
  1019. il_image_abort(il_container *ic)
  1020. {
  1021.     if (ic->abort)
  1022.         (*ic->abort)(ic); 
  1023.  
  1024.     /* Clear any pending timeouts */
  1025.     if (ic->row_output_timeout) {
  1026.         FE_ClearTimeout(ic->row_output_timeout);
  1027.         ic->row_output_timeout = NULL;
  1028.     }
  1029. }
  1030.  
  1031. void
  1032. il_stream_complete(NET_StreamClass *stream)
  1033. {
  1034.     void *data_object=stream->data_object;
  1035.     il_container *ic = (il_container *)data_object;
  1036.  
  1037.     XP_ASSERT(ic);
  1038. #ifdef XP_OS2_HACK 
  1039.     if (ic->stream) { /*DSR011696 - occasionally this goes NULL and causes 
  1040.                         the assert to trap*/ 
  1041.         XP_ASSERT(data_object == ic->stream->data_object); 
  1042.     } 
  1043. #else 
  1044.     XP_ASSERT(data_object == ic->stream->data_object);
  1045. #endif /* !XP_OS2 */ 
  1046.  
  1047.     ILTRACE(1, ("il: complete: %d seconds for %s\n",
  1048.                 XP_TIME() - ic->start_time, ic->url_address));
  1049.  
  1050.     ic->is_multipart = ic->stream->is_multipart;
  1051.     ic->stream = NULL;
  1052.  
  1053.     if (ic->complete)
  1054.         (*ic->complete)(ic);
  1055.     else
  1056.         il_image_complete(ic);
  1057. }
  1058.  
  1059.  
  1060. /* Called when the container has finished loading to update the number of
  1061.    actively loading image containers in each of the client contexts.
  1062.    Non-looping images are considered to have been loaded upon natural
  1063.    completion of network activity for the image.  Looping images, on the
  1064.    other hand, are deemed to have been loaded once the first iteration of
  1065.    the animation is complete. */
  1066. static void
  1067. il_container_loaded(il_container *ic)
  1068. {
  1069.     il_context_list *current;
  1070.     IL_GroupContext *img_cx;
  1071.     
  1072.     for (current = ic->img_cx_list; current; current = current->next) {
  1073.         /* Decrement the number of actively loading image containers in the
  1074.            client context. */
  1075.         img_cx = current->img_cx;
  1076.         img_cx->num_loading--;
  1077.         if (!img_cx->num_loading) {
  1078.             /* If this is the last image to have loaded in the group,
  1079.                notify observers that images stopped loading. */
  1080.             il_group_notify(img_cx, IL_FINISHED_LOADING);
  1081.         }
  1082.  
  1083. #ifdef DEBUG_GROUP_OBSERVER
  1084.         XP_TRACE(("2 img_cx=%x total=%d loading=%d looping=%d aborted=%d",
  1085.                   img_cx, img_cx->num_containers, img_cx->num_loading,
  1086.                   img_cx->num_looping, img_cx->num_aborted));
  1087. #endif /* DEBUG_GROUP_OBSERVER */
  1088.     }
  1089. }
  1090.  
  1091.  
  1092. /* Called when a container starts/stops looping to update the number of
  1093.    looping image containers in each of the client containers.  An animated
  1094.    image is considered to have started looping at the beginning of the
  1095.    second iteration. */
  1096. static void
  1097. il_container_looping(il_container *ic) 
  1098. {
  1099.     PRBool is_looping = ic->is_looping;
  1100.     il_context_list *current;
  1101.     IL_GroupContext *img_cx;
  1102.  
  1103.     for (current = ic->img_cx_list; current; current = current->next) {
  1104.         img_cx = current->img_cx;
  1105.         if (is_looping) {       /* Image has started looping. */
  1106.             /* Increment the number of looping image containers in the client
  1107.                context.  Observer notification takes place if the image group
  1108.                previously had no looping images. */
  1109.             if (!img_cx->num_looping)
  1110.                 il_group_notify(img_cx, IL_STARTED_LOOPING);
  1111.             img_cx->num_looping++;
  1112.  
  1113. #ifdef DEBUG_GROUP_OBSERVER
  1114.             XP_TRACE(("3 img_cx=%x total=%d loading=%d looping=%d aborted=%d",
  1115.                       img_cx, img_cx->num_containers, img_cx->num_loading,
  1116.                       img_cx->num_looping, img_cx->num_aborted));
  1117. #endif /* DEBUG_GROUP_OBSERVER */
  1118.         }
  1119.         else {                  /* Image has stopped looping. */
  1120.             /* Decrement the number of looping image containers in the client
  1121.                context.  Observer notification takes place if this was
  1122.                the last looping image in the group. */
  1123.             img_cx->num_looping--;
  1124.             if (!img_cx->num_looping)
  1125.                 il_group_notify(img_cx, IL_FINISHED_LOOPING);
  1126.  
  1127. #ifdef DEBUG_GROUP_OBSERVER
  1128.             XP_TRACE(("4 img_cx=%x total=%d loading=%d looping=%d aborted=%d",
  1129.                       img_cx, img_cx->num_containers, img_cx->num_loading,
  1130.                       img_cx->num_looping, img_cx->num_aborted));
  1131. #endif /* DEBUG_GROUP_OBSERVER */
  1132.         }
  1133.     }
  1134. }
  1135.  
  1136.  
  1137. /* Called when all activity within a container has successfully completed. */
  1138. static void
  1139. il_container_complete(il_container *ic)
  1140. {
  1141.     IL_GroupContext *img_cx = ic->img_cx;
  1142.  
  1143.     ic->stream = NULL;
  1144.  
  1145.     /* Update the image (and mask) pixmaps for the final time. */
  1146.     il_flush_image_data(ic);
  1147.  
  1148.     /* Tell the Front Ends that we will not modify the bits any further. */
  1149.     IMGCBIF_ControlPixmapBits(img_cx->img_cb, img_cx->dpy_cx, ic->image,
  1150.                               IL_RELEASE_BITS);
  1151.     if (ic->mask)
  1152.         IMGCBIF_ControlPixmapBits(img_cx->img_cb, img_cx->dpy_cx, ic->mask,
  1153.                                   IL_RELEASE_BITS);
  1154.  
  1155.     if (!ic->is_looping) {
  1156.         /* Tell the client contexts that the container has finished
  1157.            loading.  We don't do this for looping images which have just
  1158.            finished looping, since we called il_container_loaded at the
  1159.            time the first iteration completed. */
  1160.         il_container_loaded(ic);
  1161.     }
  1162.     else {
  1163.         /* This is a looping image whose loop count has reached zero, so
  1164.            set the container's state to indicate that it is no longer
  1165.            looping. */
  1166.         ic->is_looping = FALSE;             
  1167.  
  1168.         /* Inform the client contexts that the container has stopped
  1169.            looping. */
  1170.         il_container_looping(ic);
  1171.     }
  1172.         
  1173.     /* Set the container's state to complete.  This indicates that the
  1174.        natural completion of network activity for the image. */
  1175.     ic->state = IC_COMPLETE;
  1176.     
  1177. }
  1178.  
  1179.  
  1180. /* XXXM12N This routine needs to be broken up into smaller components. */
  1181. void
  1182. il_image_complete(il_container *ic)
  1183. {
  1184.     NI_PixmapHeader *src_header = ic->src_header;
  1185.     IL_GroupContext *img_cx = ic->img_cx;
  1186.     IL_DisplayType display_type = ic->display_type;
  1187.  
  1188.     if (ic->state == IC_ABORT_PENDING) {
  1189.         /* It could be that layout aborted image loading by calling
  1190.            IL_DestroyImage() before the netlib finished transferring data.
  1191.            Don't do anything. */
  1192.         ic->stream = NULL;
  1193.         il_scour_container(ic);
  1194.     }
  1195.     else if (ic->state < IC_SIZED) {
  1196.         /* If we didn't size the image, but the stream finished loading, the
  1197.            image must be corrupt or truncated. */
  1198.         ic->state = IC_BAD;
  1199.         il_bad_container(ic);
  1200.     }
  1201.     else {
  1202.         XP_ASSERT(ic->state == IC_SIZED || ic->state == IC_MULTI);
  1203.         XP_ASSERT(ic->image && ic->image->bits);
  1204.  
  1205.         ILTRACE(1,("il: complete %d image width %d (%d) height %d,"
  1206.                    " depth %d, %d colors",
  1207.                    ic->multi,
  1208.                    ic->image->header.width,
  1209.                    ic->image->header.widthBytes,
  1210.                    ic->image->header.height,
  1211.                    ic->image->header.color_space->pixmap_depth, 
  1212.                    ic->image->header.color_space->cmap.num_colors));
  1213.  
  1214.         /* 3 cases: simple, multipart MIME, multi-image animation */
  1215.         if (!ic->loop_count && !ic->is_multipart) {
  1216.             /* A single frame, single part image. */
  1217.             il_container_complete(ic);
  1218.         }
  1219.         else {
  1220.             /* Display the rest of the last image before starting a new one */
  1221.             il_flush_image_data(ic);
  1222.  
  1223.             /* Force new colormap to be loaded in case its different from the
  1224.              * LOSRC or previous images in the multipart message.
  1225.              * XXX - fur - Shouldn't do this for COLORMAP case.
  1226.              */
  1227.             il_reset_palette(ic);
  1228.  
  1229.             FREE_IF_NOT_NULL(src_header->color_space->cmap.map);
  1230.             FREE_IF_NOT_NULL(src_header->transparent_pixel);
  1231.             il_destroy_image_transparent_pixel(ic);
  1232.             FREE_IF_NOT_NULL(ic->comment);
  1233.             ic->comment_length = 0;
  1234.  
  1235.             /* Handle looping, which can be used to replay an animation. */
  1236.             if (ic->loop_count) {
  1237.                 int is_infinite_loop = (ic->loop_count == -1);
  1238.                 IL_URL *netRequest = NULL;                
  1239.                 if (!is_infinite_loop)
  1240.                     ic->loop_count--;
  1241.                 
  1242.                 ILTRACE(1,("il: loop %s", ic->url_address));
  1243.  
  1244.                 netRequest =                   
  1245.                    NET_CreateURLStruct (ic->fetch_url, NET_DONT_RELOAD);
  1246.                 if (!netRequest) {   /* OOM */
  1247.                     il_container_complete(ic);
  1248.                     goto done;
  1249.                 }
  1250.                 
  1251.                 /* Only loop if the image stream is available locally.
  1252.                    Also, if the user hit the "stop" button, don't
  1253.                    allow the animation to loop. */
  1254.                 if ((NET_IsLocalFileURL(ic->fetch_url)   ||
  1255.                      NET_IsURLInMemCache(netRequest)       ||
  1256.                      NET_IsURLInDiskCache(netRequest))          &&
  1257.  
  1258.                     (!il_image_stopped(ic))                &&
  1259.                     ic->net_cx &&
  1260.                     (display_type == IL_Console))
  1261.                 {
  1262.                     if (!ic->is_looping) {
  1263.                         /* If this is the end of the first pass of the
  1264.                            animation, then set the state of the container
  1265.                            to indicate that we have started looping. */
  1266.                         ic->is_looping = TRUE;
  1267.  
  1268.                         /* At this point the animation is considered to have
  1269.                            loaded, so we need to tell the client contexts that
  1270.                            the container has loaded. */
  1271.                         il_container_loaded(ic);
  1272.  
  1273.                         /* This is also the point at which the animation is
  1274.                            considered to have started looping, so inform the
  1275.                            client contexts accordingly. */
  1276.                         il_container_looping(ic);
  1277.                     }
  1278.                     
  1279.                     ic->bytes_consumed = 0;
  1280.                     ic->state = IC_START;
  1281.                     ic->url = netRequest;
  1282.                     /* Record the fact that we are calling NetLib to load a URL. */
  1283.                     ic->is_url_loading = PR_TRUE;
  1284.  
  1285.                     /* Suppress thermo & progress bar */
  1286.                     netRequest->load_background = TRUE;
  1287.                     netRequest->fe_data = (void *)ic;
  1288.                     (void) NET_GetURL(ic->url, FO_CACHE_AND_INTERNAL_IMAGE,
  1289.                                       ic->net_cx->cx,
  1290.                                       (Net_GetUrlExitFunc*)&il_netgeturldone);
  1291.                 } else {
  1292.                     ic->loop_count = 0;
  1293.                     NET_FreeURLStruct(netRequest);
  1294.                     il_container_complete(ic);
  1295.                 }
  1296.             }
  1297.             else if (ic->is_multipart) {
  1298.                 ic->multi++;
  1299.                 ic->state = IC_MULTI;
  1300.             }
  1301.         }
  1302.     }
  1303.     
  1304.   done:
  1305.     
  1306.     /* Clear any pending timeouts */
  1307.     if (ic->row_output_timeout) {
  1308.         FE_ClearTimeout(ic->row_output_timeout);
  1309.         ic->row_output_timeout = NULL;
  1310.     }
  1311.  
  1312.     /* Notify observers that we are done decoding. */
  1313.     if (ic->state != IC_ABORT_PENDING && ic->state != IC_BAD)
  1314.         il_image_complete_notify(ic);
  1315. }
  1316.  
  1317.  
  1318. void 
  1319. il_abort(NET_StreamClass *stream, int status)
  1320. {
  1321.     il_container *ic = (il_container *)stream->data_object;
  1322.     int old_state;
  1323.     IL_ImageReq *image_req;    
  1324.  
  1325.     XP_ASSERT(ic);
  1326.  
  1327.     ILTRACE(4,("il: abort, status=%d ic->state=%d", status, ic->state));
  1328.  
  1329.     /* Abort the image. */
  1330.     il_image_abort(ic);
  1331.  
  1332.     /* It's possible that the stream is zero
  1333.        because this container is scoured already */
  1334.     if (ic->stream)
  1335.         ic->stream->data_object = 0;
  1336.     ic->stream = NULL;
  1337.  
  1338.     if(ic->state >= IC_SIZED || (ic->state == IC_ABORT_PENDING)){
  1339.         if (status == MK_INTERRUPTED){
  1340.             il_container_aborted(ic);
  1341.         }
  1342.         else{
  1343.             for (image_req = ic->clients; image_req; image_req = image_req->next)
  1344.                 il_icon_notify(image_req, IL_IMAGE_BAD_DATA,
  1345.                            IL_ERROR_IMAGE_DATA_ILLEGAL);
  1346.         }
  1347.     }
  1348.     /*
  1349.      * It could be that layout aborted image loading by calling IL_DestroyImage()
  1350.      * before the netlib finished transferring data.  Don't do anything.
  1351.      */
  1352.     if (ic->state == IC_ABORT_PENDING)
  1353.         return;
  1354.  
  1355.     old_state = ic->state;
  1356.         
  1357.     if (status == MK_INTERRUPTED)
  1358.         ic->state = IC_INCOMPLETE;
  1359.     else
  1360.         ic->state = IC_BAD;
  1361.  
  1362.     if (old_state < IC_SIZED)
  1363.         il_bad_container(ic);
  1364. }
  1365.  
  1366. NET_StreamClass *
  1367. IL_NewStream (FO_Present_Types format_out,
  1368.               void *type,
  1369.               URL_Struct *urls,
  1370.               OPAQUE_CONTEXT *cx)
  1371. {
  1372.     IL_Stream *stream = nil;
  1373.     il_container *ic = nil;
  1374.  
  1375.     /* recover the container */
  1376.     ic = (il_container*)urls->fe_data;
  1377.  
  1378.     XP_ASSERT(ic);
  1379.  
  1380.     /*
  1381.      * It could be that layout aborted image loading by calling IL_FreeImage()
  1382.      * before the netlib finished transferring data.  Don't do anything.
  1383.      */
  1384.     if (ic->state == IC_ABORT_PENDING)
  1385.         return NULL;
  1386.     
  1387.     /* Create stream object */
  1388.     if (!(stream = XP_NEW_ZAP(NET_StreamClass))) 
  1389.     {
  1390.         ILTRACE(0,("il: MEM il_newstream"));
  1391.         return 0;
  1392.     }
  1393.  
  1394.     ic->stream = stream;        /* XXXM12N We don't really need to hold on to
  1395.                                    the stream object anymore. The self pointer
  1396.                                    gets passed into the stream object's
  1397.                                    methods. */
  1398.     XP_ASSERT(ic->net_cx->cx == cx);
  1399.  
  1400.     ic->type = (int)type;
  1401.     ic->content_length = urls->content_length;
  1402.     ILTRACE(4,("il: new stream, type %d, %s", ic->type, urls->address));
  1403.     ic->state = IC_STREAM;
  1404.  
  1405. #ifndef M12N                    /* XXXM12N Fix me. */
  1406. #ifdef XP_MAC
  1407.     ic->image->hasUniqueColormap = FALSE;
  1408. #endif
  1409. #endif /* M12N */
  1410.     
  1411.     stream->name           = "image decode";
  1412.     stream->complete       = il_stream_complete;
  1413.     stream->abort           = il_abort;
  1414.     stream->is_write_ready = il_write_ready;
  1415.     stream->data_object       = (void *)ic;
  1416.     stream->window_id       = ic->net_cx->cx;
  1417.     stream->put_block       = (MKStreamWriteFunc) il_first_write;
  1418.  
  1419.     return stream;
  1420. }
  1421.  
  1422.  
  1423. /*    Phong's linear congruential hash  */
  1424. uint32
  1425. il_hash(const char *ubuf)
  1426. {
  1427.     unsigned char * buf = (unsigned char*) ubuf;
  1428.     uint32 h=1;
  1429.     while(*buf)
  1430.     {
  1431.         h = 0x63c63cd9*h + 0x9c39c33d + (int32)*buf;
  1432.         buf++;
  1433.     }
  1434.     return h;
  1435. }
  1436.  
  1437. #define IL_LAST_ICON 62
  1438. /* Extra factor of 7 is to account for duplications between
  1439.    mc-icons and ns-icons */
  1440. static uint32 il_icon_table[(IL_LAST_ICON + 7) * 2];
  1441.  
  1442. static void
  1443. il_setup_icon_table(void)
  1444. {
  1445.     int inum = 0;
  1446.  
  1447.     /* gopher/ftp icons */
  1448.     il_icon_table[inum++] = il_hash("internal-gopher-text");
  1449.     il_icon_table[inum++] = IL_GOPHER_TEXT;
  1450.     il_icon_table[inum++] = il_hash("internal-gopher-image");
  1451.     il_icon_table[inum++] = IL_GOPHER_IMAGE;
  1452.     il_icon_table[inum++] = il_hash("internal-gopher-binary");
  1453.     il_icon_table[inum++] = IL_GOPHER_BINARY;
  1454.     il_icon_table[inum++] = il_hash("internal-gopher-sound");
  1455.     il_icon_table[inum++] = IL_GOPHER_SOUND;
  1456.     il_icon_table[inum++] = il_hash("internal-gopher-movie");
  1457.     il_icon_table[inum++] = IL_GOPHER_MOVIE;
  1458.     il_icon_table[inum++] = il_hash("internal-gopher-menu");
  1459.     il_icon_table[inum++] = IL_GOPHER_FOLDER;
  1460.     il_icon_table[inum++] = il_hash("internal-gopher-index");
  1461.     il_icon_table[inum++] = IL_GOPHER_SEARCHABLE;
  1462.     il_icon_table[inum++] = il_hash("internal-gopher-telnet");
  1463.     il_icon_table[inum++] = IL_GOPHER_TELNET;
  1464.     il_icon_table[inum++] = il_hash("internal-gopher-unknown");
  1465.     il_icon_table[inum++] = IL_GOPHER_UNKNOWN;
  1466.  
  1467.     /* news icons */
  1468.     il_icon_table[inum++] = il_hash("internal-news-catchup-group");
  1469.     il_icon_table[inum++] = IL_NEWS_CATCHUP;
  1470.     il_icon_table[inum++] = il_hash("internal-news-catchup-thread");
  1471.     il_icon_table[inum++] = IL_NEWS_CATCHUP_THREAD;
  1472.     il_icon_table[inum++] = il_hash("internal-news-followup");
  1473.     il_icon_table[inum++] = IL_NEWS_FOLLOWUP;
  1474.     il_icon_table[inum++] = il_hash("internal-news-go-to-newsrc");
  1475.     il_icon_table[inum++] = IL_NEWS_GOTO_NEWSRC;
  1476.     il_icon_table[inum++] = il_hash("internal-news-next-article");
  1477.     il_icon_table[inum++] = IL_NEWS_NEXT_ART;
  1478.     il_icon_table[inum++] = il_hash("internal-news-next-article-gray");
  1479.     il_icon_table[inum++] = IL_NEWS_NEXT_ART_GREY;
  1480.     il_icon_table[inum++] = il_hash("internal-news-next-thread");
  1481.     il_icon_table[inum++] = IL_NEWS_NEXT_THREAD;
  1482.     il_icon_table[inum++] = il_hash("internal-news-next-thread-gray");
  1483.     il_icon_table[inum++] = IL_NEWS_NEXT_THREAD_GREY;
  1484.     il_icon_table[inum++] = il_hash("internal-news-post");
  1485.     il_icon_table[inum++] = IL_NEWS_POST;
  1486.     il_icon_table[inum++] = il_hash("internal-news-prev-article");
  1487.     il_icon_table[inum++] = IL_NEWS_PREV_ART;
  1488.     il_icon_table[inum++] = il_hash("internal-news-prev-article-gray");
  1489.     il_icon_table[inum++] = IL_NEWS_PREV_ART_GREY;
  1490.     il_icon_table[inum++] = il_hash("internal-news-prev-thread");
  1491.     il_icon_table[inum++] = IL_NEWS_PREV_THREAD;
  1492.     il_icon_table[inum++] = il_hash("internal-news-prev-thread-gray");
  1493.     il_icon_table[inum++] = IL_NEWS_PREV_THREAD_GREY;
  1494.     il_icon_table[inum++] = il_hash("internal-news-reply");
  1495.     il_icon_table[inum++] = IL_NEWS_REPLY;
  1496.     il_icon_table[inum++] = il_hash("internal-news-rtn-to-group");
  1497.     il_icon_table[inum++] = IL_NEWS_RTN_TO_GROUP;
  1498.     il_icon_table[inum++] = il_hash("internal-news-show-all-articles");
  1499.     il_icon_table[inum++] = IL_NEWS_SHOW_ALL_ARTICLES;
  1500.     il_icon_table[inum++] = il_hash("internal-news-show-unread-articles");
  1501.     il_icon_table[inum++] = IL_NEWS_SHOW_UNREAD_ARTICLES;
  1502.     il_icon_table[inum++] = il_hash("internal-news-subscribe");
  1503.     il_icon_table[inum++] = IL_NEWS_SUBSCRIBE;
  1504.     il_icon_table[inum++] = il_hash("internal-news-unsubscribe");
  1505.     il_icon_table[inum++] = IL_NEWS_UNSUBSCRIBE;
  1506.     il_icon_table[inum++] = il_hash("internal-news-newsgroup");
  1507.     il_icon_table[inum++] = IL_NEWS_FILE;
  1508.     il_icon_table[inum++] = il_hash("internal-news-newsgroups");
  1509.     il_icon_table[inum++] = IL_NEWS_FOLDER;
  1510.  
  1511.     /* httpd file icons */
  1512.     il_icon_table[inum++] = il_hash("/mc-icons/menu.gif");
  1513.     il_icon_table[inum++] = IL_GOPHER_FOLDER;
  1514.     il_icon_table[inum++] = il_hash("/mc-icons/unknown.gif");  
  1515.     il_icon_table[inum++] = IL_GOPHER_UNKNOWN;
  1516.     il_icon_table[inum++] = il_hash("/mc-icons/text.gif");    
  1517.     il_icon_table[inum++] = IL_GOPHER_TEXT;
  1518.     il_icon_table[inum++] = il_hash("/mc-icons/image.gif"); 
  1519.     il_icon_table[inum++] = IL_GOPHER_IMAGE;
  1520.     il_icon_table[inum++] = il_hash("/mc-icons/sound.gif");     
  1521.     il_icon_table[inum++] = IL_GOPHER_SOUND;
  1522.     il_icon_table[inum++] = il_hash("/mc-icons/movie.gif");     
  1523.     il_icon_table[inum++] = IL_GOPHER_MOVIE;
  1524.     il_icon_table[inum++] = il_hash("/mc-icons/binary.gif"); 
  1525.     il_icon_table[inum++] = IL_GOPHER_BINARY;
  1526.  
  1527.     /* Duplicate httpd icons, but using new naming scheme. */
  1528.     il_icon_table[inum++] = il_hash("/ns-icons/menu.gif");
  1529.     il_icon_table[inum++] = IL_GOPHER_FOLDER;
  1530.     il_icon_table[inum++] = il_hash("/ns-icons/unknown.gif");  
  1531.     il_icon_table[inum++] = IL_GOPHER_UNKNOWN;
  1532.     il_icon_table[inum++] = il_hash("/ns-icons/text.gif");    
  1533.     il_icon_table[inum++] = IL_GOPHER_TEXT;
  1534.     il_icon_table[inum++] = il_hash("/ns-icons/image.gif"); 
  1535.     il_icon_table[inum++] = IL_GOPHER_IMAGE;
  1536.     il_icon_table[inum++] = il_hash("/ns-icons/sound.gif");     
  1537.     il_icon_table[inum++] = IL_GOPHER_SOUND;
  1538.     il_icon_table[inum++] = il_hash("/ns-icons/movie.gif");     
  1539.     il_icon_table[inum++] = IL_GOPHER_MOVIE;
  1540.     il_icon_table[inum++] = il_hash("/ns-icons/binary.gif"); 
  1541.     il_icon_table[inum++] = IL_GOPHER_BINARY;
  1542.  
  1543.     /* ... and names for all the image icons */
  1544.     il_icon_table[inum++] = il_hash("internal-icon-delayed"); 
  1545.     il_icon_table[inum++] = IL_IMAGE_DELAYED;
  1546.     il_icon_table[inum++] = il_hash("internal-icon-notfound"); 
  1547.     il_icon_table[inum++] = IL_IMAGE_NOT_FOUND;
  1548.     il_icon_table[inum++] = il_hash("internal-icon-baddata"); 
  1549.     il_icon_table[inum++] = IL_IMAGE_BAD_DATA;
  1550.     il_icon_table[inum++] = il_hash("internal-icon-insecure"); 
  1551.     il_icon_table[inum++] = IL_IMAGE_INSECURE;
  1552.     il_icon_table[inum++] = il_hash("internal-icon-embed"); 
  1553.     il_icon_table[inum++] = IL_IMAGE_EMBED;
  1554.  
  1555.     /* This belongs up in the `news icons' section */
  1556.     il_icon_table[inum++] = il_hash("internal-news-followup-and-reply");
  1557.     il_icon_table[inum++] = IL_NEWS_FOLLOWUP_AND_REPLY;
  1558.  
  1559.     /* editor icons. */
  1560.     il_icon_table[inum++] = il_hash("internal-edit-named-anchor");
  1561.     il_icon_table[inum++] = IL_EDIT_NAMED_ANCHOR;
  1562.     il_icon_table[inum++] = il_hash("internal-edit-form-element");
  1563.     il_icon_table[inum++] = IL_EDIT_FORM_ELEMENT;
  1564.     il_icon_table[inum++] = il_hash("internal-edit-unsupported-tag");
  1565.     il_icon_table[inum++] = IL_EDIT_UNSUPPORTED_TAG;
  1566.     il_icon_table[inum++] = il_hash("internal-edit-unsupported-end-tag");
  1567.     il_icon_table[inum++] = IL_EDIT_UNSUPPORTED_END_TAG;
  1568.     il_icon_table[inum++] = il_hash("internal-edit-java");
  1569.     il_icon_table[inum++] = IL_EDIT_JAVA;
  1570.     il_icon_table[inum++] = il_hash("internal-edit-PLUGIN");
  1571.     il_icon_table[inum++] = IL_EDIT_PLUGIN;
  1572.  
  1573.     /* Security Advisor and S/MIME icons */
  1574.     il_icon_table[inum++] = il_hash("internal-sa-signed");
  1575.     il_icon_table[inum++] = IL_SA_SIGNED;
  1576.     il_icon_table[inum++] = il_hash("internal-sa-encrypted");
  1577.     il_icon_table[inum++] = IL_SA_ENCRYPTED;
  1578.     il_icon_table[inum++] = il_hash("internal-sa-nonencrypted");
  1579.     il_icon_table[inum++] = IL_SA_NONENCRYPTED;
  1580.     il_icon_table[inum++] = il_hash("internal-sa-signed-bad");
  1581.     il_icon_table[inum++] = IL_SA_SIGNED_BAD;
  1582.     il_icon_table[inum++] = il_hash("internal-sa-encrypted-bad");
  1583.     il_icon_table[inum++] = IL_SA_ENCRYPTED_BAD;
  1584.     il_icon_table[inum++] = il_hash("internal-smime-attached");
  1585.     il_icon_table[inum++] = IL_SMIME_ATTACHED;
  1586.     il_icon_table[inum++] = il_hash("internal-smime-signed");
  1587.     il_icon_table[inum++] = IL_SMIME_SIGNED;
  1588.     il_icon_table[inum++] = il_hash("internal-smime-encrypted");
  1589.     il_icon_table[inum++] = IL_SMIME_ENCRYPTED;
  1590.     il_icon_table[inum++] = il_hash("internal-smime-encrypted-signed");
  1591.     il_icon_table[inum++] = IL_SMIME_ENC_SIGNED;
  1592.     il_icon_table[inum++] = il_hash("internal-smime-signed-bad");
  1593.     il_icon_table[inum++] = IL_SMIME_SIGNED_BAD;
  1594.     il_icon_table[inum++] = il_hash("internal-smime-encrypted-bad");
  1595.     il_icon_table[inum++] = IL_SMIME_ENCRYPTED_BAD;
  1596.     il_icon_table[inum++] = il_hash("internal-smime-encrypted-signed-bad");
  1597.     il_icon_table[inum++] = IL_SMIME_ENC_SIGNED_BAD;
  1598.  
  1599.     /* LibMsg Attachment Icon */
  1600.     il_icon_table[inum++] = il_hash("internal-attachment-icon");
  1601.     il_icon_table[inum++] = IL_MSG_ATTACH;
  1602.  
  1603.     XP_ASSERT(inum <= (sizeof(il_icon_table) / sizeof(il_icon_table[0])));
  1604. }
  1605.  
  1606.  
  1607. static uint32
  1608. il_internal_image(const char *image_url)
  1609. {
  1610.     int i;
  1611.     uint32 hash = il_hash(image_url);
  1612.     if (il_icon_table[0]==0)
  1613.         il_setup_icon_table();
  1614.  
  1615.     for (i=0; i< (sizeof(il_icon_table) / sizeof(il_icon_table[0])); i++)
  1616.     {
  1617.         if (il_icon_table[i<<1] == hash)
  1618.         {
  1619.             return il_icon_table[(i<<1)+1];
  1620.         }
  1621.     }
  1622.     return 0;
  1623. }
  1624.  
  1625.  
  1626. PRBool
  1627. IL_PreferredStream(IL_URL *urls)
  1628. {
  1629.     il_container *ic = 0;
  1630.     IL_ImageReq *image_req;
  1631.  
  1632.     XP_ASSERT(urls);
  1633.     if (urls) {
  1634.         /* xxx this MUST be an image stream */
  1635.         ic = (il_container*)urls->fe_data;        
  1636.  
  1637.         XP_ASSERT(ic);
  1638.         if (ic) {
  1639.             /*
  1640.              * It could be that layout aborted image loading by
  1641.              * calling IL_FreeImage before the netlib finished
  1642.              * transferring data.  Don't do anything.
  1643.              */
  1644.             if (ic->state == IC_ABORT_PENDING)
  1645.                 return FALSE;
  1646.  
  1647.             /* discover if layout is blocked on this image */
  1648.             for (image_req = ic->clients; image_req;
  1649.                  image_req = image_req->next) {
  1650. #ifndef M12N                    /* XXXM12N Fixme.  Observer for layout?
  1651.                                    Query mechanism for FE? */
  1652.                 if ((LO_BlockedOnImage(c->cx,
  1653.                                        (LO_ImageStruct*)c->client) == TRUE) ||
  1654.                     FE_ImageOnScreen(c->cx, (LO_ImageStruct*)c->client) )
  1655. #endif /* M12N */
  1656.                 return TRUE;
  1657.             }
  1658.         }
  1659.     }
  1660.     return FALSE;
  1661. }
  1662.  
  1663. IL_ImageReq *
  1664. IL_GetImage(const char* image_url,
  1665.             IL_GroupContext *img_cx,
  1666.             XP_ObserverList obs_list,
  1667.             NI_IRGB *background_color,
  1668.             uint32 req_width, uint32 req_height,
  1669.             uint32 flags,
  1670.             IL_NetContext *net_cx)
  1671. {
  1672.     NET_ReloadMethod cache_reload_policy = net_cx->cache_reload_policy;
  1673.  
  1674.    IL_URL *urls = NULL;
  1675.  
  1676.     IL_ImageReq *image_req;
  1677.     il_container *ic = NULL;
  1678.     int req_depth = img_cx->color_space->pixmap_depth;
  1679.     int err;
  1680.     int is_internal_external_reconnect = FALSE;
  1681.     int is_view_image;
  1682.  
  1683.     /* Create a new instance for this image request. */
  1684.     image_req = XP_NEW_ZAP(IL_ImageReq);
  1685.     if (!image_req)
  1686.         return NULL;
  1687.     image_req->img_cx = img_cx;
  1688.     /*
  1689.      * Remember the net context for this image request, It is possible that
  1690.      * the lifetime of the image container's net context will not be as
  1691.      * long as that of the image request itself (for example if the image
  1692.      * request for which the container was originally created happens to
  1693.      * be destroyed,) in which case we may need to give the container a
  1694.      * handle on this backup net context.
  1695.      */
  1696.     image_req->net_cx = IL_CloneDummyNetContext(net_cx);
  1697.     if (!image_req->net_cx) {
  1698.         XP_FREE(image_req);
  1699.         return NULL;
  1700.     }
  1701.     image_req->obs_list = obs_list;
  1702.     XP_SetObserverListObservable(obs_list, (void *)image_req);
  1703.     
  1704.     ILTRACE(1, ("il: IL_GetImage, url=%s\n", image_url));
  1705.  
  1706.     if (!image_url)
  1707.     {
  1708.         ILTRACE(0,("il: no url, sending delayed icon"));
  1709.         il_icon_notify(image_req, IL_IMAGE_DELAYED, IL_ERROR_NO_DATA);
  1710.         return image_req;
  1711.     }
  1712.  
  1713.     /* Check for any special internal-use URLs */
  1714.     if (*image_url == 'i'                  ||
  1715.         !XP_STRNCMP(image_url, "/mc-", 4)  ||
  1716.         !XP_STRNCMP(image_url, "/ns-", 4))
  1717.     {
  1718.         uint32 icon;
  1719.  
  1720.         /* A built-in icon ? */
  1721.         icon = il_internal_image(image_url);
  1722.         if (icon)
  1723.         {
  1724.             ILTRACE(4,("il: internal icon %d", icon));
  1725.  
  1726.             /* XXXM12N In response to this notification, layout should set
  1727.                lo_image->image_attr->attrmask |= LO_ATTR_INTERNAL_IMAGE; */
  1728.             il_icon_notify(image_req, icon, IL_INTERNAL_IMAGE);
  1729.  
  1730.             return image_req;
  1731.         }
  1732.  
  1733.         /* Image viewer URLs look like "internal-external-reconnect:REALURL.gif"
  1734.          * Strip off the prefix to get the real URL name.
  1735.          */
  1736.         if (!XP_STRNCMP(image_url, "internal-external-reconnect:", 28)) {
  1737.             image_url += 28;
  1738.             is_internal_external_reconnect = TRUE;
  1739.         }
  1740.     }
  1741.  
  1742.     ic = il_get_container(img_cx, cache_reload_policy, image_url,
  1743.                           background_color, img_cx->dither_mode, req_depth,
  1744.                           req_width, req_height);
  1745.     if (!ic)
  1746.     {
  1747.         ILTRACE(0,("il: MEM il_container"));
  1748.         il_icon_notify(image_req, IL_IMAGE_DELAYED, IL_ERROR_INTERNAL);
  1749.         if (is_internal_external_reconnect)
  1750.             il_abort_reconnect();
  1751.         return image_req;
  1752.     }
  1753.      
  1754.     /* Give the client a handle into the imagelib world. */
  1755.     image_req->ic = ic;
  1756.  
  1757.     /* Is this a call to the image viewer ? */
  1758. #ifndef M12N /* XXXM12N fix me. */
  1759.     is_view_image = is_internal_external_reconnect &&
  1760.         (cx->type != MWContextNews) && (cx->type != MWContextMail);
  1761. #else
  1762.     is_view_image = is_internal_external_reconnect;
  1763. #endif /* M12N */
  1764.  
  1765.     if (!il_add_client(img_cx, ic, image_req, is_view_image))
  1766.     {
  1767.         il_icon_notify(image_req, IL_IMAGE_DELAYED, IL_ERROR_INTERNAL);
  1768.         if (is_internal_external_reconnect)
  1769.             il_abort_reconnect();
  1770.         return image_req;
  1771.     }
  1772.  
  1773.     /* If the image is already in memory ... */
  1774.     if (ic->state != IC_VIRGIN) {
  1775.         switch (ic->state) {
  1776.         case IC_BAD:
  1777.             il_icon_notify(image_req, IL_IMAGE_BAD_DATA,
  1778.                            IL_ERROR_IMAGE_DATA_ILLEGAL);
  1779.             break;
  1780.  
  1781.         case IC_MISSING:
  1782.             il_icon_notify(image_req, IL_IMAGE_NOT_FOUND, IL_ERROR_NO_DATA);
  1783.             XP_ASSERT(! is_internal_external_reconnect);
  1784.             break;
  1785.  
  1786.         case IC_INCOMPLETE:
  1787.             il_icon_notify(image_req, IL_IMAGE_DELAYED,
  1788.                            IL_ERROR_IMAGE_DATA_TRUNCATED);
  1789.             break;
  1790.  
  1791.         case IC_SIZED:
  1792.         case IC_MULTI:
  1793.             /* This is a cached image that hasn't completed decoding. */
  1794.             il_cache_return_notify(image_req);
  1795.             break;
  1796.  
  1797.         case IC_COMPLETE:
  1798. #ifndef M12N
  1799.             /* M12N Fix custom colormaps. */
  1800.             il_set_color_palette(cx, ic);
  1801. #else
  1802. #endif /* M12N */
  1803.             /* This is a cached image that has already completed decoding. */
  1804.             il_cache_return_notify(image_req);
  1805.  
  1806.             break;
  1807.  
  1808.         case IC_START:
  1809.         case IC_STREAM:
  1810.         case IC_ABORT_PENDING:
  1811.         case IC_NOCACHE:
  1812.             break;
  1813.             
  1814.         default:
  1815.             XP_ASSERT(0);
  1816.             IL_DestroyDummyNetContext(image_req->net_cx);
  1817.             XP_FREE(image_req);
  1818.             return NULL;
  1819.         }
  1820.  
  1821.         if (is_internal_external_reconnect) {
  1822.             /* Since we found the image in the cache, we don't
  1823.              * need any of the data now streaming into the image viewer.
  1824.              */
  1825.             il_abort_reconnect();
  1826.         }
  1827.  
  1828.         /* NOCACHE falls through to be tried again */
  1829.         if (ic->state != IC_NOCACHE)
  1830.              return image_req;
  1831.     }
  1832.  
  1833.     /* This is a virgin (never-used) image container. */
  1834.     else
  1835.     {
  1836.         ic->forced = FORCE_RELOAD(cache_reload_policy);
  1837.     }
  1838.  
  1839.     ic->state = IC_START;
  1840.     
  1841. #ifdef DEBUG
  1842.     ic->start_time = XP_TIME();
  1843. #endif
  1844.  
  1845.     /* Record the context that actually initiates and controls the transfer. */
  1846.     ic->net_cx = IL_CloneDummyNetContext(net_cx);
  1847.  
  1848.     if (is_internal_external_reconnect)
  1849.     {
  1850.         /* "Reconnect" to the stream feeding IL_ViewStream(), already
  1851.            created. */
  1852.         il_reconnect(ic);
  1853.         return image_req;
  1854.     }
  1855.         
  1856.     /* need to make a net request */
  1857.     ILTRACE(1,("il: net request for %s, %s", image_url,
  1858.                ic->forced ? "force" : ""));
  1859.  
  1860.     urls = NET_CreateURLStruct(image_url, cache_reload_policy);
  1861.  
  1862.     if (!urls)
  1863.     {
  1864. #ifndef M12N /* XXXM12N fix me. */
  1865.         /* xxx clean up previously allocated objects */
  1866.         return MK_OUT_OF_MEMORY;
  1867. #else
  1868.         IL_DestroyDummyNetContext(image_req->net_cx);
  1869.         XP_FREE(image_req);
  1870.         return NULL;
  1871. #endif /* M12N */
  1872.     }        
  1873.     
  1874.     /* Add the referer to the URL. */
  1875.     IL_AddReferer(ic->net_cx, urls);
  1876.     
  1877.     ic->is_looping = FALSE;
  1878.     ic->url = urls;
  1879.     /* Record the fact that we are calling NetLib to load a URL. */
  1880.     ic->is_url_loading = PR_TRUE;
  1881.  
  1882.     /* save away the container */
  1883.     urls->fe_data = (void *)ic;
  1884.     err = NET_GetURL(urls, (cache_reload_policy == NET_CACHE_ONLY_RELOAD) ?
  1885.                      FO_ONLY_FROM_CACHE_AND_INTERNAL_IMAGE :
  1886.                      FO_CACHE_AND_INTERNAL_IMAGE, 
  1887.                      ic->net_cx->cx,
  1888.                      (Net_GetUrlExitFunc*)&il_netgeturldone);
  1889.     return image_req;
  1890. }
  1891.  
  1892.  
  1893. void
  1894. IL_ReloadImages(IL_GroupContext *img_cx, IL_NetContext *net_cx)
  1895. {
  1896.     /* XXXM12N - Implement me. */
  1897.  
  1898. }
  1899.  
  1900.  
  1901. void
  1902. IL_SetDisplayMode(IL_GroupContext *img_cx, uint32 display_flags,
  1903.                   IL_DisplayData *display_data)
  1904. {
  1905.     if (display_flags & IL_DISPLAY_CONTEXT)
  1906.         img_cx->dpy_cx = display_data->display_context;
  1907.     
  1908.     if (display_flags & IL_DISPLAY_TYPE)
  1909.         img_cx->display_type = display_data->display_type;
  1910.  
  1911.     if (display_flags & IL_COLOR_SPACE) {
  1912.         if (img_cx->color_space)
  1913.             IL_ReleaseColorSpace(img_cx->color_space);
  1914.         img_cx->color_space = display_data->color_space;
  1915.         IL_AddRefToColorSpace(img_cx->color_space);
  1916.     }
  1917.  
  1918.     if (display_flags & IL_PROGRESSIVE_DISPLAY)
  1919.         img_cx->progressive_display = display_data->progressive_display;
  1920.  
  1921.     if (display_flags & IL_DITHER_MODE)
  1922.         if (img_cx->color_space && img_cx->color_space->pixmap_depth == 1) {
  1923.             /* Dithering always on in monochrome mode. */
  1924.             img_cx->dither_mode = IL_Dither;
  1925.         }
  1926.         else {
  1927.             img_cx->dither_mode = display_data->dither_mode;
  1928.         }
  1929. }
  1930.  
  1931.  
  1932.  
  1933. /* Interrupts all images loading in a context.  Specifically, stops all
  1934.    looping image animations. */
  1935. void
  1936. IL_InterruptContext(IL_GroupContext *img_cx)
  1937. {
  1938.     il_container *ic;
  1939.     IL_ImageReq *image_req;
  1940.     il_container_list *ic_list;
  1941.  
  1942.     if (!img_cx)
  1943.         return;
  1944.  
  1945.     /* Mark all clients in this context as interrupted. */
  1946.     for (ic_list = img_cx->container_list; ic_list; ic_list = ic_list->next) {
  1947.         ic = ic_list->ic;
  1948.         for (image_req = ic->clients; image_req; image_req = image_req->next) {
  1949.             if (image_req->img_cx == img_cx) {
  1950.                 image_req->stopped = TRUE;
  1951.             }
  1952.         }
  1953.     }
  1954. }
  1955.  
  1956. /* Has the user aborted the image load ? */
  1957. PRBool
  1958. il_image_stopped(il_container *ic)
  1959. {
  1960.     IL_ImageReq *image_req;
  1961.     for (image_req = ic->clients; image_req; image_req = image_req->next) {
  1962.         if (!image_req->stopped)
  1963.             return FALSE;
  1964.     }
  1965.  
  1966.     /* All clients must be stopped for image container to become dormant. */
  1967.     return TRUE;
  1968. }
  1969.  
  1970.  
  1971. /* Create an IL_GroupContext, which represents an aggregation of state
  1972.    for one or more images and which contains an interface to access
  1973.    external service functions and callbacks.  IL_NewGroupContext will use
  1974.    the IMGCBIF_AddRef callback to increment the reference count for the
  1975.    interface.
  1976.  
  1977.    The dpy_cx argument is opaque to the image library and is passed back to
  1978.    all of the callbacks in the IMGCBIF interface. */
  1979. IL_GroupContext*
  1980. IL_NewGroupContext(void *dpy_cx, IMGCBIF *img_cb)
  1981. {
  1982.     IL_GroupContext *img_cx;
  1983.     
  1984.     if (!img_cb)
  1985.         return NULL;
  1986.  
  1987.     img_cx = (IL_GroupContext*)XP_NEW_ZAP(IL_GroupContext);
  1988.     if (!img_cx)
  1989.         return NULL;
  1990.  
  1991.     img_cx->dpy_cx = dpy_cx;
  1992.     img_cx->img_cb = img_cb;
  1993.  
  1994.     img_cx->progressive_display = TRUE;
  1995.  
  1996.     /* Create an observer list for the image context. */
  1997.     if (XP_NewObserverList((void *)img_cx, &img_cx->obs_list)
  1998.         == MK_OUT_OF_MEMORY) {
  1999.         XP_FREE(img_cx);
  2000.         return NULL;
  2001.     }
  2002.  
  2003.     /* Add the context to the global image context list. */
  2004.     img_cx->next = il_global_img_cx_list;
  2005.     il_global_img_cx_list = img_cx;
  2006.  
  2007.     return img_cx;
  2008. }
  2009.  
  2010.  
  2011. /* Free an image context.  IL_DestroyGroupContext will make a call
  2012.    to the IMGCBIF_Release callback function of the JMC interface prior to
  2013.    releasing the IL_GroupContext structure.  The IMGCBIF_Release callback
  2014.    is expected to decrement the reference count for the IMGCBIF interface,
  2015.    and to free the callback vtable and the interface structure if the
  2016.    reference count is zero. */
  2017. void
  2018. IL_DestroyGroupContext(IL_GroupContext *img_cx)
  2019. {
  2020.     if (img_cx) {
  2021.         /* Remove ourself from the global image context list. */
  2022.         if (img_cx == il_global_img_cx_list) {
  2023.             il_global_img_cx_list = NULL;
  2024.         }
  2025.         else {
  2026.             IL_GroupContext *current, *next;
  2027.         
  2028.             for (current = il_global_img_cx_list; current; current = next) {
  2029.                 next = current->next;
  2030.                 if (next == img_cx) {
  2031.                     current->next = next->next;
  2032.                     break;
  2033.                 }
  2034.             }
  2035.         }
  2036.         
  2037.         /* Destroy any images remaining in the context. */
  2038.         if (img_cx->num_containers) {
  2039.             XP_ASSERT(img_cx->container_list != NULL);
  2040.             IL_DestroyImageGroup(img_cx);
  2041.         }
  2042.  
  2043.         /* Destroy the observer list. */
  2044.         XP_DisposeObserverList(img_cx->obs_list);
  2045.  
  2046.         /* Release the image context's reference to the colorspace. */
  2047.         if (img_cx->color_space) {
  2048.             IL_ReleaseColorSpace(img_cx->color_space);
  2049.             img_cx->color_space = NULL;
  2050.         }
  2051.  
  2052.         /* Release the JMC callback interface. */
  2053.         IMGCBIF_release(img_cx->img_cb, NULL); /* XXXM12N Need to use
  2054.                                                   exceptions. */
  2055.         XP_FREE(img_cx);
  2056.     }
  2057. }
  2058.  
  2059.  
  2060. /* Add an observer/closure pair to an image group context's observer list.
  2061.    Returns PR_TRUE if the observer is successfully registered. */
  2062. PRBool
  2063. IL_AddGroupObserver(IL_GroupContext *img_cx, XP_ObserverProc observer,
  2064.                     void *closure)
  2065. {
  2066.     return !XP_AddObserver(img_cx->obs_list, observer, closure);    
  2067. }
  2068.  
  2069.  
  2070. /* Remove an observer/closure pair from an image group context's observer
  2071.    list.  Returns PR_TRUE if successful. */
  2072. PRBool
  2073. IL_RemoveGroupObserver(IL_GroupContext *img_cx, XP_ObserverProc observer,
  2074.                     void *closure)
  2075. {
  2076.     return XP_RemoveObserver(img_cx->obs_list, observer, closure);
  2077. }
  2078.  
  2079.  
  2080. /* Display a rectangular portion of an image.  x and y refer to the top left
  2081.    corner of the image, measured in pixels, with respect to the document origin.
  2082.    x_offset and y_offset are measured in pixels, with the upper-left-hand corner
  2083.    of the pixmap as the origin, increasing left-to-right, top-to-bottom.
  2084.  
  2085.    If the width and height values would otherwise cause the sub-image
  2086.    to extend off the edge of the source image, the function should
  2087.    perform tiling of the source image.  This is used to draw document,
  2088.    layer and table cell backdrops.  (Note: it is assumed this case will
  2089.    apply only to images which do not require any scaling.)
  2090.  
  2091.    If at any time the image library determines that an image request cannot
  2092.    be fulfilled or that the image has been delayed, it will notify the client
  2093.    synchronously through the observer mechanism.  The client may then choose to
  2094.    request that an icon be drawn instead by making a call to IL_DisplayIcon. */
  2095. void
  2096. IL_DisplaySubImage(IL_ImageReq *image_req, int x, int y, int x_offset,
  2097.                    int y_offset, int width, int height)
  2098. {
  2099.     IL_GroupContext *img_cx;
  2100.     void *dpy_cx;
  2101.     il_container *ic;
  2102.     IL_Rect *displayable_rect;
  2103.     NI_PixmapHeader *img_header;
  2104.     
  2105.     /* If we were passed a NULL image handle, don't do anything. */
  2106.     if (!image_req)
  2107.         return;
  2108.  
  2109.     /* Determine the image context and the display context for this display
  2110.        operation. */
  2111.     img_cx = image_req->img_cx;
  2112.     dpy_cx = img_cx->dpy_cx;
  2113.     if (!dpy_cx)
  2114.         return;
  2115.     
  2116.     /* Determine the image container. */
  2117.     ic = image_req->ic;
  2118.     
  2119.     if (!ic )
  2120.     return;
  2121.  
  2122.     /* Perform the drawing operation, but only do so for displayable areas
  2123.        of the image pixmap. */
  2124.     displayable_rect = &ic->displayable_rect;
  2125.     img_header = &ic->image->header;
  2126.         
  2127.     if (displayable_rect->width < img_header->width ||
  2128.         displayable_rect->height < img_header->height) {
  2129.         /* The image pixmap is only partially displayable (since the image
  2130.            is only partially decoded,) so draw the intersection of the
  2131.            requested area with the displayable area.  The client will
  2132.            receive observer notification as the pixmap is updated, and it
  2133.            is expected to call the image library again to display the
  2134.            updated area.  Note that tiling operations are not performed
  2135.            unless the entire image pixmap is displayable. */
  2136.         int32 display_left, display_top, display_bottom, display_right;
  2137.         int32 display_width, display_height;
  2138.             
  2139.         /* Determine the intersection of the requested area and the
  2140.            displayable area, in pixmap coordinates. */
  2141.         display_left = MAX(x_offset, displayable_rect->x_origin);
  2142.         display_top = MAX(y_offset, displayable_rect->y_origin);
  2143.         display_right = MIN(x_offset + width, displayable_rect->x_origin
  2144.                             + displayable_rect->width);
  2145.         display_bottom = MIN(y_offset + height, displayable_rect->y_origin
  2146.                              + displayable_rect->height);
  2147.         display_width = display_right - display_left;
  2148.         display_height = display_bottom - display_top;
  2149.         
  2150.         /* Draw the intersection of the requested area and the displayable
  2151.            area. */
  2152.         if ((display_width > 0) && (display_height > 0))
  2153.             IMGCBIF_DisplayPixmap(img_cx->img_cb, dpy_cx, ic->image, ic->mask,
  2154.                                   x, y, display_left, display_top,
  2155.                                   display_width, display_height);
  2156.     }
  2157.     else {
  2158.         /* The entire image pixmap is displayable, (although this does not
  2159.            necessarily mean that the image has finished decoding,) so draw
  2160.            the entire area requested.  In the event that the requested
  2161.            area extends beyond the bounds of the image pixmap, tiling will
  2162.            be performed. */
  2163.         if (width && height)
  2164.             IMGCBIF_DisplayPixmap(img_cx->img_cb, dpy_cx, ic->image, ic->mask,
  2165.                                   x, y, x_offset, y_offset, width, height);
  2166.     }
  2167. }
  2168.  
  2169.  
  2170. /* Display an icon.  x and y refer to the top left corner of the icon, measured
  2171.    in pixels, with respect to the document origin.  x_offset, y_offset, width
  2172.    and height specify a clipping rectangle for this drawing request. */
  2173. void
  2174. IL_DisplayIcon(IL_GroupContext *img_cx, int icon_number, int x, int y)
  2175. {
  2176.     /* Check for a NULL image context. */
  2177.     if (!img_cx)
  2178.         return;
  2179.  
  2180.     /* Ask the Front End to display the icon. */
  2181.     IMGCBIF_DisplayIcon(img_cx->img_cb, img_cx->dpy_cx, x, y, icon_number);
  2182. }
  2183.  
  2184.  
  2185. /* Return the dimensions of an icon. */
  2186. void
  2187. IL_GetIconDimensions(IL_GroupContext *img_cx, int icon_number, int *width,
  2188.                      int *height)
  2189. {
  2190.     /* Check for a NULL image context. */
  2191.     if (!img_cx)
  2192.         return;
  2193.  
  2194.     /* Obtain the dimensions of the icon. */
  2195.     IMGCBIF_GetIconDimensions(img_cx->img_cb, img_cx->dpy_cx, width, height,
  2196.                               icon_number);
  2197. }
  2198.  
  2199.  
  2200. /* Return the image IL_Pixmap associated with an image request. */
  2201. IL_Pixmap *
  2202. IL_GetImagePixmap(IL_ImageReq *image_req)
  2203. {
  2204.     if (image_req && image_req->ic)
  2205.         return image_req->ic->image;
  2206.     else
  2207.         return NULL;
  2208. }
  2209.  
  2210.  
  2211. /* Return the mask IL_Pixmap associated with an image request. */
  2212. IL_Pixmap *
  2213. IL_GetMaskPixmap(IL_ImageReq *image_req)
  2214. {
  2215.     if (image_req && image_req->ic)
  2216.         return image_req->ic->mask;
  2217.     else
  2218.         return NULL;
  2219. }
  2220.  
  2221.  
  2222. /* Return the natural dimensions of the image.  Returns 0,0 if the dimensions
  2223.    are unknown. */
  2224. void
  2225. IL_GetNaturalDimensions(IL_ImageReq *image_req, int *width, int *height)
  2226. {
  2227.     NI_PixmapHeader *src_header;
  2228.  
  2229.     if (width)    
  2230.         *width = 0;
  2231.     if (height)
  2232.         *height = 0;
  2233.  
  2234.     if (!image_req || !image_req->ic)
  2235.         return;
  2236.  
  2237.     src_header = image_req->ic->src_header;
  2238.  
  2239.     if (src_header) {
  2240.         if (width)
  2241.             *width = src_header->width;
  2242.         if (height)
  2243.             *height = src_header->height;
  2244.     }
  2245. }
  2246.  
  2247.  
  2248. #ifndef M12N                    /* XXXM12N Get rid of these functions */
  2249. void
  2250. IL_DisableScaling(MWContext *cx)
  2251. {
  2252.     il_process *ip;
  2253.  
  2254.     if (!cx)
  2255.         return;
  2256.  
  2257.     if (!(ip=cx->imageProcess))
  2258.         return;
  2259.  
  2260.     ip->dontscale = 1;
  2261. }
  2262.  
  2263.  
  2264. void
  2265. IL_DisableLowSrc(MWContext *cx)
  2266. {
  2267.     il_process *ip;
  2268.  
  2269.     if (!cx)
  2270.         return;
  2271.  
  2272.     if (!(ip=cx->imageProcess))
  2273.         return;
  2274.  
  2275.     ip->nolowsrc = 1;
  2276. }
  2277. #endif /* M12N */
  2278.  
  2279. #ifdef PROFILE
  2280. #pragma profile off
  2281. #endif
  2282.  
  2283.  
  2284.