Support chapter seeking with ordered chapters
[mplayer.git] / libmpcodecs / vd_sgi.c
blobe4e72285b974c932eb142dfde27aadabcd79bf40
1 /*
2 * author: Todd Kirby <slapcat@pacbell.net>
3 */
5 #include <stdio.h>
6 #include <stdlib.h>
8 #include "config.h"
9 #include "mp_msg.h"
10 #include "libavutil/common.h"
11 #include "mpbswap.h"
12 #include "vd_internal.h"
14 #define SGI_HEADER_LEN 512
15 #define SGI_MAGIC 474
17 #define SGI_GRAYSCALE_IMAGE 1
18 #define SGI_RGB_IMAGE 3
19 #define SGI_RGBA_IMAGE 4
21 #define OUT_PIXEL_STRIDE 3 /* RGB */
24 static const vd_info_t info =
26 "SGI Image decoder",
27 "sgi",
28 "Todd Kirby",
29 "Todd Kirby",
33 LIBVD_EXTERN(sgi)
35 typedef struct {
36 short magic;
37 char rle;
38 char bytes_per_channel;
39 unsigned short dimension;
40 unsigned short xsize;
41 unsigned short ysize;
42 unsigned short zsize;
43 } SGIInfo;
45 static unsigned int outfmt = IMGFMT_BGR24;
47 static unsigned short last_x = -1;
48 static unsigned short last_y = -1;
51 /* to set/get/query special features/parameters */
52 static int
53 control(sh_video_t* sh, int cmd, void *arg, ...)
55 switch (cmd)
57 case VDCTRL_QUERY_FORMAT:
58 if (*((unsigned int *) arg) == outfmt) {
59 return CONTROL_TRUE;
61 return CONTROL_FALSE;
63 return CONTROL_UNKNOWN;
67 /* init driver */
68 static int
69 init(sh_video_t *sh)
71 sh->context = (SGIInfo *) calloc(1, sizeof(SGIInfo));
72 last_x = -1;
74 return 1;
78 /* uninit driver */
79 static void
80 uninit(sh_video_t *sh)
82 SGIInfo *info = sh->context;
83 free(info);
87 /* expand an rle row into a channel */
88 static void
89 expandrow(unsigned char *optr, unsigned char *iptr, int chan_offset)
91 unsigned char pixel, count;
92 optr += chan_offset;
94 while (1) {
95 pixel = *iptr++;
97 if (!(count = (pixel & 0x7f))) {
98 return;
100 if(pixel & 0x80) {
101 while (count--) {
102 *optr = *iptr;
103 optr += OUT_PIXEL_STRIDE;
104 iptr++;
106 } else {
107 pixel = *iptr++;
109 while (count--) {
110 *optr = pixel;
111 optr += OUT_PIXEL_STRIDE;
118 /* expand an rle row into all 3 channels.
119 a separate function for grayscale so we don't slow down the
120 more common case rgb function with a bunch of ifs. */
121 static void
122 expandrow_gs(unsigned char *optr, unsigned char *iptr)
124 unsigned char pixel, count;
126 while (1) {
127 pixel = *iptr++;
129 if (!(count = (pixel & 0x7f))) {
130 return;
132 if(pixel & 0x80) {
133 while (count--) {
134 optr[0] = *iptr;
135 optr[1] = *iptr;
136 optr[2] = *iptr;
137 optr += OUT_PIXEL_STRIDE;
138 iptr++;
140 } else {
141 pixel = *iptr++;
143 while (count--) {
144 optr[0] = pixel;
145 optr[1] = pixel;
146 optr[2] = pixel;
147 optr += OUT_PIXEL_STRIDE;
154 /* decode a run length encoded sgi image */
155 static void
156 decode_rle_sgi(SGIInfo *info, unsigned char *data, mp_image_t *mpi)
158 unsigned char *rle_data, *dest_row;
159 uint32_t *starttab;
160 int y, z, xsize, ysize, zsize, chan_offset;
161 long start_offset;
163 xsize = info->xsize;
164 ysize = info->ysize;
165 zsize = info->zsize;
167 /* rle offset table is right after the header */
168 starttab = (uint32_t*)(data + SGI_HEADER_LEN);
170 for (z = 0; z < zsize; z++) {
172 /* set chan_offset so RGB ends up BGR */
173 chan_offset = (zsize - 1) - z;
175 /* The origin for SGI images is the lower-left corner
176 so read scan lines from bottom to top */
177 for (y = ysize - 1; y >= 0; y--) {
178 dest_row = mpi->planes[0] + mpi->stride[0] * (ysize - 1 - y);
180 /* set start of next run (offsets are from start of header) */
181 start_offset = be2me_32(*(uint32_t*) &starttab[y + z * ysize]);
183 rle_data = &data[start_offset];
185 if(info->zsize == SGI_GRAYSCALE_IMAGE) {
186 expandrow_gs(dest_row, rle_data);
187 } else {
188 expandrow(dest_row, rle_data, chan_offset);
195 /* decode an sgi image */
196 static void
197 decode_uncompressed_sgi(SGIInfo *info, unsigned char *data, mp_image_t *mpi)
199 unsigned char *src_row, *dest_row;
200 int x, y, z, xsize, ysize, zsize, chan_offset;
202 xsize = info->xsize;
203 ysize = info->ysize;
204 zsize = info->zsize;
206 /* skip header */
207 data += SGI_HEADER_LEN;
209 for (z = 0; z < zsize; z++) {
211 /* set row ptr to start of current plane */
212 src_row = data + (xsize * ysize * z);
214 /* set chan_offset for RGB -> BGR */
215 chan_offset = (zsize - 1) - z;
217 /* the origin for SGI images is the lower-left corner
218 so read scan lines from bottom to top. */
219 for (y = ysize - 1; y >= 0; y--) {
220 dest_row = mpi->planes[0] + mpi->stride[0] * y;
221 for (x = 0; x < xsize; x++) {
223 /* we only do 24 bit output so promote 8 bit pixels to 24 */
224 if (zsize == SGI_GRAYSCALE_IMAGE) {
225 /* write greyscale value into all channels */
226 dest_row[0] = src_row[x];
227 dest_row[1] = src_row[x];
228 dest_row[2] = src_row[x];
229 } else {
230 dest_row[chan_offset] = src_row[x];
233 dest_row += OUT_PIXEL_STRIDE;
236 /* move to next row of the current source plane */
237 src_row += xsize;
243 /* read sgi header fields */
244 static void
245 read_sgi_header(unsigned char *buf, SGIInfo *info)
247 /* sgi data is always stored in big endian byte order */
248 info->magic = be2me_16(*(unsigned short *) &buf[0]);
249 info->rle = buf[2];
250 info->bytes_per_channel = buf[3];
251 info->dimension = be2me_16(*(unsigned short *) &buf[4]);
252 info->xsize = be2me_16(*(unsigned short *) &buf[6]);
253 info->ysize = be2me_16(*(unsigned short *) &buf[8]);
254 info->zsize = be2me_16(*(unsigned short *) &buf[10]);
258 /* decode a frame */
259 static
260 mp_image_t *decode(sh_video_t *sh, void *raw, int len, int flags)
262 SGIInfo *info = sh->context;
263 unsigned char *data = raw;
264 mp_image_t *mpi;
266 if (len <= 0) {
267 return NULL; /* skip frame */
270 read_sgi_header(data, info);
272 /* make sure this is an SGI image file */
273 if (info->magic != SGI_MAGIC) {
274 mp_msg(MSGT_DECVIDEO, MSGL_INFO, "Bad magic number in image.\n");
275 return NULL;
278 /* check image depth */
279 if (info->bytes_per_channel != 1) {
280 mp_msg(MSGT_DECVIDEO, MSGL_INFO,
281 "Unsupported bytes per channel value %i.\n", info->bytes_per_channel);
282 return NULL;
285 /* check image dimension */
286 if (info->dimension != 2 && info->dimension != 3) {
287 mp_msg(MSGT_DECVIDEO, MSGL_INFO, "Unsupported image dimension %i.\n",
288 info->dimension);
289 return NULL;
292 /* change rgba images to rgb so alpha channel will be ignored */
293 if (info->zsize == SGI_RGBA_IMAGE) {
294 info->zsize = SGI_RGB_IMAGE;
297 /* check image depth */
298 if (info->zsize != SGI_RGB_IMAGE && info->zsize != SGI_GRAYSCALE_IMAGE) {
299 mp_msg(MSGT_DECVIDEO, MSGL_INFO, "Unsupported image depth.\n");
300 return NULL;
303 /* (re)init libvo if image size is changed */
304 if (last_x != info->xsize || last_y != info->ysize)
306 last_x = info->xsize;
307 last_y = info->ysize;
309 if (!mpcodecs_config_vo(sh, info->xsize, info->ysize, outfmt)) {
310 mp_msg(MSGT_DECVIDEO, MSGL_INFO, "Config vo failed:\n");
311 return NULL;
315 if (!(mpi = mpcodecs_get_image(sh, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
316 info->xsize, info->ysize))) {
317 return NULL;
320 if (info->rle) {
321 decode_rle_sgi(info, data, mpi);
322 } else {
323 decode_uncompressed_sgi(info, data, mpi);
326 return mpi;