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 /* SGI raster file driver */
8 #include "gdevprn.h"
9 #include "gdevsgi.h"
10 
11 #define X_DPI 72
12 #define Y_DPI 72
13 
14 #define sgi_prn_device(procs, dev_name, num_comp, depth, max_gray, max_color, print_page)\
15 {prn_device_body(gx_device_printer, procs, dev_name, \
16                  DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI, \
17                  0, 0, 0, 0, \
18                  num_comp, depth, max_gray, max_color, max_gray+1, max_color+1, \
19                  print_page)}
20 
21 static dev_proc_map_rgb_color(sgi_map_rgb_color);
22 static dev_proc_map_color_rgb(sgi_map_color_rgb);
23 
24 static dev_proc_print_page(sgi_print_page);
25 
26 static gx_device_procs sgi_procs =
27   prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
28                   sgi_map_rgb_color, sgi_map_color_rgb);
29 
30 const gx_device_printer far_data gs_sgirgb_device =
31   sgi_prn_device(sgi_procs, "sgirgb", 3, 24, 255, 255, sgi_print_page);
32 
33 static gx_color_index
sgi_map_rgb_color(gx_device * dev,const ushort cv[])34 sgi_map_rgb_color(gx_device * dev, const ushort cv[])
35 {      ushort bitspercolor = dev->color_info.depth / 3;
36        ulong max_value = (1 << bitspercolor) - 1;
37        ushort red, green, blue;
38        red = cv[0]; green = cv[1]; blue = cv[2];
39 
40        return ((red*max_value / gx_max_color_value) << (bitspercolor * 2)) +
41               ((green*max_value / gx_max_color_value) << bitspercolor) +
42               (blue*max_value / gx_max_color_value);
43 }
44 
45 static int
sgi_map_color_rgb(gx_device * dev,gx_color_index color,ushort prgb[3])46 sgi_map_color_rgb(gx_device *dev, gx_color_index color, ushort prgb[3])
47 {	ushort bitspercolor = dev->color_info.depth / 3;
48         ushort colormask = (1 << bitspercolor) - 1;
49 
50         prgb[0] = (ushort)(((color >> (bitspercolor * 2)) & colormask) *
51                 (ulong)gx_max_color_value / colormask);
52         prgb[1] = (ushort)(((color >> bitspercolor) & colormask) *
53                 (ulong)gx_max_color_value / colormask);
54         prgb[2] = (ushort)((color & colormask) *
55                 (ulong)gx_max_color_value / colormask);
56         return 0;
57 }
58 
59 typedef struct sgi_cursor_s {
60   gx_device_printer *dev;
61   int bpp;
62   uint line_size;
63   byte *data;
64   int lnum;
65 } sgi_cursor;
66 
67 /* write a short int to disk as big-endean */
putshort(unsigned int val,FILE * outf)68 static int putshort(unsigned int val, FILE *outf)
69 {
70         unsigned char buf[2];
71 
72         buf[0] = (val>>8);
73         buf[1] = val;
74         return fwrite(buf,2,1,outf);
75 }
76 
77 /* write an int to disk as big-endean */
putint(unsigned int val,FILE * outf)78 static int putint(unsigned int val, FILE *outf)
79 {
80         unsigned char buf[4];
81 
82         buf[0] = (val>>24);
83         buf[1] = (val>>16);
84         buf[2] = (val>>8);
85         buf[3] = val;
86         return fwrite(buf,4,1,outf);
87 }
88 
89 /* write the header field by field in big-endian */
putheader(IMAGE * header,FILE * outf)90 static void putheader(IMAGE *header, FILE *outf)
91 {
92         int i;
93         char filler= '\0';
94 
95         putshort(header->imagic, outf);
96         fputc(1, outf); /* RLE */
97         fputc(1, outf); /* bpp */
98         putshort(header->dim, outf);
99         putshort(header->xsize, outf);
100         putshort(header->ysize, outf);
101         putshort(header->zsize, outf);
102 
103         putint(header->min_color, outf);
104         putint(header->max_color, outf);
105         putint(header->wastebytes, outf);
106 
107         fwrite(header->name,80,1,outf);
108 
109         putint(header->colormap, outf);
110 
111    /* put the filler for the rest */
112     for (i=0; i<404; i++)
113         fputc(filler,outf);
114  }
115 
116 static int
sgi_begin_page(gx_device_printer * bdev,FILE * pstream,sgi_cursor * pcur)117 sgi_begin_page(gx_device_printer *bdev, FILE *pstream, sgi_cursor *pcur)
118 {
119      uint line_size;
120      byte *data;
121      IMAGE *header;
122 
123      if (bdev->PageCount >= 1 && !bdev->file_is_new) { /* support single page only */
124           emprintf(bdev->memory,
125                    "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