1 /*
2  *   SGI image file format library routines for CUPS.
3  *
4  *   Copyright 2007-2011 by Apple Inc.
5  *   Copyright 1993-2005 by Easy Software Products.
6  *
7  *   These coded instructions, statements, and computer programs are the
8  *   property of Apple Inc. and are protected by Federal copyright
9  *   law.  Distribution and use rights are outlined in the file "COPYING"
10  *   which should have been included with this file.
11  *
12  * Contents:
13  *
14  *   sgiClose()    - Close an SGI image file.
15  *   sgiGetRow()   - Get a row of image data from a file.
16  *   sgiOpen()     - Open an SGI image file for reading or writing.
17  *   sgiOpenFile() - Open an SGI image file for reading or writing.
18  *   sgiPutRow()   - Put a row of image data to a file.
19  *   getlong()     - Get a 32-bit big-endian integer.
20  *   getshort()    - Get a 16-bit big-endian integer.
21  *   putlong()     - Put a 32-bit big-endian integer.
22  *   putshort()    - Put a 16-bit big-endian integer.
23  *   read_rle8()   - Read 8-bit RLE data.
24  *   read_rle16()  - Read 16-bit RLE data.
25  *   write_rle8()  - Write 8-bit RLE data.
26  *   write_rle16() - Write 16-bit RLE data.
27  */
28 
29 #include "image-sgi.h"
30 #include "image-private.h"
31 
32 
33 /*
34  * Local functions...
35  */
36 
37 static int	getlong(FILE *);
38 static int	getshort(FILE *);
39 static int	putlong(long, FILE *);
40 static int	putshort(unsigned short, FILE *);
41 static int	read_rle8(FILE *, unsigned short *, int);
42 static int	read_rle16(FILE *, unsigned short *, int);
43 static int	write_rle8(FILE *, unsigned short *, int);
44 static int	write_rle16(FILE *, unsigned short *, int);
45 
46 
47 /*
48  * 'sgiClose()' - Close an SGI image file.
49  */
50 
51 int					/* O - 0 on success, -1 on error */
sgiClose(sgi_t * sgip)52 sgiClose(sgi_t *sgip)			/* I - SGI image */
53 {
54   int	i;				/* Return status */
55   long	*offset;			/* Looping var for offset table */
56 
57 
58   if (sgip == NULL)
59     return (-1);
60 
61   if (sgip->mode == SGI_WRITE && sgip->comp != SGI_COMP_NONE)
62   {
63    /*
64     * Write the scanline offset table to the file...
65     */
66 
67     fseek(sgip->file, 512, SEEK_SET);
68 
69     for (i = sgip->ysize * sgip->zsize, offset = sgip->table[0];
70          i > 0;
71          i --, offset ++)
72       if (putlong(offset[0], sgip->file) < 0)
73         return (-1);
74 
75     for (i = sgip->ysize * sgip->zsize, offset = sgip->length[0];
76          i > 0;
77          i --, offset ++)
78       if (putlong(offset[0], sgip->file) < 0)
79         return (-1);
80   }
81 
82   if (sgip->table != NULL)
83   {
84     free(sgip->table[0]);
85     free(sgip->table);
86   }
87 
88   if (sgip->length != NULL)
89   {
90     free(sgip->length[0]);
91     free(sgip->length);
92   }
93 
94   if (sgip->comp == SGI_COMP_ARLE)
95     free(sgip->arle_row);
96 
97   i = fclose(sgip->file);
98   free(sgip);
99 
100   return (i);
101 }
102 
103 
104 /*
105  * 'sgiGetRow()' - Get a row of image data from a file.
106  */
107 
108 int					/* O - 0 on success, -1 on error */
sgiGetRow(sgi_t * sgip,unsigned short * row,int y,int z)109 sgiGetRow(sgi_t          *sgip,		/* I - SGI image */
110           unsigned short *row,		/* O - Row to read */
111           int            y,		/* I - Line to read */
112           int            z)		/* I - Channel to read */
113 {
114   int	x;				/* X coordinate */
115   long	offset;				/* File offset */
116 
117 
118   if (sgip == NULL ||
119       row == NULL ||
120       y < 0 || y >= sgip->ysize ||
121       z < 0 || z >= sgip->zsize)
122     return (-1);
123 
124   switch (sgip->comp)
125   {
126     case SGI_COMP_NONE :
127        /*
128         * Seek to the image row - optimize buffering by only seeking if
129         * necessary...
130         */
131 
132         offset = 512 + (y + z * sgip->ysize) * sgip->xsize * sgip->bpp;
133         if (offset != ftell(sgip->file))
134           fseek(sgip->file, offset, SEEK_SET);
135 
136         if (sgip->bpp == 1)
137         {
138           for (x = sgip->xsize; x > 0; x --, row ++)
139             *row = getc(sgip->file);
140         }
141         else
142         {
143           for (x = sgip->xsize; x > 0; x --, row ++)
144             *row = getshort(sgip->file);
145         }
146         break;
147 
148     case SGI_COMP_RLE :
149         offset = sgip->table[z][y];
150         if (offset != ftell(sgip->file))
151           fseek(sgip->file, offset, SEEK_SET);
152 
153         if (sgip->bpp == 1)
154           return (read_rle8(sgip->file, row, sgip->xsize));
155         else
156           return (read_rle16(sgip->file, row, sgip->xsize));
157   }
158 
159   return (0);
160 }
161 
162 
163 /*
164  * 'sgiOpen()' - Open an SGI image file for reading or writing.
165  */
166 
167 sgi_t *					/* O - New image */
sgiOpen(const char * filename,int mode,int comp,int bpp,int xsize,int ysize,int zsize)168 sgiOpen(const char *filename,		/* I - File to open */
169         int        mode,		/* I - Open mode (SGI_READ or SGI_WRITE) */
170         int        comp,		/* I - Type of compression */
171         int        bpp,			/* I - Bytes per pixel */
172         int        xsize,		/* I - Width of image in pixels */
173         int        ysize,		/* I - Height of image in pixels */
174         int        zsize)		/* I - Number of channels */
175 {
176   sgi_t	*sgip;				/* New SGI image file */
177   FILE	*file;				/* Image file pointer */
178 
179 
180   if (mode == SGI_READ)
181     file = fopen(filename, "rb");
182   else
183     file = fopen(filename, "wb+");
184 
185   if (file == NULL)
186     return (NULL);
187 
188   if ((sgip = sgiOpenFile(file, mode, comp, bpp, xsize, ysize, zsize)) == NULL)
189     fclose(file);
190 
191   return (sgip);
192 }
193 
194 
195 /*
196  * 'sgiOpenFile()' - Open an SGI image file for reading or writing.
197  */
198 
199 sgi_t *					/* O - New image */
sgiOpenFile(FILE * file,int mode,int comp,int bpp,int xsize,int ysize,int zsize)200 sgiOpenFile(FILE *file,			/* I - File to open */
201             int  mode,			/* I - Open mode (SGI_READ or SGI_WRITE) */
202             int  comp,			/* I - Type of compression */
203             int  bpp,			/* I - Bytes per pixel */
204             int  xsize,			/* I - Width of image in pixels */
205             int  ysize,			/* I - Height of image in pixels */
206             int  zsize)			/* I - Number of channels */
207 {
208   int	i, j;				/* Looping var */
209   char	name[80];			/* Name of file in image header */
210   short	magic;				/* Magic number */
211   sgi_t	*sgip;				/* New image pointer */
212 
213 
214   if ((sgip = calloc(sizeof(sgi_t), 1)) == NULL)
215     return (NULL);
216 
217   sgip->file = file;
218 
219   switch (mode)
220   {
221     case SGI_READ :
222         sgip->mode = SGI_READ;
223 
224         magic = getshort(sgip->file);
225         if (magic != SGI_MAGIC)
226         {
227           free(sgip);
228           return (NULL);
229         }
230 
231         sgip->comp  = getc(sgip->file);
232         sgip->bpp   = getc(sgip->file);
233         getshort(sgip->file);		/* Dimensions */
234         sgip->xsize = getshort(sgip->file);
235         sgip->ysize = getshort(sgip->file);
236         sgip->zsize = getshort(sgip->file);
237         getlong(sgip->file);		/* Minimum pixel */
238         getlong(sgip->file);		/* Maximum pixel */
239 
240         if (sgip->comp)
241         {
242          /*
243           * This file is compressed; read the scanline tables...
244           */
245 
246           fseek(sgip->file, 512, SEEK_SET);
247 
248           if ((sgip->table = calloc(sgip->zsize, sizeof(long *))) == NULL)
249 	  {
250 	    free(sgip);
251 	    return (NULL);
252 	  }
253 
254           if ((sgip->table[0] = calloc(sgip->ysize * sgip->zsize,
255 	                               sizeof(long))) == NULL)
256           {
257 	    free(sgip->table);
258 	    free(sgip);
259 	    return (NULL);
260 	  }
261 
262           for (i = 1; i < sgip->zsize; i ++)
263             sgip->table[i] = sgip->table[0] + i * sgip->ysize;
264 
265           for (i = 0; i < sgip->zsize; i ++)
266             for (j = 0; j < sgip->ysize; j ++)
267               sgip->table[i][j] = getlong(sgip->file);
268         }
269         break;
270 
271     case SGI_WRITE :
272 	if (xsize < 1 ||
273 	    ysize < 1 ||
274 	    zsize < 1 ||
275 	    bpp < 1 || bpp > 2 ||
276 	    comp < SGI_COMP_NONE || comp > SGI_COMP_ARLE)
277         {
278           free(sgip);
279           return (NULL);
280         }
281 
282         sgip->mode = SGI_WRITE;
283 
284         putshort(SGI_MAGIC, sgip->file);
285         putc(((sgip->comp = comp) != 0) ? '1': '0', sgip->file);
286         putc(sgip->bpp = bpp, sgip->file);
287         putshort(3, sgip->file);		/* Dimensions */
288         putshort(sgip->xsize = xsize, sgip->file);
289         putshort(sgip->ysize = ysize, sgip->file);
290         putshort(sgip->zsize = zsize, sgip->file);
291         if (bpp == 1)
292         {
293           putlong(0, sgip->file);	/* Minimum pixel */
294           putlong(255, sgip->file);	/* Maximum pixel */
295         }
296         else
297         {
298           putlong(-32768, sgip->file);	/* Minimum pixel */
299           putlong(32767, sgip->file);	/* Maximum pixel */
300         }
301         putlong(0, sgip->file);		/* Reserved */
302 
303         memset(name, 0, sizeof(name));
304         fwrite(name, sizeof(name), 1, sgip->file);
305 
306         for (i = 0; i < 102; i ++)
307           putlong(0, sgip->file);
308 
309         switch (comp)
310         {
311           case SGI_COMP_NONE : /* No compression */
312              /*
313               * This file is uncompressed.  To avoid problems with sparse files,
314               * we need to write blank pixels for the entire image...
315               */
316 
317               if (bpp == 1)
318               {
319         	for (i = xsize * ysize * zsize; i > 0; i --)
320         	  putc(0, sgip->file);
321               }
322               else
323               {
324         	for (i = xsize * ysize * zsize; i > 0; i --)
325         	  putshort(0, sgip->file);
326               }
327               break;
328 
329           case SGI_COMP_ARLE : /* Aggressive RLE */
330               sgip->arle_row    = calloc(xsize, sizeof(unsigned short));
331               sgip->arle_offset = 0;
332 
333           case SGI_COMP_RLE : /* Run-Length Encoding */
334              /*
335               * This file is compressed; write the (blank) scanline tables...
336               */
337 
338               for (i = 2 * ysize * zsize; i > 0; i --)
339         	putlong(0, sgip->file);
340 
341               sgip->firstrow = ftell(sgip->file);
342               sgip->nextrow  = ftell(sgip->file);
343               if ((sgip->table = calloc(sgip->zsize, sizeof(long *))) == NULL)
344 	      {
345 	        free(sgip);
346 		return (NULL);
347 	      }
348 
349               if ((sgip->table[0] = calloc(sgip->ysize * sgip->zsize,
350 	                                   sizeof(long))) == NULL)
351               {
352 	        free(sgip->table);
353 		free(sgip);
354 		return (NULL);
355 	      }
356 
357               for (i = 1; i < sgip->zsize; i ++)
358         	sgip->table[i] = sgip->table[0] + i * sgip->ysize;
359 
360               if ((sgip->length = calloc(sgip->zsize, sizeof(long *))) == NULL)
361 	      {
362 	        free(sgip->table);
363 		free(sgip);
364 		return (NULL);
365 	      }
366 
367               if ((sgip->length[0] = calloc(sgip->ysize * sgip->zsize,
368 	                                    sizeof(long))) == NULL)
369               {
370 	        free(sgip->length);
371 		free(sgip->table);
372 		free(sgip);
373 		return (NULL);
374 	      }
375 
376               for (i = 1; i < sgip->zsize; i ++)
377         	sgip->length[i] = sgip->length[0] + i * sgip->ysize;
378               break;
379         }
380         break;
381 
382     default :
383         free(sgip);
384         return (NULL);
385   }
386 
387   return (sgip);
388 }
389 
390 
391 /*
392  * 'sgiPutRow()' - Put a row of image data to a file.
393  */
394 
395 int					/* O - 0 on success, -1 on error */
sgiPutRow(sgi_t * sgip,unsigned short * row,int y,int z)396 sgiPutRow(sgi_t          *sgip,		/* I - SGI image */
397           unsigned short *row,		/* I - Row to write */
398           int            y,		/* I - Line to write */
399           int            z)		/* I - Channel to write */
400 {
401   int	x;				/* X coordinate */
402   long	offset;				/* File offset */
403 
404 
405   if (sgip == NULL ||
406       row == NULL ||
407       y < 0 || y >= sgip->ysize ||
408       z < 0 || z >= sgip->zsize)
409     return (-1);
410 
411   switch (sgip->comp)
412   {
413     case SGI_COMP_NONE :
414        /*
415         * Seek to the image row - optimize buffering by only seeking if
416         * necessary...
417         */
418 
419         offset = 512 + (y + z * sgip->ysize) * sgip->xsize * sgip->bpp;
420         if (offset != ftell(sgip->file))
421           fseek(sgip->file, offset, SEEK_SET);
422 
423         if (sgip->bpp == 1)
424         {
425           for (x = sgip->xsize; x > 0; x --, row ++)
426             putc(*row, sgip->file);
427         }
428         else
429         {
430           for (x = sgip->xsize; x > 0; x --, row ++)
431             putshort(*row, sgip->file);
432         }
433         break;
434 
435     case SGI_COMP_ARLE :
436         if (sgip->table[z][y] != 0)
437           return (-1);
438 
439        /*
440         * First check the last row written...
441         */
442 
443         if (sgip->arle_offset > 0)
444         {
445           for (x = 0; x < sgip->xsize; x ++)
446             if (row[x] != sgip->arle_row[x])
447               break;
448 
449           if (x == sgip->xsize)
450           {
451             sgip->table[z][y]  = sgip->arle_offset;
452             sgip->length[z][y] = sgip->arle_length;
453             return (0);
454           }
455         }
456 
457        /*
458         * If that didn't match, search all the previous rows...
459         */
460 
461         fseek(sgip->file, sgip->firstrow, SEEK_SET);
462 
463         if (sgip->bpp == 1)
464         {
465           for (;;)
466           {
467             sgip->arle_offset = ftell(sgip->file);
468             if ((sgip->arle_length = read_rle8(sgip->file, sgip->arle_row, sgip->xsize)) < 0)
469             {
470               x = 0;
471               break;
472             }
473 
474             if (memcmp(row, sgip->arle_row, sgip->xsize * sizeof(unsigned short)) == 0)
475 	    {
476 	      x = sgip->xsize;
477 	      break;
478 	    }
479           }
480         }
481         else
482         {
483           for (;;)
484           {
485             sgip->arle_offset = ftell(sgip->file);
486             if ((sgip->arle_length = read_rle16(sgip->file, sgip->arle_row, sgip->xsize)) < 0)
487             {
488               x = 0;
489               break;
490             }
491 
492             if (memcmp(row, sgip->arle_row, sgip->xsize * sizeof(unsigned short)) == 0)
493 	    {
494 	      x = sgip->xsize;
495 	      break;
496 	    }
497           }
498         }
499 
500 	if (x == sgip->xsize)
501 	{
502           sgip->table[z][y]  = sgip->arle_offset;
503           sgip->length[z][y] = sgip->arle_length;
504           return (0);
505 	}
506 	else
507 	  fseek(sgip->file, 0, SEEK_END);	/* Clear EOF */
508 
509     case SGI_COMP_RLE :
510         if (sgip->table[z][y] != 0)
511           return (-1);
512 
513         offset = sgip->table[z][y] = sgip->nextrow;
514 
515         if (offset != ftell(sgip->file))
516           fseek(sgip->file, offset, SEEK_SET);
517 
518         if (sgip->bpp == 1)
519           x = write_rle8(sgip->file, row, sgip->xsize);
520         else
521           x = write_rle16(sgip->file, row, sgip->xsize);
522 
523         if (sgip->comp == SGI_COMP_ARLE)
524         {
525           sgip->arle_offset = offset;
526           sgip->arle_length = x;
527           memcpy(sgip->arle_row, row, sgip->xsize * sizeof(unsigned short));
528         }
529 
530         sgip->nextrow      = ftell(sgip->file);
531         sgip->length[z][y] = x;
532 
533         return (x);
534   }
535 
536   return (0);
537 }
538 
539 
540 /*
541  * 'getlong()' - Get a 32-bit big-endian integer.
542  */
543 
544 static int				/* O - Long value */
getlong(FILE * fp)545 getlong(FILE *fp)			/* I - File to read from */
546 {
547   unsigned char	b[4];			/* Bytes from file */
548 
549 
550   if (fread(b, 4, 1, fp) == 0 && ferror(fp))
551     DEBUG_printf(("Error reading file!"));
552   return ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]);
553 }
554 
555 
556 /*
557  * 'getshort()' - Get a 16-bit big-endian integer.
558  */
559 
560 static int				/* O - Short value */
getshort(FILE * fp)561 getshort(FILE *fp)			/* I - File to read from */
562 {
563   unsigned char	b[2];			/* Bytes from file */
564 
565 
566   if (fread(b, 2, 1, fp) == 0 && ferror(fp))
567     DEBUG_printf(("Error reading file!"));
568   return ((b[0] << 8) | b[1]);
569 }
570 
571 
572 /*
573  * 'putlong()' - Put a 32-bit big-endian integer.
574  */
575 
576 static int				/* O - 0 on success, -1 on error */
putlong(long n,FILE * fp)577 putlong(long n,				/* I - Long to write */
578         FILE *fp)			/* I - File to write to */
579 {
580   if (putc(n >> 24, fp) == EOF)
581     return (EOF);
582   if (putc(n >> 16, fp) == EOF)
583     return (EOF);
584   if (putc(n >> 8, fp) == EOF)
585     return (EOF);
586   if (putc(n, fp) == EOF)
587     return (EOF);
588   else
589     return (0);
590 }
591 
592 
593 /*
594  * 'putshort()' - Put a 16-bit big-endian integer.
595  */
596 
597 static int				/* O - 0 on success, -1 on error */
putshort(unsigned short n,FILE * fp)598 putshort(unsigned short n,		/* I - Short to write */
599          FILE           *fp)		/* I - File to write to */
600 {
601   if (putc(n >> 8, fp) == EOF)
602     return (EOF);
603   if (putc(n, fp) == EOF)
604     return (EOF);
605   else
606     return (0);
607 }
608 
609 
610 /*
611  * 'read_rle8()' - Read 8-bit RLE data.
612  */
613 
614 static int				/* O - Value on success, -1 on error */
read_rle8(FILE * fp,unsigned short * row,int xsize)615 read_rle8(FILE           *fp,		/* I - File to read from */
616           unsigned short *row,		/* O - Data */
617           int            xsize)		/* I - Width of data in pixels */
618 {
619   int	i,				/* Looping var */
620 	ch,				/* Current character */
621 	count,				/* RLE count */
622 	length;				/* Number of bytes read... */
623 
624 
625   length = 0;
626 
627   while (xsize > 0)
628   {
629     if ((ch = getc(fp)) == EOF)
630       return (-1);
631     length ++;
632 
633     count = ch & 127;
634     if (count == 0)
635       break;
636 
637     if (ch & 128)
638     {
639       for (i = 0; i < count; i ++, row ++, xsize --, length ++)
640         if (xsize > 0)
641 	  *row = getc(fp);
642     }
643     else
644     {
645       ch = getc(fp);
646       length ++;
647       for (i = 0; i < count && xsize > 0; i ++, row ++, xsize --)
648         *row = ch;
649     }
650   }
651 
652   return (xsize > 0 ? -1 : length);
653 }
654 
655 
656 /*
657  * 'read_rle16()' - Read 16-bit RLE data.
658  */
659 
660 static int				/* O - Value on success, -1 on error */
read_rle16(FILE * fp,unsigned short * row,int xsize)661 read_rle16(FILE           *fp,		/* I - File to read from */
662            unsigned short *row,		/* O - Data */
663            int            xsize)	/* I - Width of data in pixels */
664 {
665   int	i,				/* Looping var */
666 	ch,				/* Current character */
667 	count,				/* RLE count */
668 	length;				/* Number of bytes read... */
669 
670 
671   length = 0;
672 
673   while (xsize > 0)
674   {
675     if ((ch = getshort(fp)) == EOF)
676       return (-1);
677     length ++;
678 
679     count = ch & 127;
680     if (count == 0)
681       break;
682 
683     if (ch & 128)
684     {
685       for (i = 0; i < count; i ++, row ++, xsize --, length ++)
686         if (xsize > 0)
687 	  *row = getshort(fp);
688     }
689     else
690     {
691       ch = getshort(fp);
692       length ++;
693       for (i = 0; i < count && xsize > 0; i ++, row ++, xsize --)
694 	*row = ch;
695     }
696   }
697 
698   return (xsize > 0 ? -1 : length * 2);
699 }
700 
701 
702 /*
703  * 'write_rle8()' - Write 8-bit RLE data.
704  */
705 
706 static int				/* O - Length on success, -1 on error */
write_rle8(FILE * fp,unsigned short * row,int xsize)707 write_rle8(FILE           *fp,		/* I - File to write to */
708            unsigned short *row,		/* I - Data */
709            int            xsize)	/* I - Width of data in pixels */
710 {
711   int			length,		/* Length in bytes */
712 			count,		/* Number of repeating pixels */
713 			i,		/* Looping var */
714 			x;		/* Current column */
715   unsigned short	*start,		/* Start of current sequence */
716 			repeat;		/* Repeated pixel */
717 
718 
719   for (x = xsize, length = 0; x > 0;)
720   {
721     start = row;
722     row   += 2;
723     x     -= 2;
724 
725     while (x > 0 && (row[-2] != row[-1] || row[-1] != row[0]))
726     {
727       row ++;
728       x --;
729     }
730 
731     row -= 2;
732     x   += 2;
733 
734     count = row - start;
735     while (count > 0)
736     {
737       i     = count > 126 ? 126 : count;
738       count -= i;
739 
740       if (putc(128 | i, fp) == EOF)
741         return (-1);
742       length ++;
743 
744       while (i > 0)
745       {
746 	if (putc(*start, fp) == EOF)
747           return (-1);
748         start ++;
749         i --;
750         length ++;
751       }
752     }
753 
754     if (x <= 0)
755       break;
756 
757     start  = row;
758     repeat = row[0];
759 
760     row ++;
761     x --;
762 
763     while (x > 0 && *row == repeat)
764     {
765       row ++;
766       x --;
767     }
768 
769     count = row - start;
770     while (count > 0)
771     {
772       i     = count > 126 ? 126 : count;
773       count -= i;
774 
775       if (putc(i, fp) == EOF)
776         return (-1);
777       length ++;
778 
779       if (putc(repeat, fp) == EOF)
780         return (-1);
781       length ++;
782     }
783   }
784 
785   length ++;
786 
787   if (putc(0, fp) == EOF)
788     return (-1);
789   else
790     return (length);
791 }
792 
793 
794 /*
795  * 'write_rle16()' - Write 16-bit RLE data.
796  */
797 
798 static int				/* O - Length in words */
write_rle16(FILE * fp,unsigned short * row,int xsize)799 write_rle16(FILE           *fp,		/* I - File to write to */
800             unsigned short *row,	/* I - Data */
801             int            xsize)	/* I - Width of data in pixels */
802 {
803   int			length,		/* Length in words */
804 			count,		/* Number of repeating pixels */
805 			i,		/* Looping var */
806 			x;		/* Current column */
807   unsigned short	*start,		/* Start of current sequence */
808 			repeat;		/* Repeated pixel */
809 
810 
811   for (x = xsize, length = 0; x > 0;)
812   {
813     start = row;
814     row   += 2;
815     x     -= 2;
816 
817     while (x > 0 && (row[-2] != row[-1] || row[-1] != row[0]))
818     {
819       row ++;
820       x --;
821     }
822 
823     row -= 2;
824     x   += 2;
825 
826     count = row - start;
827     while (count > 0)
828     {
829       i     = count > 126 ? 126 : count;
830       count -= i;
831 
832       if (putshort(128 | i, fp) == EOF)
833         return (-1);
834       length ++;
835 
836       while (i > 0)
837       {
838 	if (putshort(*start, fp) == EOF)
839           return (-1);
840         start ++;
841         i --;
842         length ++;
843       }
844     }
845 
846     if (x <= 0)
847       break;
848 
849     start  = row;
850     repeat = row[0];
851 
852     row ++;
853     x --;
854 
855     while (x > 0 && *row == repeat)
856     {
857       row ++;
858       x --;
859     }
860 
861     count = row - start;
862     while (count > 0)
863     {
864       i     = count > 126 ? 126 : count;
865       count -= i;
866 
867       if (putshort(i, fp) == EOF)
868         return (-1);
869       length ++;
870 
871       if (putshort(repeat, fp) == EOF)
872         return (-1);
873       length ++;
874     }
875   }
876 
877   length ++;
878 
879   if (putshort(0, fp) == EOF)
880     return (-1);
881   else
882     return (2 * length);
883 }
884 
885