1/*
2 * Sun Rasterfile (.sun/.ras/im{1,8,24}/.sunras) image decoder
3 * Copyright (c) 2007, 2008 Ivo van Poorten
4 *
5 * This file is part of Libav.
6 *
7 * Libav is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * Libav is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with Libav; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include "libavutil/intreadwrite.h"
23#include "libavutil/imgutils.h"
24#include "avcodec.h"
25
26#define RT_OLD          0
27#define RT_STANDARD     1
28#define RT_BYTE_ENCODED 2
29#define RT_FORMAT_RGB   3
30#define RT_FORMAT_TIFF  4
31#define RT_FORMAT_IFF   5
32
33typedef struct SUNRASTContext {
34    AVFrame picture;
35} SUNRASTContext;
36
37static av_cold int sunrast_init(AVCodecContext *avctx) {
38    SUNRASTContext *s = avctx->priv_data;
39
40    avcodec_get_frame_defaults(&s->picture);
41    avctx->coded_frame= &s->picture;
42
43    return 0;
44}
45
46static int sunrast_decode_frame(AVCodecContext *avctx, void *data,
47                                int *data_size, AVPacket *avpkt) {
48    const uint8_t *buf = avpkt->data;
49    const uint8_t *buf_end = avpkt->data + avpkt->size;
50    SUNRASTContext * const s = avctx->priv_data;
51    AVFrame *picture = data;
52    AVFrame * const p = &s->picture;
53    unsigned int w, h, depth, type, maptype, maplength, stride, x, y, len, alen;
54    uint8_t *ptr;
55    const uint8_t *bufstart = buf;
56
57    if (avpkt->size < 32)
58        return AVERROR_INVALIDDATA;
59
60    if (AV_RB32(buf) != 0x59a66a95) {
61        av_log(avctx, AV_LOG_ERROR, "this is not sunras encoded data\n");
62        return -1;
63    }
64
65    w         = AV_RB32(buf+4);
66    h         = AV_RB32(buf+8);
67    depth     = AV_RB32(buf+12);
68    type      = AV_RB32(buf+20);
69    maptype   = AV_RB32(buf+24);
70    maplength = AV_RB32(buf+28);
71    buf      += 32;
72
73    if (type == RT_FORMAT_TIFF || type == RT_FORMAT_IFF) {
74        av_log(avctx, AV_LOG_ERROR, "unsupported (compression) type\n");
75        return -1;
76    }
77    if (type < RT_OLD || type > RT_FORMAT_IFF) {
78        av_log(avctx, AV_LOG_ERROR, "invalid (compression) type\n");
79        return -1;
80    }
81    if (av_image_check_size(w, h, 0, avctx)) {
82        av_log(avctx, AV_LOG_ERROR, "invalid image size\n");
83        return -1;
84    }
85    if (maptype & ~1) {
86        av_log(avctx, AV_LOG_ERROR, "invalid colormap type\n");
87        return -1;
88    }
89
90
91    switch (depth) {
92        case 1:
93            avctx->pix_fmt = PIX_FMT_MONOWHITE;
94            break;
95        case 8:
96            avctx->pix_fmt = PIX_FMT_PAL8;
97            break;
98        case 24:
99            avctx->pix_fmt = (type == RT_FORMAT_RGB) ? PIX_FMT_RGB24 : PIX_FMT_BGR24;
100            break;
101        default:
102            av_log(avctx, AV_LOG_ERROR, "invalid depth\n");
103            return -1;
104    }
105
106    if (p->data[0])
107        avctx->release_buffer(avctx, p);
108
109    if (w != avctx->width || h != avctx->height)
110        avcodec_set_dimensions(avctx, w, h);
111    if (avctx->get_buffer(avctx, p) < 0) {
112        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
113        return -1;
114    }
115
116    p->pict_type = AV_PICTURE_TYPE_I;
117
118    if (buf_end - buf < maplength)
119        return AVERROR_INVALIDDATA;
120
121    if (depth != 8 && maplength) {
122        av_log(avctx, AV_LOG_WARNING, "useless colormap found or file is corrupted, trying to recover\n");
123
124    } else if (depth == 8) {
125        unsigned int len = maplength / 3;
126
127        if (!maplength) {
128            av_log(avctx, AV_LOG_ERROR, "colormap expected\n");
129            return -1;
130        }
131        if (maplength % 3 || maplength > 768) {
132            av_log(avctx, AV_LOG_WARNING, "invalid colormap length\n");
133            return -1;
134        }
135
136        ptr = p->data[1];
137        for (x=0; x<len; x++, ptr+=4)
138            *(uint32_t *)ptr = (buf[x]<<16) + (buf[len+x]<<8) + buf[len+len+x];
139    }
140
141    buf += maplength;
142
143    ptr    = p->data[0];
144    stride = p->linesize[0];
145
146    /* scanlines are aligned on 16 bit boundaries */
147    len  = (depth * w + 7) >> 3;
148    alen = len + (len&1);
149
150    if (type == RT_BYTE_ENCODED) {
151        int value, run;
152        uint8_t *end = ptr + h*stride;
153
154        x = 0;
155        while (ptr != end && buf < buf_end) {
156            run = 1;
157            if (buf_end - buf < 1)
158                return AVERROR_INVALIDDATA;
159
160            if ((value = *buf++) == 0x80) {
161                run = *buf++ + 1;
162                if (run != 1)
163                    value = *buf++;
164            }
165            while (run--) {
166                if (x < len)
167                    ptr[x] = value;
168                if (++x >= alen) {
169                    x = 0;
170                    ptr += stride;
171                    if (ptr == end)
172                        break;
173                }
174            }
175        }
176    } else {
177        for (y=0; y<h; y++) {
178            if (buf_end - buf < len)
179                break;
180            memcpy(ptr, buf, len);
181            ptr += stride;
182            buf += alen;
183        }
184    }
185
186    *picture = s->picture;
187    *data_size = sizeof(AVFrame);
188
189    return buf - bufstart;
190}
191
192static av_cold int sunrast_end(AVCodecContext *avctx) {
193    SUNRASTContext *s = avctx->priv_data;
194
195    if(s->picture.data[0])
196        avctx->release_buffer(avctx, &s->picture);
197
198    return 0;
199}
200
201AVCodec ff_sunrast_decoder = {
202    .name           = "sunrast",
203    .type           = AVMEDIA_TYPE_VIDEO,
204    .id             = CODEC_ID_SUNRAST,
205    .priv_data_size = sizeof(SUNRASTContext),
206    .init           = sunrast_init,
207    .close          = sunrast_end,
208    .decode         = sunrast_decode_frame,
209    .capabilities   = CODEC_CAP_DR1,
210    .long_name = NULL_IF_CONFIG_SMALL("Sun Rasterfile image"),
211};
212