• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * SGI image encoder
3  * Todd Kirby <doubleshot@pacbell.net>
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg 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  * FFmpeg 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 FFmpeg; 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/opt.h"
23 
24 #include "avcodec.h"
25 #include "bytestream.h"
26 #include "codec_internal.h"
27 #include "encode.h"
28 #include "sgi.h"
29 #include "rle.h"
30 
31 #define SGI_SINGLE_CHAN 2
32 #define SGI_MULTI_CHAN 3
33 
34 typedef struct SgiContext {
35     AVClass *class;
36 
37     int rle;
38 } SgiContext;
39 
encode_init(AVCodecContext * avctx)40 static av_cold int encode_init(AVCodecContext *avctx)
41 {
42     if (avctx->width > 65535 || avctx->height > 65535) {
43         av_log(avctx, AV_LOG_ERROR, "Unsupported resolution %dx%d. "
44                "SGI does not support resolutions above 65535x65535\n",
45                avctx->width, avctx->height);
46         return AVERROR_INVALIDDATA;
47     }
48 
49     return 0;
50 }
51 
sgi_rle_encode(PutByteContext * pbc,const uint8_t * src,int w,int bpp)52 static int sgi_rle_encode(PutByteContext *pbc, const uint8_t *src,
53                           int w, int bpp)
54 {
55     int val, count, x, start = bytestream2_tell_p(pbc);
56     void (*bytestream2_put)(PutByteContext *, unsigned int);
57 
58     if (bpp == 1)
59         bytestream2_put = bytestream2_put_byte;
60     else
61         bytestream2_put = bytestream2_put_be16;
62 
63     for (x = 0; x < w; x += count) {
64         /* see if we can encode the next set of pixels with RLE */
65         count = ff_rle_count_pixels(src, w - x, bpp, 1);
66         if (count > 1) {
67             if (bytestream2_get_bytes_left_p(pbc) < bpp * 2)
68                 return AVERROR_INVALIDDATA;
69 
70             val = bpp == 1 ? *src : AV_RB16(src);
71             bytestream2_put(pbc, count);
72             bytestream2_put(pbc, val);
73         } else {
74             int i;
75             /* fall back on uncompressed */
76             count = ff_rle_count_pixels(src, w - x, bpp, 0);
77             if (bytestream2_get_bytes_left_p(pbc) < bpp * (count + 1))
78                 return AVERROR_INVALIDDATA;
79 
80             bytestream2_put(pbc, count + 0x80);
81             for (i = 0; i < count; i++) {
82                 val = bpp == 1 ? src[i] : AV_RB16(src + i * bpp);
83                 bytestream2_put(pbc, val);
84             }
85         }
86 
87         src += count * bpp;
88     }
89 
90     return bytestream2_tell_p(pbc) - start;
91 }
92 
encode_frame(AVCodecContext * avctx,AVPacket * pkt,const AVFrame * frame,int * got_packet)93 static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
94                         const AVFrame *frame, int *got_packet)
95 {
96     SgiContext *s = avctx->priv_data;
97     const AVFrame * const p = frame;
98     PutByteContext pbc;
99     uint8_t *in_buf, *encode_buf;
100     int x, y, z, length, tablesize, ret, i;
101     unsigned int width, height, depth, dimension;
102     unsigned int bytes_per_channel, pixmax, put_be;
103 
104     width  = avctx->width;
105     height = avctx->height;
106     bytes_per_channel = 1;
107     pixmax = 0xFF;
108     put_be = HAVE_BIGENDIAN;
109 
110     switch (avctx->pix_fmt) {
111     case AV_PIX_FMT_GRAY8:
112         dimension = SGI_SINGLE_CHAN;
113         depth     = SGI_GRAYSCALE;
114         break;
115     case AV_PIX_FMT_RGB24:
116         dimension = SGI_MULTI_CHAN;
117         depth     = SGI_RGB;
118         break;
119     case AV_PIX_FMT_RGBA:
120         dimension = SGI_MULTI_CHAN;
121         depth     = SGI_RGBA;
122         break;
123     case AV_PIX_FMT_GRAY16LE:
124         put_be = !HAVE_BIGENDIAN;
125     case AV_PIX_FMT_GRAY16BE:
126         bytes_per_channel = 2;
127         pixmax = 0xFFFF;
128         dimension = SGI_SINGLE_CHAN;
129         depth     = SGI_GRAYSCALE;
130         break;
131     case AV_PIX_FMT_RGB48LE:
132         put_be = !HAVE_BIGENDIAN;
133     case AV_PIX_FMT_RGB48BE:
134         bytes_per_channel = 2;
135         pixmax = 0xFFFF;
136         dimension = SGI_MULTI_CHAN;
137         depth     = SGI_RGB;
138         break;
139     case AV_PIX_FMT_RGBA64LE:
140         put_be = !HAVE_BIGENDIAN;
141     case AV_PIX_FMT_RGBA64BE:
142         bytes_per_channel = 2;
143         pixmax = 0xFFFF;
144         dimension = SGI_MULTI_CHAN;
145         depth     = SGI_RGBA;
146         break;
147     default:
148         return AVERROR_INVALIDDATA;
149     }
150 
151     tablesize = depth * height * 4;
152     length = SGI_HEADER_SIZE;
153     if (!s->rle)
154         length += depth * height * width;
155     else // assume sgi_rle_encode() produces at most 2x size of input
156         length += tablesize * 2 + depth * height * (2 * width + 1);
157 
158     if ((ret = ff_alloc_packet(avctx, pkt, bytes_per_channel * length)) < 0)
159         return ret;
160 
161     bytestream2_init_writer(&pbc, pkt->data, pkt->size);
162 
163     /* Encode header. */
164     bytestream2_put_be16(&pbc, SGI_MAGIC);
165     bytestream2_put_byte(&pbc, s->rle); /* RLE 1 - VERBATIM 0 */
166     bytestream2_put_byte(&pbc, bytes_per_channel);
167     bytestream2_put_be16(&pbc, dimension);
168     bytestream2_put_be16(&pbc, width);
169     bytestream2_put_be16(&pbc, height);
170     bytestream2_put_be16(&pbc, depth);
171 
172     bytestream2_put_be32(&pbc, 0L); /* pixmin */
173     bytestream2_put_be32(&pbc, pixmax);
174     bytestream2_put_be32(&pbc, 0L); /* dummy */
175 
176     /* name */
177     for (i = 0; i < 80; i++)
178         bytestream2_put_byte(&pbc, 0L);
179 
180     /* colormap */
181     bytestream2_put_be32(&pbc, 0L);
182 
183     /* The rest of the 512 byte header is unused. */
184     for (i = 0; i < 404; i++)
185         bytestream2_put_byte(&pbc, 0L);
186 
187     if (s->rle) {
188         PutByteContext taboff_pcb, tablen_pcb;
189 
190         /* Skip RLE offset table. */
191         bytestream2_init_writer(&taboff_pcb, pbc.buffer, tablesize);
192         bytestream2_skip_p(&pbc, tablesize);
193 
194         /* Skip RLE length table. */
195         bytestream2_init_writer(&tablen_pcb, pbc.buffer, tablesize);
196         bytestream2_skip_p(&pbc, tablesize);
197 
198         /* Make an intermediate consecutive buffer. */
199         if (!(encode_buf = av_malloc(width * bytes_per_channel)))
200             return AVERROR(ENOMEM);
201 
202         for (z = 0; z < depth; z++) {
203             in_buf = p->data[0] + p->linesize[0] * (height - 1) + z * bytes_per_channel;
204 
205             for (y = 0; y < height; y++) {
206                 bytestream2_put_be32(&taboff_pcb, bytestream2_tell_p(&pbc));
207 
208                 for (x = 0; x < width * bytes_per_channel; x += bytes_per_channel)
209                     if (bytes_per_channel == 1) {
210                         encode_buf[x]     = in_buf[depth * x];
211                     } else if (HAVE_BIGENDIAN ^ put_be) {
212                         encode_buf[x + 1] = in_buf[depth * x];
213                         encode_buf[x]     = in_buf[depth * x + 1];
214                     } else {
215                         encode_buf[x]     = in_buf[depth * x];
216                         encode_buf[x + 1] = in_buf[depth * x + 1];
217                     }
218 
219                 length = sgi_rle_encode(&pbc, encode_buf, width,
220                                         bytes_per_channel);
221                 if (length < 1) {
222                     av_free(encode_buf);
223                     return AVERROR_INVALIDDATA;
224                 }
225 
226                 bytestream2_put_be32(&tablen_pcb, length);
227                 in_buf -= p->linesize[0];
228             }
229         }
230 
231         av_free(encode_buf);
232     } else {
233         for (z = 0; z < depth; z++) {
234             in_buf = p->data[0] + p->linesize[0] * (height - 1) + z * bytes_per_channel;
235 
236             for (y = 0; y < height; y++) {
237                 for (x = 0; x < width * depth; x += depth)
238                     if (bytes_per_channel == 1)
239                         bytestream2_put_byte(&pbc, in_buf[x]);
240                     else
241                         if (put_be)
242                             bytestream2_put_be16(&pbc, ((uint16_t *)in_buf)[x]);
243                         else
244                             bytestream2_put_le16(&pbc, ((uint16_t *)in_buf)[x]);
245 
246                 in_buf -= p->linesize[0];
247             }
248         }
249     }
250 
251     /* total length */
252     pkt->size   = bytestream2_tell_p(&pbc);
253     *got_packet = 1;
254 
255     return 0;
256 }
257 
258 #define OFFSET(x) offsetof(SgiContext, x)
259 #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
260 static const AVOption options[] = {
261     { "rle", "Use run-length compression", OFFSET(rle), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE },
262 
263     { NULL },
264 };
265 
266 static const AVClass sgi_class = {
267     .class_name = "sgi",
268     .item_name  = av_default_item_name,
269     .option     = options,
270     .version    = LIBAVUTIL_VERSION_INT,
271 };
272 
273 const FFCodec ff_sgi_encoder = {
274     .p.name    = "sgi",
275     .p.long_name = NULL_IF_CONFIG_SMALL("SGI image"),
276     .p.type    = AVMEDIA_TYPE_VIDEO,
277     .p.id      = AV_CODEC_ID_SGI,
278     .priv_data_size = sizeof(SgiContext),
279     .p.priv_class = &sgi_class,
280     .init      = encode_init,
281     FF_CODEC_ENCODE_CB(encode_frame),
282     .p.pix_fmts = (const enum AVPixelFormat[]) {
283         AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA,
284         AV_PIX_FMT_RGB48LE, AV_PIX_FMT_RGB48BE,
285         AV_PIX_FMT_RGBA64LE, AV_PIX_FMT_RGBA64BE,
286         AV_PIX_FMT_GRAY16LE, AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_GRAY8,
287         AV_PIX_FMT_NONE
288     },
289     .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
290 };
291