vo_tag: fix RGB with alpha output on little-endian
[mplayer/glamo.git] / libmpcodecs / vd_sgi.c
blobfff6d6f60b51ef7a30fae8f914180dbe3c4c4977
1 /*
2 * Copyright (c) 2003 Todd Kirby <slapcat@pacbell.net>
4 * This file is part of MPlayer.
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.
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.
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.
21 #include <stdio.h>
22 #include <stdlib.h>
24 #include "config.h"
25 #include "mp_msg.h"
26 #include "ffmpeg_files/intreadwrite.h"
27 #include "mpbswap.h"
28 #include "vd_internal.h"
30 #define SGI_HEADER_LEN 512
31 #define SGI_MAGIC 474
33 #define SGI_GRAYSCALE_IMAGE 1
34 #define SGI_RGB_IMAGE 3
35 #define SGI_RGBA_IMAGE 4
37 #define OUT_PIXEL_STRIDE 3 /* RGB */
40 static const vd_info_t info =
42 "SGI Image decoder",
43 "sgi",
44 "Todd Kirby",
45 "Todd Kirby",
49 LIBVD_EXTERN(sgi)
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;
61 static unsigned int outfmt = IMGFMT_BGR24;
63 static unsigned short last_x = -1;
64 static unsigned short last_y = -1;
67 /* to set/get/query special features/parameters */
68 static int
69 control(sh_video_t* sh, int cmd, void *arg, ...)
71 switch (cmd)
73 case VDCTRL_QUERY_FORMAT:
74 if (*((unsigned int *) arg) == outfmt) {
75 return CONTROL_TRUE;
77 return CONTROL_FALSE;
79 return CONTROL_UNKNOWN;
83 /* init driver */
84 static int
85 init(sh_video_t *sh)
87 sh->context = calloc(1, sizeof(SGIInfo));
88 last_x = -1;
90 return 1;
94 /* uninit driver */
95 static void
96 uninit(sh_video_t *sh)
98 SGIInfo *info = sh->context;
99 free(info);
103 /* expand an rle row into a channel */
104 static void
105 expandrow(unsigned char *optr, unsigned char *iptr, int chan_offset)
107 unsigned char pixel, count;
108 optr += chan_offset;
110 while (1) {
111 pixel = *iptr++;
113 if (!(count = (pixel & 0x7f))) {
114 return;
116 if(pixel & 0x80) {
117 while (count--) {
118 *optr = *iptr;
119 optr += OUT_PIXEL_STRIDE;
120 iptr++;
122 } else {
123 pixel = *iptr++;
125 while (count--) {
126 *optr = pixel;
127 optr += OUT_PIXEL_STRIDE;
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
138 expandrow_gs(unsigned char *optr, unsigned char *iptr)
140 unsigned char pixel, count;
142 while (1) {
143 pixel = *iptr++;
145 if (!(count = (pixel & 0x7f))) {
146 return;
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++;
156 } else {
157 pixel = *iptr++;
159 while (count--) {
160 optr[0] = pixel;
161 optr[1] = pixel;
162 optr[2] = pixel;
163 optr += OUT_PIXEL_STRIDE;
170 /* decode a run length encoded sgi image */
171 static void
172 decode_rle_sgi(SGIInfo *info, unsigned char *data, mp_image_t *mpi)
174 unsigned char *rle_data, *dest_row;
175 uint32_t *starttab;
176 int y, z, xsize, ysize, zsize, chan_offset;
177 long start_offset;
179 xsize = info->xsize;
180 ysize = info->ysize;
181 zsize = info->zsize;
183 /* rle offset table is right after the header */
184 starttab = (uint32_t*)(data + SGI_HEADER_LEN);
186 for (z = 0; z < zsize; z++) {
188 /* set chan_offset so RGB ends up BGR */
189 chan_offset = (zsize - 1) - z;
191 /* The origin for SGI images is the lower-left corner
192 so read scan lines from bottom to top */
193 for (y = ysize - 1; y >= 0; y--) {
194 dest_row = mpi->planes[0] + mpi->stride[0] * (ysize - 1 - y);
196 /* set start of next run (offsets are from start of header) */
197 start_offset = AV_RB32(&starttab[y + z * ysize]);
199 rle_data = &data[start_offset];
201 if(info->zsize == SGI_GRAYSCALE_IMAGE) {
202 expandrow_gs(dest_row, rle_data);
203 } else {
204 expandrow(dest_row, rle_data, chan_offset);
211 /* decode an sgi image */
212 static void
213 decode_uncompressed_sgi(SGIInfo *info, unsigned char *data, mp_image_t *mpi)
215 unsigned char *src_row, *dest_row;
216 int x, y, z, xsize, ysize, zsize, chan_offset;
218 xsize = info->xsize;
219 ysize = info->ysize;
220 zsize = info->zsize;
222 /* skip header */
223 data += SGI_HEADER_LEN;
225 for (z = 0; z < zsize; z++) {
227 /* set row ptr to start of current plane */
228 src_row = data + (xsize * ysize * z);
230 /* set chan_offset for RGB -> BGR */
231 chan_offset = (zsize - 1) - z;
233 /* the origin for SGI images is the lower-left corner
234 so read scan lines from bottom to top. */
235 for (y = ysize - 1; y >= 0; y--) {
236 dest_row = mpi->planes[0] + mpi->stride[0] * y;
237 for (x = 0; x < xsize; x++) {
239 /* we only do 24 bit output so promote 8 bit pixels to 24 */
240 if (zsize == SGI_GRAYSCALE_IMAGE) {
241 /* write greyscale value into all channels */
242 dest_row[0] = src_row[x];
243 dest_row[1] = src_row[x];
244 dest_row[2] = src_row[x];
245 } else {
246 dest_row[chan_offset] = src_row[x];
249 dest_row += OUT_PIXEL_STRIDE;
252 /* move to next row of the current source plane */
253 src_row += xsize;
259 /* read sgi header fields */
260 static void
261 read_sgi_header(unsigned char *buf, SGIInfo *info)
263 /* sgi data is always stored in big endian byte order */
264 info->magic = AV_RB16(&buf[0]);
265 info->rle = buf[2];
266 info->bytes_per_channel = buf[3];
267 info->dimension = AV_RB16(&buf[4]);
268 info->xsize = AV_RB16(&buf[6]);
269 info->ysize = AV_RB16(&buf[8]);
270 info->zsize = AV_RB16(&buf[10]);
274 /* decode a frame */
275 static
276 mp_image_t *decode(sh_video_t *sh, void *raw, int len, int flags)
278 SGIInfo *info = sh->context;
279 unsigned char *data = raw;
280 mp_image_t *mpi;
282 if (len <= 0) {
283 return NULL; /* skip frame */
286 read_sgi_header(data, info);
288 /* make sure this is an SGI image file */
289 if (info->magic != SGI_MAGIC) {
290 mp_msg(MSGT_DECVIDEO, MSGL_INFO, "Bad magic number in image.\n");
291 return NULL;
294 /* check image depth */
295 if (info->bytes_per_channel != 1) {
296 mp_msg(MSGT_DECVIDEO, MSGL_INFO,
297 "Unsupported bytes per channel value %i.\n", info->bytes_per_channel);
298 return NULL;
301 /* check image dimension */
302 if (info->dimension != 2 && info->dimension != 3) {
303 mp_msg(MSGT_DECVIDEO, MSGL_INFO, "Unsupported image dimension %i.\n",
304 info->dimension);
305 return NULL;
308 /* change rgba images to rgb so alpha channel will be ignored */
309 if (info->zsize == SGI_RGBA_IMAGE) {
310 info->zsize = SGI_RGB_IMAGE;
313 /* check image depth */
314 if (info->zsize != SGI_RGB_IMAGE && info->zsize != SGI_GRAYSCALE_IMAGE) {
315 mp_msg(MSGT_DECVIDEO, MSGL_INFO, "Unsupported image depth.\n");
316 return NULL;
319 /* (re)init libvo if image size is changed */
320 if (last_x != info->xsize || last_y != info->ysize)
322 last_x = info->xsize;
323 last_y = info->ysize;
325 if (!mpcodecs_config_vo(sh, info->xsize, info->ysize, outfmt)) {
326 mp_msg(MSGT_DECVIDEO, MSGL_INFO, "Config vo failed:\n");
327 return NULL;
331 if (!(mpi = mpcodecs_get_image(sh, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
332 info->xsize, info->ysize))) {
333 return NULL;
336 if (info->rle) {
337 decode_rle_sgi(info, data, mpi);
338 } else {
339 decode_uncompressed_sgi(info, data, mpi);
342 return mpi;