3 * Todd Kirby <doubleshot@pacbell.net>
5 * This file is part of FFmpeg.
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.
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.
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
22 #include "libavutil/mem.h"
23 #include "libavutil/opt.h"
26 #include "bytestream.h"
27 #include "codec_internal.h"
32 #define SGI_SINGLE_CHAN 2
33 #define SGI_MULTI_CHAN 3
35 typedef struct SgiContext
{
41 static av_cold
int encode_init(AVCodecContext
*avctx
)
43 if (avctx
->width
> 65535 || avctx
->height
> 65535) {
44 av_log(avctx
, AV_LOG_ERROR
, "Unsupported resolution %dx%d. "
45 "SGI does not support resolutions above 65535x65535\n",
46 avctx
->width
, avctx
->height
);
47 return AVERROR_INVALIDDATA
;
53 static int sgi_rle_encode(PutByteContext
*pbc
, const uint8_t *src
,
56 int val
, count
, x
, start
= bytestream2_tell_p(pbc
);
57 void (*bytestream2_put
)(PutByteContext
*, unsigned int);
60 bytestream2_put
= bytestream2_put_byte
;
62 bytestream2_put
= bytestream2_put_be16
;
64 for (x
= 0; x
< w
; x
+= count
) {
65 /* see if we can encode the next set of pixels with RLE */
66 count
= ff_rle_count_pixels(src
, w
- x
, bpp
, 1);
68 if (bytestream2_get_bytes_left_p(pbc
) < bpp
* 2)
69 return AVERROR_INVALIDDATA
;
71 val
= bpp
== 1 ? *src
: AV_RB16(src
);
72 bytestream2_put(pbc
, count
);
73 bytestream2_put(pbc
, val
);
76 /* fall back on uncompressed */
77 count
= ff_rle_count_pixels(src
, w
- x
, bpp
, 0);
78 if (bytestream2_get_bytes_left_p(pbc
) < bpp
* (count
+ 1))
79 return AVERROR_INVALIDDATA
;
81 bytestream2_put(pbc
, count
+ 0x80);
82 for (i
= 0; i
< count
; i
++) {
83 val
= bpp
== 1 ? src
[i
] : AV_RB16(src
+ i
* bpp
);
84 bytestream2_put(pbc
, val
);
91 return bytestream2_tell_p(pbc
) - start
;
94 static int encode_frame(AVCodecContext
*avctx
, AVPacket
*pkt
,
95 const AVFrame
*frame
, int *got_packet
)
97 SgiContext
*s
= avctx
->priv_data
;
98 const AVFrame
* const p
= frame
;
101 int x
, y
, z
, length
, tablesize
, ret
, i
;
102 unsigned int width
, height
, depth
, dimension
;
103 unsigned int bytes_per_channel
, pixmax
, put_be
;
105 width
= avctx
->width
;
106 height
= avctx
->height
;
107 bytes_per_channel
= 1;
109 put_be
= HAVE_BIGENDIAN
;
111 switch (avctx
->pix_fmt
) {
112 case AV_PIX_FMT_GRAY8
:
113 dimension
= SGI_SINGLE_CHAN
;
114 depth
= SGI_GRAYSCALE
;
116 case AV_PIX_FMT_RGB24
:
117 dimension
= SGI_MULTI_CHAN
;
120 case AV_PIX_FMT_RGBA
:
121 dimension
= SGI_MULTI_CHAN
;
124 case AV_PIX_FMT_GRAY16LE
:
125 put_be
= !HAVE_BIGENDIAN
;
126 case AV_PIX_FMT_GRAY16BE
:
127 bytes_per_channel
= 2;
129 dimension
= SGI_SINGLE_CHAN
;
130 depth
= SGI_GRAYSCALE
;
132 case AV_PIX_FMT_RGB48LE
:
133 put_be
= !HAVE_BIGENDIAN
;
134 case AV_PIX_FMT_RGB48BE
:
135 bytes_per_channel
= 2;
137 dimension
= SGI_MULTI_CHAN
;
140 case AV_PIX_FMT_RGBA64LE
:
141 put_be
= !HAVE_BIGENDIAN
;
142 case AV_PIX_FMT_RGBA64BE
:
143 bytes_per_channel
= 2;
145 dimension
= SGI_MULTI_CHAN
;
149 return AVERROR_INVALIDDATA
;
152 tablesize
= depth
* height
* 4;
153 length
= SGI_HEADER_SIZE
;
155 length
+= depth
* height
* width
;
156 else // assume sgi_rle_encode() produces at most 2x size of input
157 length
+= tablesize
* 2 + depth
* height
* (2 * width
+ 1);
159 if ((ret
= ff_alloc_packet(avctx
, pkt
, bytes_per_channel
* length
)) < 0)
162 bytestream2_init_writer(&pbc
, pkt
->data
, pkt
->size
);
165 bytestream2_put_be16(&pbc
, SGI_MAGIC
);
166 bytestream2_put_byte(&pbc
, s
->rle
); /* RLE 1 - VERBATIM 0 */
167 bytestream2_put_byte(&pbc
, bytes_per_channel
);
168 bytestream2_put_be16(&pbc
, dimension
);
169 bytestream2_put_be16(&pbc
, width
);
170 bytestream2_put_be16(&pbc
, height
);
171 bytestream2_put_be16(&pbc
, depth
);
173 bytestream2_put_be32(&pbc
, 0L); /* pixmin */
174 bytestream2_put_be32(&pbc
, pixmax
);
175 bytestream2_put_be32(&pbc
, 0L); /* dummy */
178 for (i
= 0; i
< 80; i
++)
179 bytestream2_put_byte(&pbc
, 0L);
182 bytestream2_put_be32(&pbc
, 0L);
184 /* The rest of the 512 byte header is unused. */
185 for (i
= 0; i
< 404; i
++)
186 bytestream2_put_byte(&pbc
, 0L);
189 PutByteContext taboff_pcb
, tablen_pcb
;
191 /* Skip RLE offset table. */
192 bytestream2_init_writer(&taboff_pcb
, pbc
.buffer
, tablesize
);
193 bytestream2_skip_p(&pbc
, tablesize
);
195 /* Skip RLE length table. */
196 bytestream2_init_writer(&tablen_pcb
, pbc
.buffer
, tablesize
);
197 bytestream2_skip_p(&pbc
, tablesize
);
199 /* Make an intermediate consecutive buffer. */
200 if (!(encode_buf
= av_malloc(width
* bytes_per_channel
)))
201 return AVERROR(ENOMEM
);
203 for (z
= 0; z
< depth
; z
++) {
204 const uint8_t *in_buf
= p
->data
[0] + p
->linesize
[0] * (height
- 1) + z
* bytes_per_channel
;
206 for (y
= 0; y
< height
; y
++) {
207 bytestream2_put_be32(&taboff_pcb
, bytestream2_tell_p(&pbc
));
209 for (x
= 0; x
< width
* bytes_per_channel
; x
+= bytes_per_channel
)
210 if (bytes_per_channel
== 1) {
211 encode_buf
[x
] = in_buf
[depth
* x
];
212 } else if (HAVE_BIGENDIAN
^ put_be
) {
213 encode_buf
[x
+ 1] = in_buf
[depth
* x
];
214 encode_buf
[x
] = in_buf
[depth
* x
+ 1];
216 encode_buf
[x
] = in_buf
[depth
* x
];
217 encode_buf
[x
+ 1] = in_buf
[depth
* x
+ 1];
220 length
= sgi_rle_encode(&pbc
, encode_buf
, width
,
224 return AVERROR_INVALIDDATA
;
227 bytestream2_put_be32(&tablen_pcb
, length
);
228 in_buf
-= p
->linesize
[0];
234 for (z
= 0; z
< depth
; z
++) {
235 const uint8_t *in_buf
= p
->data
[0] + p
->linesize
[0] * (height
- 1) + z
* bytes_per_channel
;
237 for (y
= 0; y
< height
; y
++) {
238 for (x
= 0; x
< width
* depth
; x
+= depth
)
239 if (bytes_per_channel
== 1)
240 bytestream2_put_byte(&pbc
, in_buf
[x
]);
243 bytestream2_put_be16(&pbc
, ((uint16_t *)in_buf
)[x
]);
245 bytestream2_put_le16(&pbc
, ((uint16_t *)in_buf
)[x
]);
247 in_buf
-= p
->linesize
[0];
253 pkt
->size
= bytestream2_tell_p(&pbc
);
259 #define OFFSET(x) offsetof(SgiContext, x)
260 #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
261 static const AVOption options
[] = {
262 { "rle", "Use run-length compression", OFFSET(rle
), AV_OPT_TYPE_INT
, { .i64
= 1 }, 0, 1, VE
},
267 static const AVClass sgi_class
= {
269 .item_name
= av_default_item_name
,
271 .version
= LIBAVUTIL_VERSION_INT
,
274 const FFCodec ff_sgi_encoder
= {
276 CODEC_LONG_NAME("SGI image"),
277 .p
.type
= AVMEDIA_TYPE_VIDEO
,
278 .p
.id
= AV_CODEC_ID_SGI
,
279 .p
.capabilities
= AV_CODEC_CAP_DR1
| AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE
,
280 .priv_data_size
= sizeof(SgiContext
),
281 .p
.priv_class
= &sgi_class
,
283 FF_CODEC_ENCODE_CB(encode_frame
),
284 .p
.pix_fmts
= (const enum AVPixelFormat
[]) {
285 AV_PIX_FMT_RGB24
, AV_PIX_FMT_RGBA
,
286 AV_PIX_FMT_RGB48LE
, AV_PIX_FMT_RGB48BE
,
287 AV_PIX_FMT_RGBA64LE
, AV_PIX_FMT_RGBA64BE
,
288 AV_PIX_FMT_GRAY16LE
, AV_PIX_FMT_GRAY16BE
, AV_PIX_FMT_GRAY8
,