1 /*
2  * Copyright (c) 2003 Todd Kirby <slapcat@pacbell.net>
3  *
4  * This file is part of MPlayer.
5  *
6  * MPlayer is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * MPlayer is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with MPlayer; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 
24 #include "config.h"
25 #include "mp_msg.h"
26 #include "libavutil/intreadwrite.h"
27 #include "mpbswap.h"
28 #include "vd_internal.h"
29 
30 #define SGI_HEADER_LEN 512
31 #define SGI_MAGIC 474
32 
33 #define SGI_GRAYSCALE_IMAGE 1
34 #define SGI_RGB_IMAGE 3
35 #define SGI_RGBA_IMAGE 4
36 
37 #define OUT_PIXEL_STRIDE 3 /* RGB */
38 
39 
40 static const vd_info_t info =
41 {
42   "SGI Image decoder",
43   "sgi",
44   "Todd Kirby",
45   "Todd Kirby",
46   ""
47 };
48 
49 LIBVD_EXTERN(sgi)
50 
51 typedef struct {
52   short magic;
53   char rle;
54   char bytes_per_channel;
55   unsigned short dimension;
56   unsigned short xsize;
57   unsigned short ysize;
58   unsigned short zsize;
59 } SGIInfo;
60 
61 static unsigned int outfmt = IMGFMT_BGR24;
62 
63 static unsigned short last_x = -1;
64 static unsigned short last_y = -1;
65 
66 
67 /* to set/get/query special features/parameters */
68 static int
control(sh_video_t * sh,int cmd,void * arg,...)69 control(sh_video_t* sh, int cmd, void *arg, ...)
70 {
71   switch (cmd)
72   {
73     case VDCTRL_QUERY_FORMAT:
74       if (*((unsigned int *) arg) == outfmt) {
75         return CONTROL_TRUE;
76       }
77       return CONTROL_FALSE;
78   }
79   return CONTROL_UNKNOWN;
80 }
81 
82 
83 /* init driver */
84 static int
init(sh_video_t * sh)85 init(sh_video_t *sh)
86 {
87   sh->context = calloc(1, sizeof(SGIInfo));
88   last_x = -1;
89 
90   return 1;
91 }
92 
93 
94 /* uninit driver */
95 static void
uninit(sh_video_t * sh)96 uninit(sh_video_t *sh)
97 {
98   SGIInfo	*info = sh->context;
99   free(info);
100 }
101 
102 
103 /* expand an rle row into a channel */
104 static void
expandrow(unsigned char * optr,unsigned char * iptr,int chan_offset)105 expandrow(unsigned char *optr, unsigned char *iptr, int chan_offset)
106 {
107   unsigned char pixel, count;
108   optr += chan_offset;
109 
110   while (1) {
111     pixel = *iptr++;
112 
113     if (!(count = (pixel & 0x7f))) {
114       return;
115     }
116     if(pixel & 0x80) {
117       while (count--) {
118         *optr = *iptr;
119         optr += OUT_PIXEL_STRIDE;
120         iptr++;
121       }
122     } else {
123       pixel = *iptr++;
124 
125       while (count--) {
126         *optr = pixel;
127         optr += OUT_PIXEL_STRIDE;
128       }
129     }
130   }
131 }
132 
133 
134 /* expand an rle row into all 3 channels.
135    a separate function for grayscale so we don't slow down the
136    more common case rgb function with a bunch of ifs. */
137 static void
expandrow_gs(unsigned char * optr,unsigned char * iptr)138 expandrow_gs(unsigned char *optr, unsigned char *iptr)
139 {
140   unsigned char pixel, count;
141 
142   while (1) {
143     pixel = *iptr++;
144 
145     if (!(count = (pixel & 0x7f))) {
146       return;
147     }
148     if(pixel & 0x80) {
149       while (count--) {
150         optr[0] = *iptr;
151         optr[1] = *iptr;
152         optr[2] = *iptr;
153         optr += OUT_PIXEL_STRIDE;
154         iptr++;
155       }
156     } else {
157       pixel = *iptr++;
158 
159       while (count--) {
160         optr[0] = pixel;
161         optr[1] = pixel;
162         optr[2] = pixel;
163         optr += OUT_PIXEL_STRIDE;
164       }
165     }
166   }
167 }
168 
169 
170 /* decode a run length encoded sgi image */
171 static void
decode_rle_sgi(SGIInfo * info,unsigned char * data,mp_image_t * mpi)172 decode_rle_sgi(SGIInfo *info, unsigned char *data, mp_image_t *mpi)
173 {
174   unsigned char *rle_data, *dest_row;
175   uint32_t *starttab;
176   int y, z, ysize, zsize, chan_offset;
177   long start_offset;
178 
179   ysize = info->ysize;
180   zsize = info->zsize;
181 
182   /* rle offset table is right after the header */
183   starttab = (uint32_t*)(data + SGI_HEADER_LEN);
184 
185    for (z = 0; z < zsize; z++) {
186 
187      /* set chan_offset so RGB ends up BGR */
188      chan_offset = (zsize - 1) - z;
189 
190      /* The origin for SGI images is the lower-left corner
191         so read scan lines from bottom to top */
192      for (y = ysize - 1; y >= 0; y--) {
193        dest_row = mpi->planes[0] + mpi->stride[0] * (ysize - 1 - y);
194 
195       /* set start of next run (offsets are from start of header) */
196       start_offset = AV_RB32(&starttab[y + z * ysize]);
197 
198       rle_data = &data[start_offset];
199 
200       if(info->zsize == SGI_GRAYSCALE_IMAGE) {
201         expandrow_gs(dest_row, rle_data);
202       } else {
203         expandrow(dest_row, rle_data, chan_offset);
204       }
205     }
206   }
207 }
208 
209 
210 /* decode an sgi image */
211 static void
decode_uncompressed_sgi(SGIInfo * info,unsigned char * data,mp_image_t * mpi)212 decode_uncompressed_sgi(SGIInfo *info, unsigned char *data, mp_image_t *mpi)
213 {
214   unsigned char *src_row, *dest_row;
215   int x, y, z, xsize, ysize, zsize, chan_offset;
216 
217   xsize = info->xsize;
218   ysize = info->ysize;
219   zsize = info->zsize;
220 
221   /* skip header */
222   data += SGI_HEADER_LEN;
223 
224   for (z = 0; z < zsize; z++) {
225 
226     /* set row ptr to start of current plane */
227     src_row = data + (xsize * ysize * z);
228 
229     /* set chan_offset for RGB -> BGR */
230     chan_offset = (zsize - 1) - z;
231 
232     /* the origin for SGI images is the lower-left corner
233        so read scan lines from bottom to top. */
234     for (y = ysize - 1; y >= 0; y--) {
235       dest_row = mpi->planes[0] + mpi->stride[0] * y;
236       for (x = 0; x < xsize; x++) {
237 
238         /* we only do 24 bit output so promote 8 bit pixels to 24 */
239         if (zsize == SGI_GRAYSCALE_IMAGE) {
240           /* write greyscale value into all channels */
241           dest_row[0] = src_row[x];
242           dest_row[1] = src_row[x];
243           dest_row[2] = src_row[x];
244         } else {
245           dest_row[chan_offset] = src_row[x];
246         }
247 
248         dest_row += OUT_PIXEL_STRIDE;
249       }
250 
251       /* move to next row of the current source plane */
252       src_row += xsize;
253     }
254   }
255 }
256 
257 
258 /* read sgi header fields */
259 static void
read_sgi_header(unsigned char * buf,SGIInfo * info)260 read_sgi_header(unsigned char *buf, SGIInfo *info)
261 {
262   /* sgi data is always stored in big endian byte order */
263   info->magic = AV_RB16(&buf[0]);
264   info->rle = buf[2];
265   info->bytes_per_channel = buf[3];
266   info->dimension = AV_RB16(&buf[4]);
267   info->xsize = AV_RB16(&buf[6]);
268   info->ysize = AV_RB16(&buf[8]);
269   info->zsize = AV_RB16(&buf[10]);
270 }
271 
272 
273 /* decode a frame */
274 static
decode(sh_video_t * sh,void * raw,int len,int flags)275 mp_image_t *decode(sh_video_t *sh, void *raw, int len, int flags)
276 {
277   SGIInfo *info = sh->context;
278   unsigned char *data = raw;
279   mp_image_t *mpi;
280 
281   if (len <= 0) {
282     return NULL; /* skip frame */
283   }
284 
285   read_sgi_header(data, info);
286 
287   /* make sure this is an SGI image file */
288   if (info->magic != SGI_MAGIC) {
289     mp_msg(MSGT_DECVIDEO, MSGL_INFO, "Bad magic number in image.\n");
290     return NULL;
291   }
292 
293   /* check image depth */
294   if (info->bytes_per_channel != 1) {
295     mp_msg(MSGT_DECVIDEO, MSGL_INFO,
296         "Unsupported bytes per channel value %i.\n", info->bytes_per_channel);
297     return NULL;
298   }
299 
300   /* check image dimension */
301   if (info->dimension != 2 && info->dimension != 3) {
302     mp_msg(MSGT_DECVIDEO, MSGL_INFO, "Unsupported image dimension %i.\n",
303         info->dimension);
304     return NULL;
305   }
306 
307   /* change rgba images to rgb so alpha channel will be ignored */
308   if (info->zsize == SGI_RGBA_IMAGE) {
309     info->zsize = SGI_RGB_IMAGE;
310   }
311 
312   /* check image depth */
313   if (info->zsize != SGI_RGB_IMAGE && info->zsize != SGI_GRAYSCALE_IMAGE) {
314     mp_msg(MSGT_DECVIDEO, MSGL_INFO, "Unsupported image depth.\n");
315     return NULL;
316   }
317 
318   /* (re)init libvo if image size is changed */
319   if (last_x != info->xsize || last_y != info->ysize)
320   {
321     last_x = info->xsize;
322     last_y = info->ysize;
323 
324     if (!mpcodecs_config_vo(sh, info->xsize, info->ysize, outfmt)) {
325       mp_msg(MSGT_DECVIDEO, MSGL_INFO, "Config vo failed:\n");
326       return NULL;
327     }
328   }
329 
330   if (!(mpi = mpcodecs_get_image(sh, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
331           info->xsize, info->ysize))) {
332     return NULL;
333   }
334 
335   if (info->rle) {
336     decode_rle_sgi(info, data, mpi);
337   } else {
338     decode_uncompressed_sgi(info, data, mpi);
339   }
340 
341   return mpi;
342 }
343