]> git.ipfire.org Git - thirdparty/cups.git/blob - filter/image.c
Merge changes from CUPS 1.5.1-r9875.
[thirdparty/cups.git] / filter / image.c
1 /*
2 * "$Id: image.c 7473 2008-04-21 17:51:58Z mike $"
3 *
4 * Base image support for CUPS.
5 *
6 * Copyright 2007-2011 by Apple Inc.
7 * Copyright 1993-2005 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
19 * cupsImageClose() - Close an image file.
20 * cupsImageGetCol() - Get a column of pixels from an image.
21 * cupsImageGetColorSpace() - Get the image colorspace.
22 * cupsImageGetDepth() - Get the number of bytes per pixel.
23 * cupsImageGetHeight() - Get the height of an image.
24 * cupsImageGetRow() - Get a row of pixels from an image.
25 * cupsImageGetWidth() - Get the width of an image.
26 * cupsImageGetXPPI() - Get the horizontal resolution of an image.
27 * cupsImageGetYPPI() - Get the vertical resolution of an image.
28 * cupsImageOpen() - Open an image file and read it into memory.
29 * _cupsImagePutCol() - Put a column of pixels to an image.
30 * _cupsImagePutRow() - Put a row of pixels to an image.
31 * cupsImageSetMaxTiles() - Set the maximum number of tiles to cache.
32 * flush_tile() - Flush the least-recently-used tile in the cache.
33 * get_tile() - Get a cached tile.
34 */
35
36 /*
37 * Include necessary headers...
38 */
39
40 #include "image-private.h"
41
42
43 /*
44 * Local functions...
45 */
46
47 static void flush_tile(cups_image_t *img);
48 static cups_ib_t *get_tile(cups_image_t *img, int x, int y);
49
50
51 /*
52 * 'cupsImageClose()' - Close an image file.
53 */
54
55 void
56 cupsImageClose(cups_image_t *img) /* I - Image to close */
57 {
58 cups_ic_t *current, /* Current cached tile */
59 *next; /* Next cached tile */
60
61
62 /*
63 * Wipe the tile cache file (if any)...
64 */
65
66 if (img->cachefile >= 0)
67 {
68 DEBUG_printf(("Closing/removing swap file \"%s\"...\n", img->cachename));
69
70 close(img->cachefile);
71 unlink(img->cachename);
72 }
73
74 /*
75 * Free the image cache...
76 */
77
78 DEBUG_puts("Freeing memory...");
79
80 for (current = img->first, next = NULL; current != NULL; current = next)
81 {
82 DEBUG_printf(("Freeing cache (%p, next = %p)...\n", current, next));
83
84 next = current->next;
85 free(current);
86 }
87
88 /*
89 * Free the rest of memory...
90 */
91
92 if (img->tiles != NULL)
93 {
94 DEBUG_printf(("Freeing tiles (%p)...\n", img->tiles[0]));
95
96 free(img->tiles[0]);
97
98 DEBUG_printf(("Freeing tile pointers (%p)...\n", img->tiles));
99
100 free(img->tiles);
101 }
102
103 free(img);
104 }
105
106
107 /*
108 * 'cupsImageGetCol()' - Get a column of pixels from an image.
109 */
110
111 int /* O - -1 on error, 0 on success */
112 cupsImageGetCol(cups_image_t *img, /* I - Image */
113 int x, /* I - Column */
114 int y, /* I - Start row */
115 int height, /* I - Column height */
116 cups_ib_t *pixels) /* O - Pixel data */
117 {
118 int bpp, /* Bytes per pixel */
119 twidth, /* Tile width */
120 count; /* Number of pixels to get */
121 const cups_ib_t *ib; /* Pointer into tile */
122
123
124 if (img == NULL || x < 0 || x >= img->xsize || y >= img->ysize)
125 return (-1);
126
127 if (y < 0)
128 {
129 height += y;
130 y = 0;
131 }
132
133 if ((y + height) > img->ysize)
134 height = img->ysize - y;
135
136 if (height < 1)
137 return (-1);
138
139 bpp = cupsImageGetDepth(img);
140 twidth = bpp * (CUPS_TILE_SIZE - 1);
141
142 while (height > 0)
143 {
144 ib = get_tile(img, x, y);
145
146 if (ib == NULL)
147 return (-1);
148
149 count = CUPS_TILE_SIZE - (y & (CUPS_TILE_SIZE - 1));
150 if (count > height)
151 count = height;
152
153 y += count;
154 height -= count;
155
156 for (; count > 0; count --, ib += twidth)
157 switch (bpp)
158 {
159 case 4 :
160 *pixels++ = *ib++;
161 case 3 :
162 *pixels++ = *ib++;
163 *pixels++ = *ib++;
164 case 1 :
165 *pixels++ = *ib++;
166 break;
167 }
168 }
169
170 return (0);
171 }
172
173
174 /*
175 * 'cupsImageGetColorSpace()' - Get the image colorspace.
176 */
177
178 cups_icspace_t /* O - Colorspace */
179 cupsImageGetColorSpace(
180 cups_image_t *img) /* I - Image */
181 {
182 return (img->colorspace);
183 }
184
185
186 /*
187 * 'cupsImageGetDepth()' - Get the number of bytes per pixel.
188 */
189
190 int /* O - Bytes per pixel */
191 cupsImageGetDepth(cups_image_t *img) /* I - Image */
192 {
193 return (abs(img->colorspace));
194 }
195
196
197 /*
198 * 'cupsImageGetHeight()' - Get the height of an image.
199 */
200
201 unsigned /* O - Height in pixels */
202 cupsImageGetHeight(cups_image_t *img) /* I - Image */
203 {
204 return (img->ysize);
205 }
206
207
208 /*
209 * 'cupsImageGetRow()' - Get a row of pixels from an image.
210 */
211
212 int /* O - -1 on error, 0 on success */
213 cupsImageGetRow(cups_image_t *img, /* I - Image */
214 int x, /* I - Start column */
215 int y, /* I - Row */
216 int width, /* I - Width of row */
217 cups_ib_t *pixels) /* O - Pixel data */
218 {
219 int bpp, /* Bytes per pixel */
220 count; /* Number of pixels to get */
221 const cups_ib_t *ib; /* Pointer to pixels */
222
223
224 if (img == NULL || y < 0 || y >= img->ysize || x >= img->xsize)
225 return (-1);
226
227 if (x < 0)
228 {
229 width += x;
230 x = 0;
231 }
232
233 if ((x + width) > img->xsize)
234 width = img->xsize - x;
235
236 if (width < 1)
237 return (-1);
238
239 bpp = img->colorspace < 0 ? -img->colorspace : img->colorspace;
240
241 while (width > 0)
242 {
243 ib = get_tile(img, x, y);
244
245 if (ib == NULL)
246 return (-1);
247
248 count = CUPS_TILE_SIZE - (x & (CUPS_TILE_SIZE - 1));
249 if (count > width)
250 count = width;
251 memcpy(pixels, ib, count * bpp);
252 pixels += count * bpp;
253 x += count;
254 width -= count;
255 }
256
257 return (0);
258 }
259
260
261 /*
262 * 'cupsImageGetWidth()' - Get the width of an image.
263 */
264
265 unsigned /* O - Width in pixels */
266 cupsImageGetWidth(cups_image_t *img) /* I - Image */
267 {
268 return (img->xsize);
269 }
270
271
272 /*
273 * 'cupsImageGetXPPI()' - Get the horizontal resolution of an image.
274 */
275
276 unsigned /* O - Horizontal PPI */
277 cupsImageGetXPPI(cups_image_t *img) /* I - Image */
278 {
279 return (img->xppi);
280 }
281
282
283 /*
284 * 'cupsImageGetYPPI()' - Get the vertical resolution of an image.
285 */
286
287 unsigned /* O - Vertical PPI */
288 cupsImageGetYPPI(cups_image_t *img) /* I - Image */
289 {
290 return (img->yppi);
291 }
292
293
294 /*
295 * 'cupsImageOpen()' - Open an image file and read it into memory.
296 */
297
298 cups_image_t * /* O - New image */
299 cupsImageOpen(
300 const char *filename, /* I - Filename of image */
301 cups_icspace_t primary, /* I - Primary colorspace needed */
302 cups_icspace_t secondary, /* I - Secondary colorspace if primary no good */
303 int saturation, /* I - Color saturation level */
304 int hue, /* I - Color hue adjustment */
305 const cups_ib_t *lut) /* I - RGB gamma/brightness LUT */
306 {
307 FILE *fp; /* File pointer */
308 unsigned char header[16], /* First 16 bytes of file */
309 header2[16]; /* Bytes 2048-2064 (PhotoCD) */
310 cups_image_t *img; /* New image buffer */
311 int status; /* Status of load... */
312
313
314 DEBUG_printf(("cupsImageOpen(\"%s\", %d, %d, %d, %d, %p)\n",
315 filename ? filename : "(null)", primary, secondary,
316 saturation, hue, lut));
317
318 /*
319 * Figure out the file type...
320 */
321
322 if ((fp = fopen(filename, "r")) == NULL)
323 return (NULL);
324
325 if (fread(header, 1, sizeof(header), fp) == 0)
326 {
327 fclose(fp);
328 return (NULL);
329 }
330
331 fseek(fp, 2048, SEEK_SET);
332 memset(header2, 0, sizeof(header2));
333 fread(header2, 1, sizeof(header2), fp);
334 fseek(fp, 0, SEEK_SET);
335
336 /*
337 * Allocate memory...
338 */
339
340 img = calloc(sizeof(cups_image_t), 1);
341
342 if (img == NULL)
343 {
344 fclose(fp);
345 return (NULL);
346 }
347
348 /*
349 * Load the image as appropriate...
350 */
351
352 img->cachefile = -1;
353 img->max_ics = CUPS_TILE_MINIMUM;
354 img->xppi = 128;
355 img->yppi = 128;
356
357 if (!memcmp(header, "GIF87a", 6) || !memcmp(header, "GIF89a", 6))
358 status = _cupsImageReadGIF(img, fp, primary, secondary, saturation, hue,
359 lut);
360 else if (!memcmp(header, "BM", 2))
361 status = _cupsImageReadBMP(img, fp, primary, secondary, saturation, hue,
362 lut);
363 else if (header[0] == 0x01 && header[1] == 0xda)
364 status = _cupsImageReadSGI(img, fp, primary, secondary, saturation, hue,
365 lut);
366 else if (header[0] == 0x59 && header[1] == 0xa6 &&
367 header[2] == 0x6a && header[3] == 0x95)
368 status = _cupsImageReadSunRaster(img, fp, primary, secondary, saturation,
369 hue, lut);
370 else if (header[0] == 'P' && header[1] >= '1' && header[1] <= '6')
371 status = _cupsImageReadPNM(img, fp, primary, secondary, saturation, hue,
372 lut);
373 else if (!memcmp(header2, "PCD_IPI", 7))
374 status = _cupsImageReadPhotoCD(img, fp, primary, secondary, saturation,
375 hue, lut);
376 else if (!memcmp(header + 8, "\000\010", 2) ||
377 !memcmp(header + 8, "\000\030", 2))
378 status = _cupsImageReadPIX(img, fp, primary, secondary, saturation, hue,
379 lut);
380 #if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ)
381 else if (!memcmp(header, "\211PNG", 4))
382 status = _cupsImageReadPNG(img, fp, primary, secondary, saturation, hue,
383 lut);
384 #endif /* HAVE_LIBPNG && HAVE_LIBZ */
385 #ifdef HAVE_LIBJPEG
386 else if (!memcmp(header, "\377\330\377", 3) && /* Start-of-Image */
387 header[3] >= 0xe0 && header[3] <= 0xef) /* APPn */
388 status = _cupsImageReadJPEG(img, fp, primary, secondary, saturation, hue,
389 lut);
390 #endif /* HAVE_LIBJPEG */
391 #ifdef HAVE_LIBTIFF
392 else if (!memcmp(header, "MM\000\052", 4) ||
393 !memcmp(header, "II\052\000", 4))
394 status = _cupsImageReadTIFF(img, fp, primary, secondary, saturation, hue,
395 lut);
396 #endif /* HAVE_LIBTIFF */
397 else
398 {
399 fclose(fp);
400 status = -1;
401 }
402
403 if (status)
404 {
405 free(img);
406 return (NULL);
407 }
408 else
409 return (img);
410 }
411
412
413 /*
414 * '_cupsImagePutCol()' - Put a column of pixels to an image.
415 */
416
417 int /* O - -1 on error, 0 on success */
418 _cupsImagePutCol(
419 cups_image_t *img, /* I - Image */
420 int x, /* I - Column */
421 int y, /* I - Start row */
422 int height, /* I - Column height */
423 const cups_ib_t *pixels) /* I - Pixels to put */
424 {
425 int bpp, /* Bytes per pixel */
426 twidth, /* Width of tile */
427 count; /* Number of pixels to put */
428 int tilex, /* Column within tile */
429 tiley; /* Row within tile */
430 cups_ib_t *ib; /* Pointer to pixels in tile */
431
432
433 if (img == NULL || x < 0 || x >= img->xsize || y >= img->ysize)
434 return (-1);
435
436 if (y < 0)
437 {
438 height += y;
439 y = 0;
440 }
441
442 if ((y + height) > img->ysize)
443 height = img->ysize - y;
444
445 if (height < 1)
446 return (-1);
447
448 bpp = cupsImageGetDepth(img);
449 twidth = bpp * (CUPS_TILE_SIZE - 1);
450 tilex = x / CUPS_TILE_SIZE;
451 tiley = y / CUPS_TILE_SIZE;
452
453 while (height > 0)
454 {
455 ib = get_tile(img, x, y);
456
457 if (ib == NULL)
458 return (-1);
459
460 img->tiles[tiley][tilex].dirty = 1;
461 tiley ++;
462
463 count = CUPS_TILE_SIZE - (y & (CUPS_TILE_SIZE - 1));
464 if (count > height)
465 count = height;
466
467 y += count;
468 height -= count;
469
470 for (; count > 0; count --, ib += twidth)
471 switch (bpp)
472 {
473 case 4 :
474 *ib++ = *pixels++;
475 case 3 :
476 *ib++ = *pixels++;
477 *ib++ = *pixels++;
478 case 1 :
479 *ib++ = *pixels++;
480 break;
481 }
482 }
483
484 return (0);
485 }
486
487
488 /*
489 * '_cupsImagePutRow()' - Put a row of pixels to an image.
490 */
491
492 int /* O - -1 on error, 0 on success */
493 _cupsImagePutRow(
494 cups_image_t *img, /* I - Image */
495 int x, /* I - Start column */
496 int y, /* I - Row */
497 int width, /* I - Row width */
498 const cups_ib_t *pixels) /* I - Pixel data */
499 {
500 int bpp, /* Bytes per pixel */
501 count; /* Number of pixels to put */
502 int tilex, /* Column within tile */
503 tiley; /* Row within tile */
504 cups_ib_t *ib; /* Pointer to pixels in tile */
505
506
507 if (img == NULL || y < 0 || y >= img->ysize || x >= img->xsize)
508 return (-1);
509
510 if (x < 0)
511 {
512 width += x;
513 x = 0;
514 }
515
516 if ((x + width) > img->xsize)
517 width = img->xsize - x;
518
519 if (width < 1)
520 return (-1);
521
522 bpp = img->colorspace < 0 ? -img->colorspace : img->colorspace;
523 tilex = x / CUPS_TILE_SIZE;
524 tiley = y / CUPS_TILE_SIZE;
525
526 while (width > 0)
527 {
528 ib = get_tile(img, x, y);
529
530 if (ib == NULL)
531 return (-1);
532
533 img->tiles[tiley][tilex].dirty = 1;
534
535 count = CUPS_TILE_SIZE - (x & (CUPS_TILE_SIZE - 1));
536 if (count > width)
537 count = width;
538 memcpy(ib, pixels, count * bpp);
539 pixels += count * bpp;
540 x += count;
541 width -= count;
542 tilex ++;
543 }
544
545 return (0);
546 }
547
548
549 /*
550 * 'cupsImageSetMaxTiles()' - Set the maximum number of tiles to cache.
551 *
552 * If the "max_tiles" argument is 0 then the maximum number of tiles is
553 * computed from the image size or the RIP_CACHE environment variable.
554 */
555
556 void
557 cupsImageSetMaxTiles(
558 cups_image_t *img, /* I - Image to set */
559 int max_tiles) /* I - Number of tiles to cache */
560 {
561 int cache_size, /* Size of tile cache in bytes */
562 min_tiles, /* Minimum number of tiles to cache */
563 max_size; /* Maximum cache size in bytes */
564 char *cache_env, /* Cache size environment variable */
565 cache_units[255]; /* Cache size units */
566
567
568 min_tiles = max(CUPS_TILE_MINIMUM,
569 1 + max((img->xsize + CUPS_TILE_SIZE - 1) / CUPS_TILE_SIZE,
570 (img->ysize + CUPS_TILE_SIZE - 1) / CUPS_TILE_SIZE));
571
572 if (max_tiles == 0)
573 max_tiles = ((img->xsize + CUPS_TILE_SIZE - 1) / CUPS_TILE_SIZE) *
574 ((img->ysize + CUPS_TILE_SIZE - 1) / CUPS_TILE_SIZE);
575
576 cache_size = max_tiles * CUPS_TILE_SIZE * CUPS_TILE_SIZE *
577 cupsImageGetDepth(img);
578
579 if ((cache_env = getenv("RIP_MAX_CACHE")) != NULL)
580 {
581 switch (sscanf(cache_env, "%d%254s", &max_size, cache_units))
582 {
583 case 0 :
584 max_size = 32 * 1024 * 1024;
585 break;
586 case 1 :
587 max_size *= 4 * CUPS_TILE_SIZE * CUPS_TILE_SIZE;
588 break;
589 case 2 :
590 if (tolower(cache_units[0] & 255) == 'g')
591 max_size *= 1024 * 1024 * 1024;
592 else if (tolower(cache_units[0] & 255) == 'm')
593 max_size *= 1024 * 1024;
594 else if (tolower(cache_units[0] & 255) == 'k')
595 max_size *= 1024;
596 else if (tolower(cache_units[0] & 255) == 't')
597 max_size *= 4 * CUPS_TILE_SIZE * CUPS_TILE_SIZE;
598 break;
599 }
600 }
601 else
602 max_size = 32 * 1024 * 1024;
603
604 if (cache_size > max_size)
605 max_tiles = max_size / CUPS_TILE_SIZE / CUPS_TILE_SIZE /
606 cupsImageGetDepth(img);
607
608 if (max_tiles < min_tiles)
609 max_tiles = min_tiles;
610
611 img->max_ics = max_tiles;
612
613 DEBUG_printf(("max_ics=%d...\n", img->max_ics));
614 }
615
616
617 /*
618 * 'flush_tile()' - Flush the least-recently-used tile in the cache.
619 */
620
621 static void
622 flush_tile(cups_image_t *img) /* I - Image */
623 {
624 int bpp; /* Bytes per pixel */
625 cups_itile_t *tile; /* Pointer to tile */
626
627
628 bpp = cupsImageGetDepth(img);
629 tile = img->first->tile;
630
631 if (!tile->dirty)
632 {
633 tile->ic = NULL;
634 return;
635 }
636
637 if (img->cachefile < 0)
638 {
639 if ((img->cachefile = cupsTempFd(img->cachename,
640 sizeof(img->cachename))) < 0)
641 {
642 tile->ic = NULL;
643 tile->dirty = 0;
644 return;
645 }
646
647 DEBUG_printf(("Created swap file \"%s\"...\n", img->cachename));
648 }
649
650 if (tile->pos >= 0)
651 {
652 if (lseek(img->cachefile, tile->pos, SEEK_SET) != tile->pos)
653 {
654 tile->ic = NULL;
655 tile->dirty = 0;
656 return;
657 }
658 }
659 else
660 {
661 if ((tile->pos = lseek(img->cachefile, 0, SEEK_END)) < 0)
662 {
663 tile->ic = NULL;
664 tile->dirty = 0;
665 return;
666 }
667 }
668
669 write(img->cachefile, tile->ic->pixels, bpp * CUPS_TILE_SIZE * CUPS_TILE_SIZE);
670
671 tile->ic = NULL;
672 tile->dirty = 0;
673 }
674
675
676 /*
677 * 'get_tile()' - Get a cached tile.
678 */
679
680 static cups_ib_t * /* O - Pointer to tile or NULL */
681 get_tile(cups_image_t *img, /* I - Image */
682 int x, /* I - Column in image */
683 int y) /* I - Row in image */
684 {
685 int bpp, /* Bytes per pixel */
686 tilex, /* Column within tile */
687 tiley, /* Row within tile */
688 xtiles, /* Number of tiles horizontally */
689 ytiles; /* Number of tiles vertically */
690 cups_ic_t *ic; /* Cache pointer */
691 cups_itile_t *tile; /* Tile pointer */
692
693
694 if (img->tiles == NULL)
695 {
696 xtiles = (img->xsize + CUPS_TILE_SIZE - 1) / CUPS_TILE_SIZE;
697 ytiles = (img->ysize + CUPS_TILE_SIZE - 1) / CUPS_TILE_SIZE;
698
699 DEBUG_printf(("Creating tile array (%dx%d)\n", xtiles, ytiles));
700
701 if ((img->tiles = calloc(sizeof(cups_itile_t *), ytiles)) == NULL)
702 return (NULL);
703
704 if ((tile = calloc(xtiles * sizeof(cups_itile_t), ytiles)) == NULL)
705 return (NULL);
706
707 for (tiley = 0; tiley < ytiles; tiley ++)
708 {
709 img->tiles[tiley] = tile;
710 for (tilex = xtiles; tilex > 0; tilex --, tile ++)
711 tile->pos = -1;
712 }
713 }
714
715 bpp = cupsImageGetDepth(img);
716 tilex = x / CUPS_TILE_SIZE;
717 tiley = y / CUPS_TILE_SIZE;
718 tile = img->tiles[tiley] + tilex;
719 x &= (CUPS_TILE_SIZE - 1);
720 y &= (CUPS_TILE_SIZE - 1);
721
722 if ((ic = tile->ic) == NULL)
723 {
724 if (img->num_ics < img->max_ics)
725 {
726 if ((ic = calloc(sizeof(cups_ic_t) +
727 bpp * CUPS_TILE_SIZE * CUPS_TILE_SIZE, 1)) == NULL)
728 {
729 if (img->num_ics == 0)
730 return (NULL);
731
732 flush_tile(img);
733 ic = img->first;
734 }
735 else
736 {
737 ic->pixels = ((cups_ib_t *)ic) + sizeof(cups_ic_t);
738
739 img->num_ics ++;
740
741 DEBUG_printf(("Allocated cache tile %d (%p)...\n", img->num_ics, ic));
742 }
743 }
744 else
745 {
746 DEBUG_printf(("Flushing old cache tile (%p)...\n", img->first));
747
748 flush_tile(img);
749 ic = img->first;
750 }
751
752 ic->tile = tile;
753 tile->ic = ic;
754
755 if (tile->pos >= 0)
756 {
757 DEBUG_printf(("Loading cache tile from file position " CUPS_LLFMT "...\n",
758 CUPS_LLCAST tile->pos));
759
760 lseek(img->cachefile, tile->pos, SEEK_SET);
761 read(img->cachefile, ic->pixels, bpp * CUPS_TILE_SIZE * CUPS_TILE_SIZE);
762 }
763 else
764 {
765 DEBUG_puts("Clearing cache tile...");
766
767 memset(ic->pixels, 0, bpp * CUPS_TILE_SIZE * CUPS_TILE_SIZE);
768 }
769 }
770
771 if (ic == img->first)
772 {
773 if (ic->next != NULL)
774 ic->next->prev = NULL;
775
776 img->first = ic->next;
777 ic->next = NULL;
778 ic->prev = NULL;
779 }
780 else if (img->first == NULL)
781 img->first = ic;
782
783 if (ic != img->last)
784 {
785 /*
786 * Remove the cache entry from the list...
787 */
788
789 if (ic->prev != NULL)
790 ic->prev->next = ic->next;
791 if (ic->next != NULL)
792 ic->next->prev = ic->prev;
793
794 /*
795 * And add it to the end...
796 */
797
798 if (img->last != NULL)
799 img->last->next = ic;
800
801 ic->prev = img->last;
802 img->last = ic;
803 }
804
805 ic->next = NULL;
806
807 return (ic->pixels + bpp * (y * CUPS_TILE_SIZE + x));
808 }
809
810
811 /*
812 * End of "$Id: image.c 7473 2008-04-21 17:51:58Z mike $".
813 */