Milán Major

netscape-revival

1 branch
Code

lib/libimg/scale.c

/* -*- Mode: C; tab-width: 4 -*-
   scale.c --- Controls rendering of scan lines to screen, including scaling
               and transparency
   Copyright © 1995 Netscape Communications Corporation, all rights reserved.
*/

#include "xp.h"                 /* Cross-platform definitions */

#include "il.h"                 /* Image library external API */
#include "if.h"                 /* Image library internal declarations */

#ifdef DEBUG_jwz
extern XP_Bool IL_AnimationLoopsEnabled;
extern XP_Bool IL_AnimationsEnabled;
#endif /* DEBUG_jwz */


/* MAX is almost universal, but be paranoid and use our definition */
#ifdef MAX
#    undef MAX
#endif
#define MAX(x, y) ((x) > (y) ? (x) : (y))

/* Approximate size of pixel data chunks sent to the FE for display */
#define OUTPUT_CHUNK_SIZE        15000

/* Delay from decode to display of first scanline, in milliseconds. */
#define ROW_OUTPUT_INITIAL_DELAY    50

/* Delays between subsequent sets of scanlines */
#define ROW_OUTPUT_DELAY           300

static void
il_timeout_callback(void *closure)
{
    int delay;
	il_container *ic = (il_container *)closure;
    IL_Image *im = ic->image;

    ic->row_output_timeout = NULL;
    if (ic->state == IC_ABORT_PENDING)
        return;

    /*
     * Don't schedule any more timeouts once we start decoding the
     * second image in a multipart image sequence.  Instead display
     * will take place when the entire image is decoded.
     */
    if (ic->multi && ((uint32)im->height * im->width < 100000)) {
        return;
    }
    
    il_flush_image_data(ic, ilPartialData);

    delay = (ic->pass > 1) ? 2 * ROW_OUTPUT_DELAY : ROW_OUTPUT_DELAY;

    ic->row_output_timeout = FE_SetTimeout(il_timeout_callback, ic, delay);
}

/*-----------------------------------------------------------------------------
 * Display a specified number of rows of pixels for the purpose of
 * progressive image display.  The data is accumulated without forwarding
 * it to the front-end for display until either the row-output timeout
 * fires or the image is fully decoded.
 *---------------------------------------------------------------------------*/
