1 /*
2  * This file is distributed with Ghostscript, but its author,
3  * Tanmoy Bhattacharya (tanmoy@qcd.lanl.gov) hereby places it in the
4  * public domain.
5  */
6 
7 /* $Id: gdevsgi.c 9490 2009-02-19 17:13:36Z ray $*/
8 /* SGI raster file driver */
9 #include "gdevprn.h"
10 #include "gdevsgi.h"
11 
12 #define X_DPI 72
13 #define Y_DPI 72
14 
15 #define sgi_prn_device(procs, dev_name, num_comp, depth, max_gray, max_color, print_page)\
16 {prn_device_body(gx_device_printer, procs, dev_name, \
17 		 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI, \
18 		 0, 0, 0, 0, \
19 		 num_comp, depth, max_gray, max_color, max_gray+1, max_color+1, \
20 		 print_page)}
21 
22 static dev_proc_map_rgb_color(sgi_map_rgb_color);
23 static dev_proc_map_color_rgb(sgi_map_color_rgb);
24 
25 static dev_proc_print_page(sgi_print_page);
26 
27 static gx_device_procs sgi_procs =
28   prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
29 		  sgi_map_rgb_color, sgi_map_color_rgb);
30 
31 const gx_device_printer far_data gs_sgirgb_device =
32   sgi_prn_device(sgi_procs, "sgirgb", 3, 24, 255, 255, sgi_print_page);
33 
34 static gx_color_index
sgi_map_rgb_color(gx_device * dev,const ushort cv[])35 sgi_map_rgb_color(gx_device * dev, const ushort cv[])
36 {      ushort bitspercolor = dev->color_info.depth / 3;
37        ulong max_value = (1 << bitspercolor) - 1;
38        ushort red, green, blue;
39        red = cv[0]; green = cv[1]; blue = cv[2];
40 
41        return ((red*max_value / gx_max_color_value) << (bitspercolor * 2)) +
42 	      ((green*max_value / gx_max_color_value) << bitspercolor) +
43 	      (blue*max_value / gx_max_color_value);
44 }
45 
46 static int
sgi_map_color_rgb(gx_device * dev,gx_color_index color,ushort prgb[3])47 sgi_map_color_rgb(gx_device *dev, gx_color_index color, ushort prgb[3])
48 {	ushort bitspercolor = dev->color_info.depth / 3;
49 	ushort colormask = (1 << bitspercolor) - 1;
50 
51 	prgb[0] = (ushort)(((color >> (bitspercolor * 2)) & colormask) *
52 		(ulong)gx_max_color_value / colormask);
53 	prgb[1] = (ushort)(((color >> bitspercolor) & colormask) *
54 		(ulong)gx_max_color_value / colormask);
55 	prgb[2] = (ushort)((color & colormask) *
56 		(ulong)gx_max_color_value / colormask);
57 	return 0;
58 }
59 
60 typedef struct sgi_cursor_s {
61   gx_device_printer *dev;
62   int bpp;
63   uint line_size;
64   byte *data;
65   int lnum;
66 } sgi_cursor;
67 
68 /* write a short int to disk as big-endean */
putshort(unsigned int val,FILE * outf)69 static int putshort(unsigned int val, FILE *outf)
70 {
71 	unsigned char buf[2];
72 
73 	buf[0] = (val>>8);
74 	buf[1] = val;
75 	return fwrite(buf,2,1,outf);
76 }
77 
78 /* write an int to disk as big-endean */
putint(unsigned int val,FILE * outf)79 static int putint(unsigned int val, FILE *outf)
80 {
81 	unsigned char buf[4];
82 
83 	buf[0] = (val>>24);
84 	buf[1] = (val>>16);
85 	buf[2] = (val>>8);
86 	buf[3] = val;
87 	return fwrite(buf,4,1,outf);
88 }
89 
90 /* write the header field by field in big-endian */
putheader(IMAGE * header,FILE * outf)91 static void putheader(IMAGE *header, FILE *outf)
92 {
93 	int i;
94 	char filler= '\0';
95 
96 	putshort(header->imagic, outf);
97 	fputc(1, outf); /* RLE */
98 	fputc(1, outf); /* bpp */
99 	putshort(header->dim, outf);
100 	putshort(header->xsize, outf);
101 	putshort(header->ysize, outf);
102 	putshort(header->zsize, outf);
103 
104 	putint(header->min_color, outf);
105 	putint(header->max_color, outf);
106 	putint(header->wastebytes, outf);
107 
108 	fwrite(header->name,80,1,outf);
109 
110 	putint(header->colormap, outf);
111 
112    /* put the filler for the rest */
113     for (i=0; i<404; i++)
114         fputc(filler,outf);
115  }
116 
117 static int
sgi_begin_page(gx_device_printer * bdev,FILE * pstream,sgi_cursor * pcur)118 sgi_begin_page(gx_device_printer *bdev, FILE *pstream, sgi_cursor *pcur)
119 {
120      uint line_size;
121      byte *data;
122      IMAGE *header;
123 
124      if (bdev->PageCount >= 1 && !bdev->file_is_new) { /* support single page only */
125           eprintf("sgi rgb format only supports one page per file.\n"
126                   "Please use the '%%d' OutputFile option to create one file for each page.\n");
127 	  return_error(gs_error_rangecheck);
128      }
129      line_size = gdev_mem_bytes_per_scan_line((gx_device_printer*)bdev);
130      data = (byte*)gs_malloc(bdev->memory, line_size, 1, "sgi_begin_page");
131      header= (IMAGE*)gs_malloc(bdev->memory, sizeof(IMAGE),1,"sgi_begin_page");
132 
133      if ((data == (byte*)0)||(header == (IMAGE*)0)) {
134         gs_free(bdev->memory, data, line_size, 1, "sgi_begin_page");
135         gs_free(bdev->memory, header, sizeof(IMAGE),1,"sgi_begin_page");
136 	return_error(gs_error_VMerror);
137      }
138      memset(header,0, sizeof(IMAGE));
139      header->imagic = IMAGIC;
140      header->type = RLE(1);
141      header->dim = 3;
142      header->xsize=bdev->width;
143      header->ysize=bdev->height;
144      header->zsize=3;
145      header->min_color  = 0;
146      header->max_color  = bdev->color_info.max_color;
147      header->wastebytes = 0;
148      strncpy(header->name,"gs picture",80);
149      header->colormap = CM_NORMAL;
150      header->dorev=0;
151      putheader(header,pstream);
152      pcur->dev = bdev;
153      pcur->bpp = bdev->color_info.depth;
154      pcur->line_size = line_size;
155      pcur->data = data;
156      return 0;
157 }
158 
159 static int
sgi_next_row(sgi_cursor * pcur)160 sgi_next_row(sgi_cursor *pcur)
161 {    if (pcur->lnum < 0)
162        return 1;
163      gdev_prn_copy_scan_lines((gx_device_printer*)pcur->dev,
164 			      pcur->lnum--, pcur->data, pcur->line_size);
165      return 0;
166 }
167 
168 #define bdev ((gx_device_printer *)pdev)
169 
170 static int
sgi_print_page(gx_device_printer * pdev,FILE * pstream)171 sgi_print_page(gx_device_printer *pdev, FILE *pstream)
172 {      sgi_cursor cur;
173        int code = sgi_begin_page(bdev, pstream, &cur);
174        uint bpe, mask;
175        int separation;
176        int *rowsizes;
177        byte *edata ;
178        long lastval;
179        int rownumber;
180 
181        if (pdev->PageCount >= 1 && !pdev->file_is_new)
182 	  return_error(gs_error_rangecheck);  /* support single page only, can't happen */
183 
184 #define aref2(a,b) (a)*bdev->height+(b)
185 	   rowsizes=(int*)gs_malloc(pdev->memory, sizeof(int),3*bdev->height,"sgi_print_page");
186        edata =  (byte*)gs_malloc(pdev->memory, cur.line_size, 1, "sgi_begin_page");
187 
188        if((code<0)||(rowsizes==(int*)NULL)||(edata==(byte*)NULL)) {
189            code = gs_note_error(gs_error_VMerror);
190            goto free_mem;
191        }
192 
193        lastval = 512+4*6*bdev->height; /* skip offset table */
194        fseek(pstream,lastval,0);
195        for (separation=0; separation < 3; separation++)
196 	 {
197 	   cur.lnum = cur.dev->height-1;
198 	   rownumber = 0;
199 	   bpe = cur.bpp/3;
200 	   mask = (1<<bpe) - 1;
201 	   while ( !(code=sgi_next_row(&cur)))
202 	     { byte *bp;
203 	       uint x;
204 	       int shift;
205 	       byte *curcol=cur.data;
206 	       byte *startcol=edata;
207 	       int count;
208 	       byte todo, cc;
209 	       byte *iptr, *sptr, *optr, *ibufend;
210 	       for (bp = cur.data, x=0, shift = 8 - cur.bpp;
211 		    x < bdev->width;
212 		    )
213 		 { uint pixel = 0;
214 		   uint r, g, b;
215 		   switch (cur.bpp >> 3)
216 		     {
217 		     case 3: pixel = (uint)*bp << 16; bp++;
218 		     case 2: pixel += (uint)*bp << 8; bp++;
219 		     case 1: pixel += *bp; bp++; break;
220 		     case 0: pixel = *bp >> shift;
221 		       if ((shift-=cur.bpp) < 0)
222 			 bp++, shift += 8; break;
223 		     }
224 		   ++x;
225 		   b = pixel & mask; pixel >>= bpe;
226 		   g = pixel & mask; pixel >>= bpe;
227 		   r = pixel & mask;
228 		   switch(separation)
229 		     {
230 		     case 0: *curcol++=r; break;
231 		     case 1: *curcol++=g; break;
232 		     case 2: *curcol++=b; break;
233 		     }
234 		 }
235 	       iptr=cur.data;
236 	       optr=startcol;
237 	       ibufend=curcol-1;
238 	       while(iptr<ibufend) {
239 		 sptr = iptr;
240 		 iptr += 2;
241 		 while((iptr<ibufend)&&((iptr[-2]!=iptr[-1])||(iptr[-1]!=iptr[0])))
242 		   iptr++;
243 		 iptr -= 2;
244 		 count = iptr-sptr;
245 		 while(count) {
246 		   todo = count>126 ? 126:count;
247 		   count -= todo;
248 		   *optr++ = 0x80|todo;
249 		   while(todo--)
250 		     *optr++ = *sptr++;
251 		 }
252 		 sptr = iptr;
253 		 cc = *iptr++;
254 		 while( (iptr<ibufend) && (*iptr == cc) )
255 		   iptr++;
256 		 count = iptr-sptr;
257 		 while(count) {
258 		   todo = count>126 ? 126:count;
259 		   count -= todo;
260 		   *optr++ = todo;
261 		   *optr++ = cc;
262 		 }
263 	       }
264                *optr++ = 0;
265 	       rowsizes[aref2(separation,rownumber++)] = optr-startcol;
266 	       if (fwrite(startcol,1,optr-startcol,pstream) != optr-startcol) {
267                    code = gs_note_error(gs_error_ioerror);
268                    goto free_mem;
269                }
270 	     }
271 	 }
272        fseek(pstream,512L,0);
273        for(separation=0; separation<3; separation++)
274 	 for(rownumber=0; rownumber<bdev->height; rownumber++)
275 	   {putint(lastval,pstream);
276 	    lastval+=rowsizes[aref2(separation,rownumber)];}
277        for(separation=0; separation<3; separation++)
278 	 for(rownumber=0; rownumber<bdev->height; rownumber++)
279 	   {lastval=rowsizes[aref2(separation,rownumber)];
280 	    putint(lastval,pstream);}
281      free_mem:
282        gs_free(pdev->memory, (char*)cur.data, cur.line_size, 1,
283 		 "sgi_print_page(done)");
284        gs_free(pdev->memory, (char*)edata, cur.line_size, 1, "sgi_print_page(done)");
285        gs_free(pdev->memory, (char*)rowsizes,4,3*bdev->height,"sgi_print_page(done)");
286        return (code < 0 ? code : 0);
287 }
288