/src/ffmpeg/libavcodec/pcxenc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * PC Paintbrush PCX (.pcx) image encoder |
3 | | * Copyright (c) 2009 Daniel Verkamp <daniel at drv.nu> |
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 | | /** |
23 | | * @file |
24 | | * PCX image encoder |
25 | | * @author Daniel Verkamp |
26 | | * @see http://bespin.org/~qz/pc-gpe/pcx.txt |
27 | | */ |
28 | | |
29 | | #include "libavutil/imgutils_internal.h" |
30 | | #include "avcodec.h" |
31 | | #include "bytestream.h" |
32 | | #include "codec_internal.h" |
33 | | #include "encode.h" |
34 | | |
35 | | static const uint32_t monoblack_pal[16] = { 0x000000, 0xFFFFFF }; |
36 | | |
37 | | /** |
38 | | * PCX run-length encoder |
39 | | * @param dst output buffer |
40 | | * @param dst_size size of output buffer |
41 | | * @param src input buffer |
42 | | * @param src_plane_size size of one plane of input buffer in bytes |
43 | | * @param nplanes number of planes in input buffer |
44 | | * @return number of bytes written to dst or -1 on error |
45 | | * @bug will not work for nplanes != 1 && bpp != 8 |
46 | | */ |
47 | | static int pcx_rle_encode( uint8_t *dst, int dst_size, |
48 | | const uint8_t *src, int src_plane_size, int nplanes) |
49 | 2.29M | { |
50 | 2.29M | int p; |
51 | 2.29M | const uint8_t *dst_start = dst; |
52 | | |
53 | | // check worst-case upper bound on dst_size |
54 | 2.29M | if (dst_size < 2LL * src_plane_size * nplanes || src_plane_size <= 0) |
55 | 0 | return AVERROR(EINVAL); |
56 | | |
57 | 7.12M | for (p = 0; p < nplanes; p++) { |
58 | 4.82M | int count = 1; |
59 | 4.82M | const uint8_t *src_plane = src + p; |
60 | 4.82M | const uint8_t *src_plane_end = src_plane + src_plane_size * nplanes; |
61 | 4.82M | uint8_t prev = *src_plane; |
62 | 4.82M | src_plane += nplanes; |
63 | | |
64 | 26.9M | for (; ; src_plane += nplanes) { |
65 | 26.9M | if (src_plane < src_plane_end && *src_plane == prev && count < 0x3F) { |
66 | | // current byte is same as prev |
67 | 20.8M | ++count; |
68 | 20.8M | } else { |
69 | | // output prev * count |
70 | 6.07M | if (count != 1 || prev >= 0xC0) |
71 | 5.29M | *dst++ = 0xC0 | count; |
72 | 6.07M | *dst++ = prev; |
73 | | |
74 | 6.07M | if (src_plane == src_plane_end) |
75 | 4.82M | break; |
76 | | |
77 | | // start new run |
78 | 1.25M | count = 1; |
79 | 1.25M | prev = *src_plane; |
80 | 1.25M | } |
81 | 26.9M | } |
82 | 4.82M | } |
83 | | |
84 | 2.29M | return dst - dst_start; |
85 | 2.29M | } |
86 | | |
87 | | static int pcx_encode_frame(AVCodecContext *avctx, AVPacket *pkt, |
88 | | const AVFrame *frame, int *got_packet) |
89 | 9.53k | { |
90 | 9.53k | const uint8_t *buf_end; |
91 | 9.53k | uint8_t *buf; |
92 | | |
93 | 9.53k | int bpp, nplanes, i, y, line_bytes, written, ret, max_pkt_size, sw, sh; |
94 | 9.53k | const uint32_t *pal = NULL; |
95 | 9.53k | uint32_t palette256[256]; |
96 | 9.53k | const uint8_t *src; |
97 | | |
98 | 9.53k | if (avctx->width > 65535 || avctx->height > 65535) { |
99 | 0 | av_log(avctx, AV_LOG_ERROR, "image dimensions do not fit in 16 bits\n"); |
100 | 0 | return AVERROR(EINVAL); |
101 | 0 | } |
102 | | |
103 | 9.53k | switch (avctx->pix_fmt) { |
104 | 283 | case AV_PIX_FMT_RGB24: |
105 | 283 | bpp = 8; |
106 | 283 | nplanes = 3; |
107 | 283 | break; |
108 | 2.74k | case AV_PIX_FMT_RGB8: |
109 | 2.99k | case AV_PIX_FMT_BGR8: |
110 | 3.46k | case AV_PIX_FMT_RGB4_BYTE: |
111 | 6.45k | case AV_PIX_FMT_BGR4_BYTE: |
112 | 8.65k | case AV_PIX_FMT_GRAY8: |
113 | 8.65k | bpp = 8; |
114 | 8.65k | nplanes = 1; |
115 | 8.65k | avpriv_set_systematic_pal2(palette256, avctx->pix_fmt); |
116 | 8.65k | pal = palette256; |
117 | 8.65k | break; |
118 | 265 | case AV_PIX_FMT_PAL8: |
119 | 265 | bpp = 8; |
120 | 265 | nplanes = 1; |
121 | 265 | pal = (uint32_t *)frame->data[1]; |
122 | 265 | break; |
123 | 334 | case AV_PIX_FMT_MONOBLACK: |
124 | 334 | bpp = 1; |
125 | 334 | nplanes = 1; |
126 | 334 | pal = monoblack_pal; |
127 | 334 | break; |
128 | 0 | default: |
129 | 0 | av_log(avctx, AV_LOG_ERROR, "unsupported pixfmt\n"); |
130 | 0 | return AVERROR(EINVAL); |
131 | 9.53k | } |
132 | | |
133 | 9.53k | line_bytes = (avctx->width * bpp + 7) >> 3; |
134 | 9.53k | line_bytes = (line_bytes + 1) & ~1; |
135 | | |
136 | 9.53k | max_pkt_size = 128 + avctx->height * 2 * line_bytes * nplanes + (pal ? 256*3 + 1 : 0); |
137 | 9.53k | if ((ret = ff_alloc_packet(avctx, pkt, max_pkt_size)) < 0) |
138 | 0 | return ret; |
139 | 9.53k | buf = pkt->data; |
140 | 9.53k | buf_end = pkt->data + pkt->size; |
141 | | |
142 | 9.53k | sw = avctx->sample_aspect_ratio.num; |
143 | 9.53k | sh = avctx->sample_aspect_ratio.den; |
144 | 9.53k | if (sw > 0xFFFFu || sh > 0xFFFFu) |
145 | 0 | av_reduce(&sw, &sh, sw, sh, 0xFFFFu); |
146 | | |
147 | 9.53k | bytestream_put_byte(&buf, 10); // manufacturer |
148 | 9.53k | bytestream_put_byte(&buf, 5); // version |
149 | 9.53k | bytestream_put_byte(&buf, 1); // encoding |
150 | 9.53k | bytestream_put_byte(&buf, bpp); // bits per pixel per plane |
151 | 9.53k | bytestream_put_le16(&buf, 0); // x min |
152 | 9.53k | bytestream_put_le16(&buf, 0); // y min |
153 | 9.53k | bytestream_put_le16(&buf, avctx->width - 1); // x max |
154 | 9.53k | bytestream_put_le16(&buf, avctx->height - 1); // y max |
155 | 9.53k | bytestream_put_le16(&buf, sw); // horizontal DPI |
156 | 9.53k | bytestream_put_le16(&buf, sh); // vertical DPI |
157 | 162k | for (i = 0; i < 16; i++) |
158 | 152k | bytestream_put_be24(&buf, pal ? pal[i] : 0);// palette (<= 16 color only) |
159 | 9.53k | bytestream_put_byte(&buf, 0); // reserved |
160 | 9.53k | bytestream_put_byte(&buf, nplanes); // number of planes |
161 | 9.53k | bytestream_put_le16(&buf, line_bytes); // scanline plane size in bytes |
162 | | |
163 | 581k | while (buf - pkt->data < 128) |
164 | 571k | *buf++= 0; |
165 | | |
166 | 9.53k | src = frame->data[0]; |
167 | | |
168 | 2.30M | for (y = 0; y < avctx->height; y++) { |
169 | 2.29M | if ((written = pcx_rle_encode(buf, buf_end - buf, |
170 | 2.29M | src, line_bytes, nplanes)) < 0) { |
171 | 0 | av_log(avctx, AV_LOG_ERROR, "buffer too small\n"); |
172 | 0 | return AVERROR_BUG; |
173 | 0 | } |
174 | 2.29M | buf += written; |
175 | 2.29M | src += frame->linesize[0]; |
176 | 2.29M | } |
177 | | |
178 | 9.53k | if (nplanes == 1 && bpp == 8) { |
179 | 8.91k | if (buf_end - buf < 257) { |
180 | 0 | av_log(avctx, AV_LOG_ERROR, "buffer too small\n"); |
181 | 0 | return AVERROR_BUG; |
182 | 0 | } |
183 | 8.91k | bytestream_put_byte(&buf, 12); |
184 | 2.29M | for (i = 0; i < 256; i++) { |
185 | 2.28M | bytestream_put_be24(&buf, pal[i]); |
186 | 2.28M | } |
187 | 8.91k | } |
188 | | |
189 | 9.53k | pkt->size = buf - pkt->data; |
190 | 9.53k | *got_packet = 1; |
191 | | |
192 | 9.53k | return 0; |
193 | 9.53k | } |
194 | | |
195 | | const FFCodec ff_pcx_encoder = { |
196 | | .p.name = "pcx", |
197 | | CODEC_LONG_NAME("PC Paintbrush PCX image"), |
198 | | .p.type = AVMEDIA_TYPE_VIDEO, |
199 | | .p.id = AV_CODEC_ID_PCX, |
200 | | .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, |
201 | | FF_CODEC_ENCODE_CB(pcx_encode_frame), |
202 | | .p.pix_fmts = (const enum AVPixelFormat[]){ |
203 | | AV_PIX_FMT_RGB24, |
204 | | AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8, AV_PIX_FMT_RGB4_BYTE, AV_PIX_FMT_BGR4_BYTE, |
205 | | AV_PIX_FMT_GRAY8, AV_PIX_FMT_PAL8, |
206 | | AV_PIX_FMT_MONOBLACK, |
207 | | AV_PIX_FMT_NONE |
208 | | }, |
209 | | }; |