void
il_partial(
    il_container *ic,   /* The image container */
    int row,            /* Starting row; zero is top row of image */
    int row_count,      /* Number of rows to output, including starting row */
    int pass)           /* Zero, unless  interlaced GIF,
                           in which case ranges 1-4, or progressive JPEG,
                           in which case ranges from 1-n. */
{	
	IL_Image *im = ic->image;

    if (!ic->new_data_for_fe) {
        ic->update_start_row = row;
        ic->update_end_row = row + row_count - 1;
        ic->new_data_for_fe = TRUE;
    } else {
        if (row < ic->update_start_row)
            ic->update_start_row = row;
        
        if ((row + row_count - 1) > ic->update_end_row)
            ic->update_end_row = row + row_count - 1;
    }

    ic->pass = pass;

    if (ic->ip->progressive_display)
    {
#ifdef XP_WIN
        /* The last pass of an image is displayed with less delay. Bug #25985 */
        if (!ic->multi && (pass == IL_FINAL_PASS)) {
#else
        /* The first and last pass of an image are displayed with less delay. */
        if (!ic->multi && ((pass <= 1) || (pass == IL_FINAL_PASS))) {
#endif
            int num_rows = ic->update_end_row - ic->update_start_row + 1;
            if (num_rows * im->widthBytes > OUTPUT_CHUNK_SIZE)
                il_flush_image_data(ic, ilPartialData);
        }
        
        /* Set a timer that will actually display the image data. */
        if (!ic->row_output_timeout)
            ic->row_output_timeout =
                FE_SetTimeout(il_timeout_callback, ic,ROW_OUTPUT_INITIAL_DELAY);
    }
}

/* Draw the progress-bar "thermometer" at the bottom of the screen
 * to estimate the percentage loaded of the image.
 */
void
il_update_thermometer(il_container *ic)
{
    uint percent_done;
    uint32 content_length;
    static uint last_percent_done = 0;

	IL_Image *im = ic->image;
    int row = ic->update_end_row;

    /* No more progress bars for GIF animations after initial load. */
    if (ic->is_looping)
        return;

    /* Calculate the percentage of image decoded (not displayed) */
    if ((content_length = ic->content_length)) {
        percent_done =
            (uint32)100 * ic->bytes_consumed / content_length;

    /* Some protocols, e.g. gopher, don't support content-length, so
     * show the percentage of the image displayed instead
     */
    } else {
        /* Could be zero if before il_size() is called. */
        if (im->height == 0)
            return;

        /* Interlaced GIFs are weird */
        if (ic->type == IL_GIF) {
            percent_done = il_gif_compute_percentage_complete(row, ic);
        }
        else
        {
            /* This isn't correct for progressive JPEGs, but there's
             * really nothing we can do about that because the number
             * of scans in a progressive JPEG isn't known until the
             * whole file has been read.
             */
            percent_done = (uint)(row * (uint32)100 / im->height);

        }
    }
    
    if (percent_done != last_percent_done) {
        il_client *c;
        for (c = ic->clients; c; c = c->next)
        {
            if (c->is_view_image)
                FE_SetProgressBarPercent (c->cx, percent_done);
        }
        last_percent_done = percent_done;
    }
}

/*-----------------------------------------------------------------------------
 * Force the front-end to to display any lines in the image bitmap
 * that have been decoded, but haven't yet been sent to the screen.
 * (Progressively displayed images are normally displayed several
 * lines at a time for efficiency.  This routine flushes out the last
 * few undisplayed lines in the image.)
 *---------------------------------------------------------------------------*/
void
il_flush_image_data(
    il_container *ic,          /* The image container */
    IL_ImageStatus new_status) /* Either ilPartialData or ilComplete,
                                  depending on whether more pixel rows
                                  will be added to the image or
                                  redrawn */
{
	il_client *c;
	IL_Image *im = ic->image;
    int row, start_row, end_row, row_interval;
    int first_client = TRUE;

    /* If we never called il_size(), we have no data for the FE. */
    if (!im->bits)
        return;

    start_row = ic->update_start_row;
    end_row = ic->update_end_row;
    row_interval = (2 * OUTPUT_CHUNK_SIZE) / im->widthBytes;

    for(c=ic->clients; c; c = c->next) {

        /* The user aborted the image loading.
           Don't display any more image data */
        if (c->stopped)
            continue;
        
        row = start_row;

#ifdef XP_UNIX
        /* If the amount of image data becomes really large, break it
         * up into chunks to BLT out to the screen.  Otherwise, there
         * can be a noticeable delay as the FE processes a large image.
         * (In the case of the XFE, it can take a long time to send
         * it to the server.)
         */
        for (;row < (end_row - row_interval);
             row += row_interval) {
            im->validHeight = MAX(im->validHeight, row + row_interval);
#ifdef DEBUG_jwz
            if (ic->multi == 0 || IL_AnimationsEnabled)
              /* if this is not the first frame, and animations are disabled,
                 then don't send any bits to the FE. */
#endif /* DEBUG_jwz */
              FE_ImageData(c->cx, ilPartialData, ic->image, c->client, row, row_interval, first_client);
        }
#endif /* XP_UNIX */

        /* Draw whatever is leftover after sending the chunks */
        im->validHeight = MAX(im->validHeight, end_row + 1);
#ifdef DEBUG_jwz
            if (ic->multi == 0 || IL_AnimationsEnabled)
              /* if this is not the first frame, and animations are disabled,
                 then don't send any bits to the FE. */
#endif /* DEBUG_jwz */
              FE_ImageData(c->cx, new_status, ic->image, c->client, row, end_row - row + 1, first_client);
        first_client = FALSE;
    }
        
    ic->new_data_for_fe = FALSE;
    ic->update_end_row = ic->update_start_row = 0;

    il_update_thermometer(ic);
}

/* Copy a packed RGB triple */
#define COPY_RGB(src, dest)                                                   \
    {dest[0] = src[0]; dest[1] = src[1]; dest[2] = src[2];}


/*-----------------------------------------------------------------------------
 * Scale a row of packed RGB pixels using the Bresenham algorithm.
 * Output is also packed RGB pixels.
 *---------------------------------------------------------------------------*/
static void
il_scale_RGB_row(
    uint8 XP_HUGE *src, /* Source row of packed RGB pixels */
    int src_len,        /* Number of pixels in source row  */
    uint8 *dest,        /* Destination, packed RGB pixels */
    int dest_len)       /* Length of target row, in pixels */
{
    uint8 *dest_end = dest + (3 * dest_len);
    int n = 0;
    
    XP_ASSERT(dest);
    XP_ASSERT(src_len != dest_len);

    /* Two cases */

    /* Scaling down ? ... */
    if (src_len > dest_len)
    {
        while (dest < dest_end) {
            COPY_RGB(src, dest);
            dest += 3;
            n += src_len;
            while (n >= dest_len) {
                src += 3;
                n -= dest_len;
            }
        }
    }
    else
    /* Scaling up. */
    {
        while (dest < dest_end) {
            n += dest_len;
            while (n >= src_len) {
                COPY_RGB(src, dest);
                dest += 3;
                n -= src_len;
            }
            src += 3;
        }
    }
}

/*-----------------------------------------------------------------------------
 * Scale a row of single-byte pixels using the Bresenham algorithm.
 * Output is also single-byte pixels.
 *---------------------------------------------------------------------------*/
static void
il_scale_CI_row(
    uint8 XP_HUGE *src, /* Source row of packed RGB pixels */
    int src_len,        /* Number of pixels in source row  */
    uint8 *dest,        /* Destination, packed RGB pixels */
    int dest_len,       /* Length of target row, in pixels */
    uint8* indirect_map,/* image-to-FE color mapping */
    int transparent_pixel_color)
{
    int src_pixel, mapped_src_pixel;
    uint8 *dest_end = dest + dest_len;
    int n = 0;
    
    XP_ASSERT(dest);
    XP_ASSERT(src_len != dest_len);

    /* Two cases */

    /* Scaling down ? ... */
    if (src_len > dest_len)
    {
        while (dest < dest_end) {
            if (*src != transparent_pixel_color)
                *dest = indirect_map[*src];
            dest ++;
            n += src_len;
            while (n >= dest_len) {
                src ++;
                n -= dest_len;
            }
        }
    }
    else
    /* Scaling up. */
    {
        while (dest < dest_end) {
            n += dest_len;
            src_pixel = *src;
            mapped_src_pixel = indirect_map[src_pixel];
            while (n >= src_len) {
                if (src_pixel != transparent_pixel_color)
                    *dest = mapped_src_pixel;
                dest ++;
                n -= src_len;
            }
            src++;
        }
    }
}

/* Convert row coordinate from image space to display space. */
#define SCALE_YCOORD(ic, y)                                                   \
    ((int)((uint32)(y) * (ic)->image->height / (ic)->unscaled_height))

#define SCALE_XCOORD(ic, x)                                                   \
    ((int)((uint32)(x) * (ic)->image->width / (ic)->unscaled_width))

/*-----------------------------------------------------------------------------
 * Create a transparency mask bitmap.  Perform horizontal scaling if
 * requested using a Bresenham algorithm. Accumulate the mask in
 * 32-bit chunks for efficiency.
 *---------------------------------------------------------------------------*/
static void
il_generate_scaled_transparency_mask(
    IL_Image *im,        /* The image */
    uint8 *src,          /* Row of pixels, 8-bit pseudocolor data */
    int src_len,         /* Number of pixels in source row */
    int x_offset,        /* Destination offset from left edge */
    uint8 XP_HUGE *mask, /* Output pointer, left-justified bitmask */
    int mask_len,        /* Number of pixels in output row */
    il_draw_mode draw_mode)     /* ilOverlay or ilErase */
{
    int not_transparent, n = 0;
    int image_transparent_color_index =
        im->transparent ? im->transparent->index : -1;
    int output_bits_remaining = mask_len;

    uint32 bgmask32 = 0;        /* 32-bit temporary mask accumulators */
    uint32 fgmask32 = 0;

    int mask_bit;               /* next bit to write in setmask32 */

    uint32 *m = ((uint32*)mask) + (x_offset >> 5);
    mask_bit = ~x_offset & 0x1f;

    XP_ASSERT(mask_len);

    /* Handle weird case in which we have a mask for a non-transparent
       image.  This can happen when we have a LOSRC that is a transparent
       GIF and a SRC that is a JPEG.  For now, we avoid crashing.  Later
       we should fix that case so it does the right thing and gets rid
       of the mask. */
    if (!src)
        return;

/* Add a bit to the row of mask bits.  Flush accumulator to memory if full. */
#define SHIFT_IMAGE_MASK(not_transparent_flag)								  \
    {																		  \
        fgmask32 |=  ((uint32)not_transparent_flag    ) << M32(mask_bit);     \
        bgmask32 |=  ((uint32)not_transparent_flag ^ 1) << M32(mask_bit);     \
																			  \
        /* Filled up 32-bit mask word.  Write it to memory. */				  \
        if (mask_bit-- == 0) {                                                \
            uint32 mtmp = *m;                                                 \
            mtmp |= fgmask32;                                                 \
         	if (draw_mode == ilErase)                                         \
                mtmp &= ~bgmask32;                                            \
            *m++ = mtmp;                                                      \
            mask_bit = 31;													  \
            bgmask32 = 0;                                                     \
            fgmask32 = 0;                                                     \
        }																	  \
        output_bits_remaining--;                                              \
    }

    /* Two cases */

    /* Scaling down ? (or source and dest same size) ... */
    if (src_len >= mask_len)
    {
        while (output_bits_remaining) {
            not_transparent = (*src != image_transparent_color_index);
            SHIFT_IMAGE_MASK(not_transparent);
            n += src_len;

            while (n >= mask_len) {
                src++;
                n -= mask_len;
            }
        }
    }
    else
    /* Scaling up */
    {
        while (output_bits_remaining) {
            n += mask_len;
            not_transparent = (*src != image_transparent_color_index);

            while (n >= src_len) {
                SHIFT_IMAGE_MASK(not_transparent);
                n -= src_len;
            }
            src++;
        }
    }
    
    /* End of scan line. Write out any remaining mask bits. */ 
    if (mask_bit < 31) {
        uint32 mtmp = *m;
        mtmp |= fgmask32;
        if (draw_mode == ilErase)
            mtmp &= ~bgmask32; 
        *m = mtmp; 
    }

#undef SHIFT_IMAGE_MASK    
}


/*-----------------------------------------------------------------------------
 * When color quantization (possibly accompanied by dithering) takes
 * place, the background pixels in a transparent image that overlays a
 * solid-color background, e.g. <BODY BGCOLOR=#c5c5c5>, will get
 * mapped to a color in the color-cube.  The real background color,
 * however, may not be one of these colors reserved for images.  This
 * routine serves to return transparent pixels to their background
 * color.  This routine must performing scaling because the source
 * pixels are in the image space and the target pixels are in the
 * display space.
 *---------------------------------------------------------------------------*/
static void
il_reset_background_pixels(
    il_container *ic,    /* The image container */
    uint8 *src,          /* Row of pixels, 8-bit pseudocolor data */
    int src_len,         /* Number of pixels in row */
    uint8 XP_HUGE *dest, /* Output pointer, 8-bit pseudocolor data */
    int dest_len)        /* Width of output pixel row */
{
    int is_transparent, n = 0;
    uint8 XP_HUGE *dest_end = dest + dest_len;
    int image_transparent_color_index = ic->image->transparent->index;
    int display_background_color_index = ic->ip->transparent.index;

    /* Two cases */

    /* Scaling down ? (or not scaling ?) ... */
    if (src_len >= dest_len) {
        while (dest < dest_end) {
            is_transparent = (*src == image_transparent_color_index);
            if (is_transparent)
                *dest = display_background_color_index;
            dest++;
            n += src_len;

            while (n >= dest_len) {
                src++;
                n -= dest_len;
            }
        }
    } else {
    /* Scaling up */
        while (dest < dest_end) {
            n += dest_len;
            is_transparent = (*src++ == image_transparent_color_index);
            
            if (is_transparent)
                while (n >= src_len) {
                    *dest++ = display_background_color_index;
                    n -= src_len;
                }
            else
                while (n >= src_len) {
                    dest++;
                    n -= src_len;
                }
        }
    }
}

static void
il_generate_byte_mask(
    il_container *ic,    /* The image container */
    uint8 *src,          /* Row of pixels, 8-bit pseudocolor data */
    int src_len,         /* Number of pixels in row */
    uint8 *dest,         /* Output pointer, 8-bit pseudocolor data */
    int dest_len)        /* Width of output pixel row */
{
    int is_transparent, n = 0;
    uint8 XP_HUGE *dest_end = dest + dest_len;
    int image_transparent_color_index = ic->image->transparent->index;

    /* Two cases */

    /* Scaling down ? (or not scaling ?) ... */
    if (src_len >= dest_len) {
        while (dest < dest_end) {
            is_transparent = (*src == image_transparent_color_index);
            *dest = is_transparent - 1;
            dest++;
            n += src_len;

            while (n >= dest_len) {
                src++;
                n -= dest_len;
            }
        }
    } else {
    /* Scaling up */
        while (dest < dest_end) {
            n += dest_len;
            is_transparent = (*src++ == image_transparent_color_index);
            
            if (is_transparent)
                while (n >= src_len) {
                    *dest++ = 0;
                    n -= src_len;
                }
            else
                while (n >= src_len) {
                    *dest++ = (uint8)-1;
                    n -= src_len;
                }
        }
    }
}

static void
il_overlay(uint8 *src, uint8 *dest, uint8 *byte_mask, int num_cols, int bytes_per_pixel)
{
    int i, col;
#if 0
    uint8 *s = src;
    uint8 *s_end = src + (num_cols * bytes_per_pixel);
#endif
    for (col = num_cols; col > 0; col--) {
        if (*byte_mask++) {
            for (i = bytes_per_pixel; i > 0; i--) {
                dest[i] = src[i];
            }
        }
        dest += bytes_per_pixel;
        src += bytes_per_pixel;
    }
}

static uint8 il_tmpbuf[MAX_IMAGE_WIDTH];
    
/*-----------------------------------------------------------------------------
 *  Emit a complete row of pixel data into the image.  This routine
 *  provides any necessary conversion to the display depth, optional dithering
 *  for pseudocolor displays, scaling and transparency, including mask
 *  generation, if necessary.  If sufficient data is accumulated, the screen
 *  image is updated, as well.
 *---------------------------------------------------------------------------*/
void
il_emit_row(
    il_container *ic,   /* The image container */
    uint8 *cbuf,        /* Color index data source, or NULL if source
                           is RGB data */
    uint8 *rgbbuf,      /* Packed RGB data or RGB workspace if <cbuf> != NULL */
    int x_offset,       /* First column to write data into */
    int len,            /* Width of source image, in pixels */
    int row,            /* Starting row of image */
    int dup_row_count,          /* Number of times to duplicate row */
    il_draw_mode draw_mode,     /* ilOverlay or ilErase */
    int pass)           /* Zero, unless  interlaced GIF,
                           in which case ranges 1-4, or progressive JPEG,
                           in which case ranges from 1-n. */
{
	IL_Image *im = ic->image;
	uint8 XP_HUGE *out;
    uint8 XP_HUGE *dp;
    uint8 XP_HUGE *mp;
	uint8 XP_HUGE *maskp = NULL;
    uint8 *byte_mask = NULL;
	uint8 XP_HUGE *srcbuf = rgbbuf;
    uint8 *p = cbuf;
    uint8 *pl = cbuf+len;
	int drow_start, drow_end, row_count, color_index, dup, do_dither;
    int dcolumn_start, dcolumn_end, column_count, offset, src_len, dest_len;

	XP_ASSERT(row >= 0);

	if(row >= ic->unscaled_height) {
		ILTRACE(2,("il: ignoring extra row (%d)", row));
		return;
	}

	/* Set first and last destination row in the image.  Assume no scaling. */
	drow_start = row;
    drow_end = row + dup_row_count - 1;
    dcolumn_start = x_offset;
    dcolumn_end = x_offset + len - 1;

    /* If scaling, convert vertical image coordinates to display space. */
    if (im->height != ic->unscaled_height) {
        int d = drow_start;
        int next_drow_start = SCALE_YCOORD(ic, drow_end + 1);
        drow_start = SCALE_YCOORD(ic, drow_start);

        /*
         * Don't emit a row of pixels that will be overwritten later.
         * (as may happen during when images are being reduced vertically).
         */
        if (drow_start == next_drow_start) {
            /*
             * Except that the bottom line of pixels can never be
             * overwritten by a subsequent line.
             */
            if (d != (ic->unscaled_height - 1))
                return;
            else
                drow_end = drow_start;
        } else {
            drow_end = next_drow_start - 1;
            if (drow_end >= im->height)
                drow_end = im->height - 1;
        }
    }

    /* If scaling, convert horizontal image coordinates to display space. */
    if (im->width != ic->unscaled_width) {
        int d = dcolumn_start;
        int next_dcolumn_start = SCALE_XCOORD(ic, dcolumn_end + 1);
        dcolumn_start = SCALE_XCOORD(ic, dcolumn_start);

        /*
         * Don't emit a column of pixels that will be overwritten later.
         * (as may happen during when images are being reduced vertically).
         */
        if (dcolumn_start == next_dcolumn_start) {
            /*
             * Except that the right column of pixels can never be
             * overwritten by a subsequent column.
             */
            if (d != (ic->unscaled_width - 1))
                return;
            else
                dcolumn_end = dcolumn_start;
        } else {
            dcolumn_end = next_dcolumn_start - 1;
            if (dcolumn_end >= im->width)
                dcolumn_end = im->width - 1;
        }
    }

    /* Number of pixel rows and columns to emit into framebuffer */
    row_count = drow_end - drow_start + 1;
    column_count = dcolumn_end - dcolumn_start + 1;

    /* If a transparent image appears over a background image ... */
    if (im->mask) {

        /* Must be 8-bit image: There are no transparent 24-bit images (yet!).
           Except that, due to a bug, we retain the mask from a transparent
           LOSRC GIF when the SRC is a JPEG. */
        /* XP_ASSERT(cbuf); */
        
#ifdef _USD
		maskp = (uint8 XP_HUGE *)im->mask +
            (im->height - drow_start - 1) * (uint32)im->maskWidthBytes;
#else
		maskp = (uint8 XP_HUGE *)im->mask + drow_start * im->maskWidthBytes;
#endif
        il_generate_scaled_transparency_mask(im, cbuf, (int)len,
                                             dcolumn_start,
                                             maskp, column_count,
                                             draw_mode);
	}

	if (!ic->converter) {
        int i;
        int image_transparent_color_index;
        uint8 XP_HUGE * dest;

        uint8 *indirect_map = ic->cs->current_indirect_map;
        
        if ((draw_mode == ilErase) || !im->transparent)
            image_transparent_color_index = -1; /* no transparency */
        else
            image_transparent_color_index = im->transparent->index;

		/* No converter, image is already rendered in pseudocolor. */
#ifdef _USD
		out = (uint8 XP_HUGE *)im->bits +
            (im->height - drow_start - 1) * (uint32)im->widthBytes;
#else
		out = (uint8 XP_HUGE *)im->bits + drow_start * im->widthBytes;
#endif

        dest = out + dcolumn_start;
        
        /* If horizontal scaling ... */
        if (len != column_count) {
            il_scale_CI_row(cbuf, len, dest, column_count,
                            indirect_map,
                            image_transparent_color_index);
        } else {

            /* Convert to FE's palette indices */
            for (i = 0; i < len; i++)
                if (cbuf[i] != image_transparent_color_index)
                    dest[i] = indirect_map[cbuf[i]];
        }

	} else {

        /* Generate the output row in RGB space, regardless of screen depth. */
		if (cbuf) {
			uint8 *r = rgbbuf;
			IL_RGB *map = im->map, *entry;

			if (! im->transparent) {
				/* Simple case: no transparency */
				while (p < pl) {
                    color_index = *p++;
					entry = map + color_index;
					r[0] = entry->red;
					r[1] = entry->green;
					r[2] = entry->blue;
                    r +=3 ;
				}
			} else {
				/*
                 * There are two kinds of transparency, depending on whether
                 * the image is overlaying:
                 *   1) a solid color background, or
                 *   2) another image
                 *
                 * The first case is easy.  We just substitute the background
                 * color for all the transparent pixels in the image.  No mask
                 * is necessary.  The second case requires that we generate a
                 * bit mask (see the code above).  It also seems to require that
                 * all the transparent pixels in the image be set to black.
                 * XXX - Why ?  Is this some platform-specific thing ? - fur
                 */
                int background_r, background_g, background_b;
				int image_transparent_color_index = im->transparent->index;

                background_r = background_g = background_b = 0;
				if (!im->mask) {
                    /* Solid background color */
					background_r = ic->ip->transparent.red;
					background_g = ic->ip->transparent.green;
					background_b = ic->ip->transparent.blue;
                }
                
                /* Remap transparent pixels */
                while (p < pl) {
                    color_index = *p++;
                    if (color_index == image_transparent_color_index) {
                        r[0] = background_r;
                        r[1] = background_g;
                        r[2] = background_b;
                        r += 3;
                    } else {
                        entry = map + color_index;
                        r[0] = entry->red;
                        r[1] = entry->green;
                        r[2] = entry->blue;
                        r += 3;
                    }
                }
			}
		}


		/* Now we are in RGB space. */

		/* Simple anamorphic scaling (in RGB space for now) */
        src_len = len;
        dest_len = column_count;
		if (src_len != dest_len) {
			uint8 XP_HUGE *src = rgbbuf;
			uint8 *dest = ic->scalerow;
			srcbuf = dest;

			/* Scale the pixel data (mask data already scaled) */
            il_scale_RGB_row(src, src_len, dest, dest_len);
		}

#ifdef _USD
		out = (uint8 XP_HUGE *)im->bits +
            (im->height - drow_start - 1) * (uint32)im->widthBytes;
#else
		out = (uint8 XP_HUGE *)im->bits + drow_start * im->widthBytes;
#endif

        if (im->transparent && (draw_mode == ilOverlay))
        {
            il_generate_byte_mask(ic, cbuf, len, il_tmpbuf, column_count);
            byte_mask = il_tmpbuf;
        }
        
        /*
         * Convert RGB to display depth.  If display is pseudocolor, this may
         * also color-quantize and dither.
         */
		(*ic->converter)(ic, byte_mask, srcbuf, dcolumn_start,
                         column_count, out);

        /*
         * Have to reset transparent pixels to background color
         * because color quantization may have mutated them.
         */
		if (im->transparent && (ic->cs->mode == ilCI) &&
            !im->mask && (draw_mode == ilErase))
            il_reset_background_pixels(ic, cbuf, len,
                                       out + dcolumn_start, column_count);
	}

	/*
     * We now have one row of pixels and, if required for transparency, a row
     * of mask data.  If the pixels in this row of the image cover span more
     * than one pixel vertically when displayed, the row needs to be
     * replicated in the framebuffer.  (This replication is necessary when
     * displaying interlaced GIFs and/or vertical scaling of any image type.)
     * Actually, pixel rows are not simply copied: Dithering may need to be
     * applied on a line-by-line basis.
     */
    dp = out;
    mp = maskp;
    dup = row_count - 1;
    offset = dcolumn_start * ic->image->bytesPerPixel;

    if (ic->image->depth == 1)
        do_dither = TRUE;
    else
        do_dither = ic->converter && (row_count <= 4) &&
            ((ic->dither_mode == ilDither) || (ic->type == IL_JPEG));

    while (dup--) {
#ifdef _USD
        dp -= im->widthBytes;
        mp -= im->maskWidthBytes;
#else
        dp += im->widthBytes;
        mp += im->maskWidthBytes;
#endif
        /* Is dithering being done (either mono or pseudocolor) ... ? */
        if (do_dither) {
            
            /* Dither / color-quantize */
            (*ic->converter)(ic, byte_mask, srcbuf, dcolumn_start,
                             column_count, dp);

            /*
             * Have to reset transparent pixels to background color
             * because color quantization may have mutated them.
             */
            if (im->transparent && (ic->cs->mode == ilCI) &&
                !im->mask && (draw_mode == ilErase))
                il_reset_background_pixels(ic, cbuf, len, dp + dcolumn_start,
                                           column_count);
        } else {
            /* If no dithering, each row of pixels is exactly the same. */
            if (byte_mask)
                il_overlay(dp + offset, out + offset, byte_mask, column_count,
                           ic->image->bytesPerPixel);
            else
                XP_MEMCPY(dp + offset, out + offset,
                          ic->image->bytesPerPixel * column_count);
        }

        /* Duplicate the mask also. */
        if (maskp) {
            if (column_count == ic->image->width) /* easy case */
                XP_MEMCPY(mp, maskp, im->maskWidthBytes);
            else
                il_generate_scaled_transparency_mask(im, cbuf, (int)len,
                                                     dcolumn_start,
                                                     mp, column_count,
                                                     draw_mode);
        }
    }

    /* If enough rows accumulated, send to the front-end for display. */
    il_partial(ic, drow_start, row_count, pass);
}