| /* |
| * "$Id: image.c 7473 2008-04-21 17:51:58Z mike $" |
| * |
| * Base image support for CUPS. |
| * |
| * Copyright 2007-2011 by Apple Inc. |
| * Copyright 1993-2005 by Easy Software Products. |
| * |
| * These coded instructions, statements, and computer programs are the |
| * property of Apple Inc. and are protected by Federal copyright |
| * law. Distribution and use rights are outlined in the file "LICENSE.txt" |
| * which should have been included with this file. If this file is |
| * file is missing or damaged, see the license at "http://www.cups.org/". |
| * |
| * This file is subject to the Apple OS-Developed Software exception. |
| * |
| * Contents: |
| * |
| * cupsImageClose() - Close an image file. |
| * cupsImageGetCol() - Get a column of pixels from an image. |
| * cupsImageGetColorSpace() - Get the image colorspace. |
| * cupsImageGetDepth() - Get the number of bytes per pixel. |
| * cupsImageGetHeight() - Get the height of an image. |
| * cupsImageGetRow() - Get a row of pixels from an image. |
| * cupsImageGetWidth() - Get the width of an image. |
| * cupsImageGetXPPI() - Get the horizontal resolution of an image. |
| * cupsImageGetYPPI() - Get the vertical resolution of an image. |
| * cupsImageOpen() - Open an image file and read it into memory. |
| * _cupsImagePutCol() - Put a column of pixels to an image. |
| * _cupsImagePutRow() - Put a row of pixels to an image. |
| * cupsImageSetMaxTiles() - Set the maximum number of tiles to cache. |
| * flush_tile() - Flush the least-recently-used tile in the cache. |
| * get_tile() - Get a cached tile. |
| */ |
| |
| /* |
| * Include necessary headers... |
| */ |
| |
| #include "image-private.h" |
| |
| |
| /* |
| * Local functions... |
| */ |
| |
| static void flush_tile(cups_image_t *img); |
| static cups_ib_t *get_tile(cups_image_t *img, int x, int y); |
| |
| |
| /* |
| * 'cupsImageClose()' - Close an image file. |
| */ |
| |
| void |
| cupsImageClose(cups_image_t *img) /* I - Image to close */ |
| { |
| cups_ic_t *current, /* Current cached tile */ |
| *next; /* Next cached tile */ |
| |
| |
| /* |
| * Wipe the tile cache file (if any)... |
| */ |
| |
| if (img->cachefile >= 0) |
| { |
| DEBUG_printf(("Closing/removing swap file \"%s\"...\n", img->cachename)); |
| |
| close(img->cachefile); |
| unlink(img->cachename); |
| } |
| |
| /* |
| * Free the image cache... |
| */ |
| |
| DEBUG_puts("Freeing memory..."); |
| |
| for (current = img->first, next = NULL; current != NULL; current = next) |
| { |
| DEBUG_printf(("Freeing cache (%p, next = %p)...\n", current, next)); |
| |
| next = current->next; |
| free(current); |
| } |
| |
| /* |
| * Free the rest of memory... |
| */ |
| |
| if (img->tiles != NULL) |
| { |
| DEBUG_printf(("Freeing tiles (%p)...\n", img->tiles[0])); |
| |
| free(img->tiles[0]); |
| |
| DEBUG_printf(("Freeing tile pointers (%p)...\n", img->tiles)); |
| |
| free(img->tiles); |
| } |
| |
| free(img); |
| } |
| |
| |
| /* |
| * 'cupsImageGetCol()' - Get a column of pixels from an image. |
| */ |
| |
| int /* O - -1 on error, 0 on success */ |
| cupsImageGetCol(cups_image_t *img, /* I - Image */ |
| int x, /* I - Column */ |
| int y, /* I - Start row */ |
| int height, /* I - Column height */ |
| cups_ib_t *pixels) /* O - Pixel data */ |
| { |
| int bpp, /* Bytes per pixel */ |
| twidth, /* Tile width */ |
| count; /* Number of pixels to get */ |
| const cups_ib_t *ib; /* Pointer into tile */ |
| |
| |
| if (img == NULL || x < 0 || x >= img->xsize || y >= img->ysize) |
| return (-1); |
| |
| if (y < 0) |
| { |
| height += y; |
| y = 0; |
| } |
| |
| if ((y + height) > img->ysize) |
| height = img->ysize - y; |
| |
| if (height < 1) |
| return (-1); |
| |
| bpp = cupsImageGetDepth(img); |
| twidth = bpp * (CUPS_TILE_SIZE - 1); |
| |
| while (height > 0) |
| { |
| ib = get_tile(img, x, y); |
| |
| if (ib == NULL) |
| return (-1); |
| |
| count = CUPS_TILE_SIZE - (y & (CUPS_TILE_SIZE - 1)); |
| if (count > height) |
| count = height; |
| |
| y += count; |
| height -= count; |
| |
| for (; count > 0; count --, ib += twidth) |
| switch (bpp) |
| { |
| case 4 : |
| *pixels++ = *ib++; |
| case 3 : |
| *pixels++ = *ib++; |
| *pixels++ = *ib++; |
| case 1 : |
| *pixels++ = *ib++; |
| break; |
| } |
| } |
| |
| return (0); |
| } |
| |
| |
| /* |
| * 'cupsImageGetColorSpace()' - Get the image colorspace. |
| */ |
| |
| cups_icspace_t /* O - Colorspace */ |
| cupsImageGetColorSpace( |
| cups_image_t *img) /* I - Image */ |
| { |
| return (img->colorspace); |
| } |
| |
| |
| /* |
| * 'cupsImageGetDepth()' - Get the number of bytes per pixel. |
| */ |
| |
| int /* O - Bytes per pixel */ |
| cupsImageGetDepth(cups_image_t *img) /* I - Image */ |
| { |
| return (abs(img->colorspace)); |
| } |
| |
| |
| /* |
| * 'cupsImageGetHeight()' - Get the height of an image. |
| */ |
| |
| unsigned /* O - Height in pixels */ |
| cupsImageGetHeight(cups_image_t *img) /* I - Image */ |
| { |
| return (img->ysize); |
| } |
| |
| |
| /* |
| * 'cupsImageGetRow()' - Get a row of pixels from an image. |
| */ |
| |
| int /* O - -1 on error, 0 on success */ |
| cupsImageGetRow(cups_image_t *img, /* I - Image */ |
| int x, /* I - Start column */ |
| int y, /* I - Row */ |
| int width, /* I - Width of row */ |
| cups_ib_t *pixels) /* O - Pixel data */ |
| { |
| int bpp, /* Bytes per pixel */ |
| count; /* Number of pixels to get */ |
| const cups_ib_t *ib; /* Pointer to pixels */ |
| |
| |
| if (img == NULL || y < 0 || y >= img->ysize || x >= img->xsize) |
| return (-1); |
| |
| if (x < 0) |
| { |
| width += x; |
| x = 0; |
| } |
| |
| if ((x + width) > img->xsize) |
| width = img->xsize - x; |
| |
| if (width < 1) |
| return (-1); |
| |
| bpp = img->colorspace < 0 ? -img->colorspace : img->colorspace; |
| |
| while (width > 0) |
| { |
| ib = get_tile(img, x, y); |
| |
| if (ib == NULL) |
| return (-1); |
| |
| count = CUPS_TILE_SIZE - (x & (CUPS_TILE_SIZE - 1)); |
| if (count > width) |
| count = width; |
| memcpy(pixels, ib, count * bpp); |
| pixels += count * bpp; |
| x += count; |
| width -= count; |
| } |
| |
| return (0); |
| } |
| |
| |
| /* |
| * 'cupsImageGetWidth()' - Get the width of an image. |
| */ |
| |
| unsigned /* O - Width in pixels */ |
| cupsImageGetWidth(cups_image_t *img) /* I - Image */ |
| { |
| return (img->xsize); |
| } |
| |
| |
| /* |
| * 'cupsImageGetXPPI()' - Get the horizontal resolution of an image. |
| */ |
| |
| unsigned /* O - Horizontal PPI */ |
| cupsImageGetXPPI(cups_image_t *img) /* I - Image */ |
| { |
| return (img->xppi); |
| } |
| |
| |
| /* |
| * 'cupsImageGetYPPI()' - Get the vertical resolution of an image. |
| */ |
| |
| unsigned /* O - Vertical PPI */ |
| cupsImageGetYPPI(cups_image_t *img) /* I - Image */ |
| { |
| return (img->yppi); |
| } |
| |
| |
| /* |
| * 'cupsImageOpen()' - Open an image file and read it into memory. |
| */ |
| |
| cups_image_t * /* O - New image */ |
| cupsImageOpen( |
| const char *filename, /* I - Filename of image */ |
| cups_icspace_t primary, /* I - Primary colorspace needed */ |
| cups_icspace_t secondary, /* I - Secondary colorspace if primary no good */ |
| int saturation, /* I - Color saturation level */ |
| int hue, /* I - Color hue adjustment */ |
| const cups_ib_t *lut) /* I - RGB gamma/brightness LUT */ |
| { |
| FILE *fp; /* File pointer */ |
| unsigned char header[16], /* First 16 bytes of file */ |
| header2[16]; /* Bytes 2048-2064 (PhotoCD) */ |
| cups_image_t *img; /* New image buffer */ |
| int status; /* Status of load... */ |
| |
| |
| DEBUG_printf(("cupsImageOpen(\"%s\", %d, %d, %d, %d, %p)\n", |
| filename ? filename : "(null)", primary, secondary, |
| saturation, hue, lut)); |
| |
| /* |
| * Figure out the file type... |
| */ |
| |
| if ((fp = fopen(filename, "r")) == NULL) |
| return (NULL); |
| |
| if (fread(header, 1, sizeof(header), fp) == 0) |
| { |
| fclose(fp); |
| return (NULL); |
| } |
| |
| fseek(fp, 2048, SEEK_SET); |
| memset(header2, 0, sizeof(header2)); |
| fread(header2, 1, sizeof(header2), fp); |
| fseek(fp, 0, SEEK_SET); |
| |
| /* |
| * Allocate memory... |
| */ |
| |
| img = calloc(sizeof(cups_image_t), 1); |
| |
| if (img == NULL) |
| { |
| fclose(fp); |
| return (NULL); |
| } |
| |
| /* |
| * Load the image as appropriate... |
| */ |
| |
| img->max_ics = CUPS_TILE_MINIMUM; |
| img->xppi = 128; |
| img->yppi = 128; |
| |
| if (!memcmp(header, "GIF87a", 6) || !memcmp(header, "GIF89a", 6)) |
| status = _cupsImageReadGIF(img, fp, primary, secondary, saturation, hue, |
| lut); |
| else if (!memcmp(header, "BM", 2)) |
| status = _cupsImageReadBMP(img, fp, primary, secondary, saturation, hue, |
| lut); |
| else if (header[0] == 0x01 && header[1] == 0xda) |
| status = _cupsImageReadSGI(img, fp, primary, secondary, saturation, hue, |
| lut); |
| else if (header[0] == 0x59 && header[1] == 0xa6 && |
| header[2] == 0x6a && header[3] == 0x95) |
| status = _cupsImageReadSunRaster(img, fp, primary, secondary, saturation, |
| hue, lut); |
| else if (header[0] == 'P' && header[1] >= '1' && header[1] <= '6') |
| status = _cupsImageReadPNM(img, fp, primary, secondary, saturation, hue, |
| lut); |
| else if (!memcmp(header2, "PCD_IPI", 7)) |
| status = _cupsImageReadPhotoCD(img, fp, primary, secondary, saturation, |
| hue, lut); |
| else if (!memcmp(header + 8, "\000\010", 2) || |
| !memcmp(header + 8, "\000\030", 2)) |
| status = _cupsImageReadPIX(img, fp, primary, secondary, saturation, hue, |
| lut); |
| #if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ) |
| else if (!memcmp(header, "\211PNG", 4)) |
| status = _cupsImageReadPNG(img, fp, primary, secondary, saturation, hue, |
| lut); |
| #endif /* HAVE_LIBPNG && HAVE_LIBZ */ |
| #ifdef HAVE_LIBJPEG |
| else if (!memcmp(header, "\377\330\377", 3) && /* Start-of-Image */ |
| header[3] >= 0xe0 && header[3] <= 0xef) /* APPn */ |
| status = _cupsImageReadJPEG(img, fp, primary, secondary, saturation, hue, |
| lut); |
| #endif /* HAVE_LIBJPEG */ |
| #ifdef HAVE_LIBTIFF |
| else if (!memcmp(header, "MM\000\052", 4) || |
| !memcmp(header, "II\052\000", 4)) |
| status = _cupsImageReadTIFF(img, fp, primary, secondary, saturation, hue, |
| lut); |
| #endif /* HAVE_LIBTIFF */ |
| else |
| { |
| fclose(fp); |
| status = -1; |
| } |
| |
| if (status) |
| { |
| free(img); |
| return (NULL); |
| } |
| else |
| return (img); |
| } |
| |
| |
| /* |
| * '_cupsImagePutCol()' - Put a column of pixels to an image. |
| */ |
| |
| int /* O - -1 on error, 0 on success */ |
| _cupsImagePutCol( |
| cups_image_t *img, /* I - Image */ |
| int x, /* I - Column */ |
| int y, /* I - Start row */ |
| int height, /* I - Column height */ |
| const cups_ib_t *pixels) /* I - Pixels to put */ |
| { |
| int bpp, /* Bytes per pixel */ |
| twidth, /* Width of tile */ |
| count; /* Number of pixels to put */ |
| int tilex, /* Column within tile */ |
| tiley; /* Row within tile */ |
| cups_ib_t *ib; /* Pointer to pixels in tile */ |
| |
| |
| if (img == NULL || x < 0 || x >= img->xsize || y >= img->ysize) |
| return (-1); |
| |
| if (y < 0) |
| { |
| height += y; |
| y = 0; |
| } |
| |
| if ((y + height) > img->ysize) |
| height = img->ysize - y; |
| |
| if (height < 1) |
| return (-1); |
| |
| bpp = cupsImageGetDepth(img); |
| twidth = bpp * (CUPS_TILE_SIZE - 1); |
| tilex = x / CUPS_TILE_SIZE; |
| tiley = y / CUPS_TILE_SIZE; |
| |
| while (height > 0) |
| { |
| ib = get_tile(img, x, y); |
| |
| if (ib == NULL) |
| return (-1); |
| |
| img->tiles[tiley][tilex].dirty = 1; |
| tiley ++; |
| |
| count = CUPS_TILE_SIZE - (y & (CUPS_TILE_SIZE - 1)); |
| if (count > height) |
| count = height; |
| |
| y += count; |
| height -= count; |
| |
| for (; count > 0; count --, ib += twidth) |
| switch (bpp) |
| { |
| case 4 : |
| *ib++ = *pixels++; |
| case 3 : |
| *ib++ = *pixels++; |
| *ib++ = *pixels++; |
| case 1 : |
| *ib++ = *pixels++; |
| break; |
| } |
| } |
| |
| return (0); |
| } |
| |
| |
| /* |
| * '_cupsImagePutRow()' - Put a row of pixels to an image. |
| */ |
| |
| int /* O - -1 on error, 0 on success */ |
| _cupsImagePutRow( |
| cups_image_t *img, /* I - Image */ |
| int x, /* I - Start column */ |
| int y, /* I - Row */ |
| int width, /* I - Row width */ |
| const cups_ib_t *pixels) /* I - Pixel data */ |
| { |
| int bpp, /* Bytes per pixel */ |
| count; /* Number of pixels to put */ |
| int tilex, /* Column within tile */ |
| tiley; /* Row within tile */ |
| cups_ib_t *ib; /* Pointer to pixels in tile */ |
| |
| |
| if (img == NULL || y < 0 || y >= img->ysize || x >= img->xsize) |
| return (-1); |
| |
| if (x < 0) |
| { |
| width += x; |
| x = 0; |
| } |
| |
| if ((x + width) > img->xsize) |
| width = img->xsize - x; |
| |
| if (width < 1) |
| return (-1); |
| |
| bpp = img->colorspace < 0 ? -img->colorspace : img->colorspace; |
| tilex = x / CUPS_TILE_SIZE; |
| tiley = y / CUPS_TILE_SIZE; |
| |
| while (width > 0) |
| { |
| ib = get_tile(img, x, y); |
| |
| if (ib == NULL) |
| return (-1); |
| |
| img->tiles[tiley][tilex].dirty = 1; |
| |
| count = CUPS_TILE_SIZE - (x & (CUPS_TILE_SIZE - 1)); |
| if (count > width) |
| count = width; |
| memcpy(ib, pixels, count * bpp); |
| pixels += count * bpp; |
| x += count; |
| width -= count; |
| tilex ++; |
| } |
| |
| return (0); |
| } |
| |
| |
| /* |
| * 'cupsImageSetMaxTiles()' - Set the maximum number of tiles to cache. |
| * |
| * If the "max_tiles" argument is 0 then the maximum number of tiles is |
| * computed from the image size or the RIP_CACHE environment variable. |
| */ |
| |
| void |
| cupsImageSetMaxTiles( |
| cups_image_t *img, /* I - Image to set */ |
| int max_tiles) /* I - Number of tiles to cache */ |
| { |
| int cache_size, /* Size of tile cache in bytes */ |
| min_tiles, /* Minimum number of tiles to cache */ |
| max_size; /* Maximum cache size in bytes */ |
| char *cache_env, /* Cache size environment variable */ |
| cache_units[255]; /* Cache size units */ |
| |
| |
| min_tiles = max(CUPS_TILE_MINIMUM, |
| 1 + max((img->xsize + CUPS_TILE_SIZE - 1) / CUPS_TILE_SIZE, |
| (img->ysize + CUPS_TILE_SIZE - 1) / CUPS_TILE_SIZE)); |
| |
| if (max_tiles == 0) |
| max_tiles = ((img->xsize + CUPS_TILE_SIZE - 1) / CUPS_TILE_SIZE) * |
| ((img->ysize + CUPS_TILE_SIZE - 1) / CUPS_TILE_SIZE); |
| |
| cache_size = max_tiles * CUPS_TILE_SIZE * CUPS_TILE_SIZE * |
| cupsImageGetDepth(img); |
| |
| if ((cache_env = getenv("RIP_MAX_CACHE")) != NULL) |
| { |
| switch (sscanf(cache_env, "%d%254s", &max_size, cache_units)) |
| { |
| case 0 : |
| max_size = 32 * 1024 * 1024; |
| break; |
| case 1 : |
| max_size *= 4 * CUPS_TILE_SIZE * CUPS_TILE_SIZE; |
| break; |
| case 2 : |
| if (tolower(cache_units[0] & 255) == 'g') |
| max_size *= 1024 * 1024 * 1024; |
| else if (tolower(cache_units[0] & 255) == 'm') |
| max_size *= 1024 * 1024; |
| else if (tolower(cache_units[0] & 255) == 'k') |
| max_size *= 1024; |
| else if (tolower(cache_units[0] & 255) == 't') |
| max_size *= 4 * CUPS_TILE_SIZE * CUPS_TILE_SIZE; |
| break; |
| } |
| } |
| else |
| max_size = 32 * 1024 * 1024; |
| |
| if (cache_size > max_size) |
| max_tiles = max_size / CUPS_TILE_SIZE / CUPS_TILE_SIZE / |
| cupsImageGetDepth(img); |
| |
| if (max_tiles < min_tiles) |
| max_tiles = min_tiles; |
| |
| img->max_ics = max_tiles; |
| |
| DEBUG_printf(("max_ics=%d...\n", img->max_ics)); |
| } |
| |
| |
| /* |
| * 'flush_tile()' - Flush the least-recently-used tile in the cache. |
| */ |
| |
| static void |
| flush_tile(cups_image_t *img) /* I - Image */ |
| { |
| int bpp; /* Bytes per pixel */ |
| cups_itile_t *tile; /* Pointer to tile */ |
| |
| |
| bpp = cupsImageGetDepth(img); |
| tile = img->first->tile; |
| |
| if (!tile->dirty) |
| { |
| tile->ic = NULL; |
| return; |
| } |
| |
| if (img->cachefile < 0) |
| { |
| if ((img->cachefile = cupsTempFd(img->cachename, |
| sizeof(img->cachename))) < 0) |
| { |
| tile->ic = NULL; |
| tile->dirty = 0; |
| return; |
| } |
| |
| DEBUG_printf(("Created swap file \"%s\"...\n", img->cachename)); |
| } |
| |
| if (tile->pos >= 0) |
| { |
| if (lseek(img->cachefile, tile->pos, SEEK_SET) != tile->pos) |
| { |
| tile->ic = NULL; |
| tile->dirty = 0; |
| return; |
| } |
| } |
| else |
| { |
| if ((tile->pos = lseek(img->cachefile, 0, SEEK_END)) < 0) |
| { |
| tile->ic = NULL; |
| tile->dirty = 0; |
| return; |
| } |
| } |
| |
| write(img->cachefile, tile->ic->pixels, bpp * CUPS_TILE_SIZE * CUPS_TILE_SIZE); |
| |
| tile->ic = NULL; |
| tile->dirty = 0; |
| } |
| |
| |
| /* |
| * 'get_tile()' - Get a cached tile. |
| */ |
| |
| static cups_ib_t * /* O - Pointer to tile or NULL */ |
| get_tile(cups_image_t *img, /* I - Image */ |
| int x, /* I - Column in image */ |
| int y) /* I - Row in image */ |
| { |
| int bpp, /* Bytes per pixel */ |
| tilex, /* Column within tile */ |
| tiley, /* Row within tile */ |
| xtiles, /* Number of tiles horizontally */ |
| ytiles; /* Number of tiles vertically */ |
| cups_ic_t *ic; /* Cache pointer */ |
| cups_itile_t *tile; /* Tile pointer */ |
| |
| |
| if (img->tiles == NULL) |
| { |
| xtiles = (img->xsize + CUPS_TILE_SIZE - 1) / CUPS_TILE_SIZE; |
| ytiles = (img->ysize + CUPS_TILE_SIZE - 1) / CUPS_TILE_SIZE; |
| |
| DEBUG_printf(("Creating tile array (%dx%d)\n", xtiles, ytiles)); |
| |
| if ((img->tiles = calloc(sizeof(cups_itile_t *), ytiles)) == NULL) |
| return (NULL); |
| |
| if ((tile = calloc(xtiles * sizeof(cups_itile_t), ytiles)) == NULL) |
| return (NULL); |
| |
| for (tiley = 0; tiley < ytiles; tiley ++) |
| { |
| img->tiles[tiley] = tile; |
| for (tilex = xtiles; tilex > 0; tilex --, tile ++) |
| tile->pos = -1; |
| } |
| } |
| |
| bpp = cupsImageGetDepth(img); |
| tilex = x / CUPS_TILE_SIZE; |
| tiley = y / CUPS_TILE_SIZE; |
| tile = img->tiles[tiley] + tilex; |
| x &= (CUPS_TILE_SIZE - 1); |
| y &= (CUPS_TILE_SIZE - 1); |
| |
| if ((ic = tile->ic) == NULL) |
| { |
| if (img->num_ics < img->max_ics) |
| { |
| if ((ic = calloc(sizeof(cups_ic_t) + |
| bpp * CUPS_TILE_SIZE * CUPS_TILE_SIZE, 1)) == NULL) |
| { |
| if (img->num_ics == 0) |
| return (NULL); |
| |
| flush_tile(img); |
| ic = img->first; |
| } |
| else |
| { |
| ic->pixels = ((cups_ib_t *)ic) + sizeof(cups_ic_t); |
| |
| img->num_ics ++; |
| |
| DEBUG_printf(("Allocated cache tile %d (%p)...\n", img->num_ics, ic)); |
| } |
| } |
| else |
| { |
| DEBUG_printf(("Flushing old cache tile (%p)...\n", img->first)); |
| |
| flush_tile(img); |
| ic = img->first; |
| } |
| |
| ic->tile = tile; |
| tile->ic = ic; |
| |
| if (tile->pos >= 0) |
| { |
| DEBUG_printf(("Loading cache tile from file position " CUPS_LLFMT "...\n", |
| CUPS_LLCAST tile->pos)); |
| |
| lseek(img->cachefile, tile->pos, SEEK_SET); |
| read(img->cachefile, ic->pixels, bpp * CUPS_TILE_SIZE * CUPS_TILE_SIZE); |
| } |
| else |
| { |
| DEBUG_puts("Clearing cache tile..."); |
| |
| memset(ic->pixels, 0, bpp * CUPS_TILE_SIZE * CUPS_TILE_SIZE); |
| } |
| } |
| |
| if (ic == img->first) |
| { |
| if (ic->next != NULL) |
| ic->next->prev = NULL; |
| |
| img->first = ic->next; |
| ic->next = NULL; |
| ic->prev = NULL; |
| } |
| else if (img->first == NULL) |
| img->first = ic; |
| |
| if (ic != img->last) |
| { |
| /* |
| * Remove the cache entry from the list... |
| */ |
| |
| if (ic->prev != NULL) |
| ic->prev->next = ic->next; |
| if (ic->next != NULL) |
| ic->next->prev = ic->prev; |
| |
| /* |
| * And add it to the end... |
| */ |
| |
| if (img->last != NULL) |
| img->last->next = ic; |
| |
| ic->prev = img->last; |
| img->last = ic; |
| } |
| |
| ic->next = NULL; |
| |
| return (ic->pixels + bpp * (y * CUPS_TILE_SIZE + x)); |
| } |
| |
| |
| /* |
| * End of "$Id: image.c 7473 2008-04-21 17:51:58Z mike $". |
| */ |