#include "image.h"
#include <unistd.h>
#include <ctype.h>
#include <math.h>
#include <cups/cups.h>
static ib_t *get_tile(image_t *img, int x, int y);
static void flush_tile(image_t *img);
image_t *
ImageOpen(char *filename,
int primary,
int secondary,
int saturation,
int hue,
const ib_t *lut)
{
FILE *fp;
unsigned char header[16],
header2[16];
image_t *img;
int status;
fprintf(stderr, "DEBUG: ImageOpen(\"%s\", %d, %d, %d, %d, %p)\n",
filename ? filename : "(null)", primary, secondary,
saturation, hue, lut);
if (filename == NULL)
{
fputs("ERROR: Image filename == NULL!\n", stderr);
return (NULL);
}
if ((fp = fopen(filename, "r")) == NULL)
{
perror("ERROR: Unable to open image file");
return (NULL);
}
if (fread(header, 1, sizeof(header), fp) == 0)
{
perror("ERROR: Unable to read image file header");
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);
img = calloc(sizeof(image_t), 1);
if (img == NULL)
{
perror("ERROR: Unable to allocate memory for image file");
fclose(fp);
return (NULL);
}
img->max_ics = TILE_MINIMUM;
img->xppi = 128;
img->yppi = 128;
if (memcmp(header, "GIF87a", 6) == 0 ||
memcmp(header, "GIF89a", 6) == 0)
status = ImageReadGIF(img, fp, primary, secondary, saturation, hue, lut);
else if (memcmp(header, "BM", 2) == 0)
status = ImageReadBMP(img, fp, primary, secondary, saturation, hue, lut);
else if (header[0] == 0x01 && header[1] == 0xda)
status = ImageReadSGI(img, fp, primary, secondary, saturation, hue, lut);
else if (header[0] == 0x59 && header[1] == 0xa6 &&
header[2] == 0x6a && header[3] == 0x95)
status = ImageReadSunRaster(img, fp, primary, secondary, saturation, hue, lut);
else if (header[0] == 'P' && header[1] >= '1' && header[1] <= '6')
status = ImageReadPNM(img, fp, primary, secondary, saturation, hue, lut);
else if (memcmp(header2, "PCD_IPI", 7) == 0)
status = ImageReadPhotoCD(img, fp, primary, secondary, saturation, hue, lut);
else if (memcmp(header + 8, "\000\010", 2) == 0 ||
memcmp(header + 8, "\000\030", 2) == 0)
status = ImageReadPIX(img, fp, primary, secondary, saturation, hue, lut);
#if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ)
else if (memcmp(header, "\211PNG", 4) == 0)
status = ImageReadPNG(img, fp, primary, secondary, saturation, hue, lut);
#endif
#ifdef HAVE_LIBJPEG
else if (memcmp(header, "\377\330\377", 3) == 0 &&
header[3] >= 0xe0 && header[3] <= 0xef)
status = ImageReadJPEG(img, fp, primary, secondary, saturation, hue, lut);
#endif
#ifdef HAVE_LIBTIFF
else if (memcmp(header, "MM", 2) == 0 ||
memcmp(header, "II", 2) == 0)
status = ImageReadTIFF(img, fp, primary, secondary, saturation, hue, lut);
#endif
else
{
fputs("ERROR: Unknown image file format!\n", stderr);
fclose(fp);
status = -1;
}
if (status)
{
free(img);
return (NULL);
}
else
return (img);
}
void
ImageClose(image_t *img)
{
ic_t *current,
*next;
if (img->cachefile != NULL)
{
fprintf(stderr, "DEBUG: Closing and removing swap file \"%s\"...\n",
img->cachename);
fclose(img->cachefile);
unlink(img->cachename);
}
fputs("DEBUG: Freeing memory...\n", stderr);
for (current = img->first, next = NULL; current != NULL; current = next)
{
fprintf(stderr, "DEBUG: Freeing cache (%p, next = %p)...\n",
current, next);
next = current->next;
free(current);
}
if (img->tiles != NULL)
{
fprintf(stderr, "DEBUG: Freeing tiles (%p)...\n", img->tiles[0]);
free(img->tiles[0]);
fprintf(stderr, "DEBUG: Freeing tile pointers (%p)...\n", img->tiles);
free(img->tiles);
}
free(img);
}
void
ImageSetMaxTiles(image_t *img,
int max_tiles)
{
int cache_size,
min_tiles,
max_size;
char *cache_env,
cache_units[255];
min_tiles = max(TILE_MINIMUM,
1 + max((img->xsize + TILE_SIZE - 1) / TILE_SIZE,
(img->ysize + TILE_SIZE - 1) / TILE_SIZE));
if (max_tiles == 0)
max_tiles = ((img->xsize + TILE_SIZE - 1) / TILE_SIZE) *
((img->ysize + TILE_SIZE - 1) / TILE_SIZE);
cache_size = max_tiles * TILE_SIZE * TILE_SIZE * ImageGetDepth(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 * TILE_SIZE * 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 * TILE_SIZE * TILE_SIZE;
break;
}
}
else
max_size = 32 * 1024 * 1024;
if (cache_size > max_size)
max_tiles = max_size / TILE_SIZE / TILE_SIZE / ImageGetDepth(img);
if (max_tiles < min_tiles)
max_tiles = min_tiles;
img->max_ics = max_tiles;
fprintf(stderr, "DEBUG: max_ics=%d...\n", img->max_ics);
}
int
ImageGetCol(image_t *img,
int x,
int y,
int height,
ib_t *pixels)
{
int bpp,
twidth,
count;
const ib_t *ib;
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 = ImageGetDepth(img);
twidth = bpp * (TILE_SIZE - 1);
while (height > 0)
{
ib = get_tile(img, x, y);
if (ib == NULL)
return (-1);
count = TILE_SIZE - (y & (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);
}
int
ImageGetRow(image_t *img,
int x,
int y,
int width,
ib_t *pixels)
{
int bpp,
count;
const ib_t *ib;
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 = TILE_SIZE - (x & (TILE_SIZE - 1));
if (count > width)
count = width;
memcpy(pixels, ib, count * bpp);
pixels += count * bpp;
x += count;
width -= count;
}
return (0);
}
int
ImagePutCol(image_t *img,
int x,
int y,
int height,
const ib_t *pixels)
{
int bpp,
twidth,
count;
int tilex,
tiley;
ib_t *ib;
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 = ImageGetDepth(img);
twidth = bpp * (TILE_SIZE - 1);
tilex = x / TILE_SIZE;
tiley = y / TILE_SIZE;
while (height > 0)
{
ib = get_tile(img, x, y);
if (ib == NULL)
return (-1);
img->tiles[tiley][tilex].dirty = 1;
tiley ++;
count = TILE_SIZE - (y & (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);
}
int
ImagePutRow(image_t *img,
int x,
int y,
int width,
const ib_t *pixels)
{
int bpp,
count;
int tilex,
tiley;
ib_t *ib;
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 / TILE_SIZE;
tiley = y / TILE_SIZE;
while (width > 0)
{
ib = get_tile(img, x, y);
if (ib == NULL)
return (-1);
img->tiles[tiley][tilex].dirty = 1;
count = TILE_SIZE - (x & (TILE_SIZE - 1));
if (count > width)
count = width;
memcpy(ib, pixels, count * bpp);
pixels += count * bpp;
x += count;
width -= count;
tilex ++;
}
return (0);
}
static ib_t *
get_tile(image_t *img,
int x,
int y)
{
int bpp,
tilex,
tiley,
xtiles,
ytiles;
ic_t *ic;
itile_t *tile;
if (x >= img->xsize || y >= img->ysize)
{
fprintf(stderr, "ERROR: Internal image RIP error - %d,%d is outside of %dx%d\n",
x, y, img->xsize, img->ysize);
return (NULL);
}
if (img->tiles == NULL)
{
xtiles = (img->xsize + TILE_SIZE - 1) / TILE_SIZE;
ytiles = (img->ysize + TILE_SIZE - 1) / TILE_SIZE;
fprintf(stderr, "DEBUG: Creating tile array (%dx%d)\n", xtiles, ytiles);
img->tiles = calloc(sizeof(itile_t *), ytiles);
tile = calloc(sizeof(itile_t), xtiles * ytiles);
for (tiley = 0; tiley < ytiles; tiley ++)
{
img->tiles[tiley] = tile;
for (tilex = xtiles; tilex > 0; tilex --, tile ++)
tile->pos = -1;
}
}
bpp = ImageGetDepth(img);
tilex = x / TILE_SIZE;
tiley = y / TILE_SIZE;
x &= (TILE_SIZE - 1);
y &= (TILE_SIZE - 1);
tile = img->tiles[tiley] + tilex;
if ((ic = tile->ic) == NULL)
{
if (img->num_ics < img->max_ics)
{
ic = calloc(sizeof(ic_t) + bpp * TILE_SIZE * TILE_SIZE, 1);
ic->pixels = ((ib_t *)ic) + sizeof(ic_t);
img->num_ics ++;
fprintf(stderr, "DEBUG: Allocated cache tile %d (%p)...\n",
img->num_ics, ic);
}
else
{
fprintf(stderr, "DEBUG: Flushing old cache tile (%p)...\n",
img->first);
flush_tile(img);
ic = img->first;
}
ic->tile = tile;
tile->ic = ic;
if (tile->pos >= 0)
{
fprintf(stderr, "DEBUG: Loading cache tile from file position %ld...\n",
tile->pos);
if (ftell(img->cachefile) != tile->pos)
if (fseek(img->cachefile, tile->pos, SEEK_SET))
perror("get_tile:");
fread(ic->pixels, bpp, TILE_SIZE * TILE_SIZE, img->cachefile);
}
else
{
fputs("DEBUG: Clearing cache tile...\n", stderr);
memset(ic->pixels, 0, bpp * TILE_SIZE * 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)
{
if (ic->prev != NULL)
ic->prev->next = ic->next;
if (ic->next != NULL)
ic->next->prev = ic->prev;
if (img->last != NULL)
img->last->next = ic;
ic->prev = img->last;
img->last = ic;
}
ic->next = NULL;
return (ic->pixels + bpp * (y * TILE_SIZE + x));
}
static void
flush_tile(image_t *img)
{
int fd;
int bpp;
itile_t *tile;
bpp = ImageGetDepth(img);
tile = img->first->tile;
if (!tile->dirty)
{
tile->ic = NULL;
return;
}
if (img->cachefile == NULL)
{
if ((fd = cupsTempFd(img->cachename, sizeof(img->cachename))) < 0)
{
perror("ERROR: Unable to create image swap file");
tile->ic = NULL;
tile->dirty = 0;
return;
}
fprintf(stderr, "DEBUG: Created swap file \"%s\"...\n", img->cachename);
if ((img->cachefile = fdopen(fd, "wb+")) == NULL)
{
perror("ERROR: Unable to create image swap file");
close(fd);
unlink(img->cachename);
tile->ic = NULL;
tile->dirty = 0;
return;
}
}
if (tile->pos >= 0)
{
if (ftell(img->cachefile) != tile->pos)
if (fseek(img->cachefile, tile->pos, SEEK_SET))
{
perror("ERROR: Unable to seek in swap file");
tile->ic = NULL;
tile->dirty = 0;
return;
}
}
else
{
if (fseek(img->cachefile, 0, SEEK_END))
{
perror("ERROR: Unable to append to swap file");
tile->ic = NULL;
tile->dirty = 0;
return;
}
tile->pos = ftell(img->cachefile);
}
if (fwrite(tile->ic->pixels, bpp, TILE_SIZE * TILE_SIZE, img->cachefile) < 1)
perror("ERROR: Unable to write tile to swap file");
else
fprintf(stderr, "DEBUG: Wrote tile at position %ld...\n", tile->pos);
tile->ic = NULL;
tile->dirty = 0;
}