/src/ffmpeg/libavcodec/pcxenc.c
Line | Count | Source |
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 | 1.95M | { |
50 | 1.95M | int p; |
51 | 1.95M | const uint8_t *dst_start = dst; |
52 | | |
53 | | // check worst-case upper bound on dst_size |
54 | 1.95M | if (dst_size < 2LL * src_plane_size * nplanes || src_plane_size <= 0) |
55 | 0 | return AVERROR(EINVAL); |
56 | | |
57 | 5.89M | for (p = 0; p < nplanes; p++) { |
58 | 3.93M | int count = 1; |
59 | 3.93M | const uint8_t *src_plane = src + p; |
60 | 3.93M | const uint8_t *src_plane_end = src_plane + src_plane_size * nplanes; |
61 | 3.93M | uint8_t prev = *src_plane; |
62 | 3.93M | src_plane += nplanes; |
63 | | |
64 | 25.3M | for (; ; src_plane += nplanes) { |
65 | 25.3M | if (src_plane < src_plane_end && *src_plane == prev && count < 0x3F) { |
66 | | // current byte is same as prev |
67 | 20.4M | ++count; |
68 | 20.4M | } else { |
69 | | // output prev * count |
70 | 4.90M | if (count != 1 || prev >= 0xC0) |
71 | 4.33M | *dst++ = 0xC0 | count; |
72 | 4.90M | *dst++ = prev; |
73 | | |
74 | 4.90M | if (src_plane == src_plane_end) |
75 | 3.93M | break; |
76 | | |
77 | | // start new run |
78 | 974k | count = 1; |
79 | 974k | prev = *src_plane; |
80 | 974k | } |
81 | 25.3M | } |
82 | 3.93M | } |
83 | | |
84 | 1.95M | return dst - dst_start; |
85 | 1.95M | } |
86 | | |
87 | | static int pcx_encode_frame(AVCodecContext *avctx, AVPacket *pkt, |
88 | | const AVFrame *frame, int *got_packet) |
89 | 11.8k | { |
90 | 11.8k | const uint8_t *buf_end; |
91 | 11.8k | uint8_t *buf; |
92 | | |
93 | 11.8k | int bpp, nplanes, i, y, line_bytes, written, ret, max_pkt_size, sw, sh; |
94 | 11.8k | const uint32_t *pal = NULL; |
95 | 11.8k | uint32_t palette256[256]; |
96 | 11.8k | const uint8_t *src; |
97 | | |
98 | 11.8k | 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 | 11.8k | switch (avctx->pix_fmt) { |
104 | 256 | case AV_PIX_FMT_RGB24: |
105 | 256 | bpp = 8; |
106 | 256 | nplanes = 3; |
107 | 256 | break; |
108 | 1.66k | case AV_PIX_FMT_RGB8: |
109 | 2.51k | case AV_PIX_FMT_BGR8: |
110 | 3.39k | case AV_PIX_FMT_RGB4_BYTE: |
111 | 5.40k | case AV_PIX_FMT_BGR4_BYTE: |
112 | 9.70k | case AV_PIX_FMT_GRAY8: |
113 | 9.70k | bpp = 8; |
114 | 9.70k | nplanes = 1; |
115 | 9.70k | avpriv_set_systematic_pal2(palette256, avctx->pix_fmt); |
116 | 9.70k | pal = palette256; |
117 | 9.70k | break; |
118 | 264 | case AV_PIX_FMT_PAL8: |
119 | 264 | bpp = 8; |
120 | 264 | nplanes = 1; |
121 | 264 | pal = (uint32_t *)frame->data[1]; |
122 | 264 | break; |
123 | 1.61k | case AV_PIX_FMT_MONOBLACK: |
124 | 1.61k | bpp = 1; |
125 | 1.61k | nplanes = 1; |
126 | 1.61k | pal = monoblack_pal; |
127 | 1.61k | break; |
128 | 0 | default: |
129 | 0 | av_log(avctx, AV_LOG_ERROR, "unsupported pixfmt\n"); |
130 | 0 | return AVERROR(EINVAL); |
131 | 11.8k | } |
132 | | |
133 | 11.8k | line_bytes = (avctx->width * bpp + 7) >> 3; |
134 | 11.8k | line_bytes = (line_bytes + 1) & ~1; |
135 | | |
136 | 11.8k | max_pkt_size = 128 + avctx->height * 2 * line_bytes * nplanes + (pal ? 256*3 + 1 : 0); |
137 | 11.8k | if ((ret = ff_alloc_packet(avctx, pkt, max_pkt_size)) < 0) |
138 | 0 | return ret; |
139 | 11.8k | buf = pkt->data; |
140 | 11.8k | buf_end = pkt->data + pkt->size; |
141 | | |
142 | 11.8k | sw = avctx->sample_aspect_ratio.num; |
143 | 11.8k | sh = avctx->sample_aspect_ratio.den; |
144 | 11.8k | if (sw > 0xFFFFu || sh > 0xFFFFu) |
145 | 0 | av_reduce(&sw, &sh, sw, sh, 0xFFFFu); |
146 | | |
147 | 11.8k | bytestream_put_byte(&buf, 10); // manufacturer |
148 | 11.8k | bytestream_put_byte(&buf, 5); // version |
149 | 11.8k | bytestream_put_byte(&buf, 1); // encoding |
150 | 11.8k | bytestream_put_byte(&buf, bpp); // bits per pixel per plane |
151 | 11.8k | bytestream_put_le16(&buf, 0); // x min |
152 | 11.8k | bytestream_put_le16(&buf, 0); // y min |
153 | 11.8k | bytestream_put_le16(&buf, avctx->width - 1); // x max |
154 | 11.8k | bytestream_put_le16(&buf, avctx->height - 1); // y max |
155 | 11.8k | bytestream_put_le16(&buf, sw); // horizontal DPI |
156 | 11.8k | bytestream_put_le16(&buf, sh); // vertical DPI |
157 | 201k | for (i = 0; i < 16; i++) |
158 | 189k | bytestream_put_be24(&buf, pal ? pal[i] : 0);// palette (<= 16 color only) |
159 | 11.8k | bytestream_put_byte(&buf, 0); // reserved |
160 | 11.8k | bytestream_put_byte(&buf, nplanes); // number of planes |
161 | 11.8k | bytestream_put_le16(&buf, line_bytes); // scanline plane size in bytes |
162 | | |
163 | 722k | while (buf - pkt->data < 128) |
164 | 710k | *buf++= 0; |
165 | | |
166 | 11.8k | src = frame->data[0]; |
167 | | |
168 | 1.96M | for (y = 0; y < avctx->height; y++) { |
169 | 1.95M | if ((written = pcx_rle_encode(buf, buf_end - buf, |
170 | 1.95M | 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 | 1.95M | buf += written; |
175 | 1.95M | src += frame->linesize[0]; |
176 | 1.95M | } |
177 | | |
178 | 11.8k | if (nplanes == 1 && bpp == 8) { |
179 | 9.97k | 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 | 9.97k | bytestream_put_byte(&buf, 12); |
184 | 2.56M | for (i = 0; i < 256; i++) { |
185 | 2.55M | bytestream_put_be24(&buf, pal[i]); |
186 | 2.55M | } |
187 | 9.97k | } |
188 | | |
189 | 11.8k | pkt->size = buf - pkt->data; |
190 | 11.8k | *got_packet = 1; |
191 | | |
192 | 11.8k | return 0; |
193 | 11.8k | } |
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 | | CODEC_PIXFMTS(AV_PIX_FMT_RGB24, |
203 | | AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8, |
204 | | 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 | | }; |