00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00029 #include "avcodec.h"
00030 #include "bytestream.h"
00031 #include "libavutil/imgutils.h"
00032 #include "internal.h"
00033
00034 typedef struct PCXContext {
00035 AVFrame picture;
00036 } PCXContext;
00037
00038 static const uint32_t monoblack_pal[16] = { 0x000000, 0xFFFFFF };
00039
00040 static av_cold int pcx_encode_init(AVCodecContext *avctx)
00041 {
00042 PCXContext *s = avctx->priv_data;
00043
00044 avcodec_get_frame_defaults(&s->picture);
00045 avctx->coded_frame = &s->picture;
00046
00047 return 0;
00048 }
00049
00060 static int pcx_rle_encode( uint8_t *dst, int dst_size,
00061 const uint8_t *src, int src_plane_size, int nplanes)
00062 {
00063 int p;
00064 const uint8_t *dst_start = dst;
00065
00066
00067 if (dst_size < 2LL * src_plane_size * nplanes || src_plane_size <= 0)
00068 return -1;
00069
00070 for (p = 0; p < nplanes; p++) {
00071 int count = 1;
00072 const uint8_t *src_plane = src + p;
00073 const uint8_t *src_plane_end = src_plane + src_plane_size * nplanes;
00074 uint8_t prev = *src_plane;
00075 src_plane += nplanes;
00076
00077 for (; ; src_plane += nplanes) {
00078 if (src_plane < src_plane_end && *src_plane == prev && count < 0x3F) {
00079
00080 ++count;
00081 } else {
00082
00083 if (count != 1 || prev >= 0xC0)
00084 *dst++ = 0xC0 | count;
00085 *dst++ = prev;
00086
00087 if (src_plane == src_plane_end)
00088 break;
00089
00090
00091 count = 1;
00092 prev = *src_plane;
00093 }
00094 }
00095 }
00096
00097 return dst - dst_start;
00098 }
00099
00100 static int pcx_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
00101 const AVFrame *frame, int *got_packet)
00102 {
00103 PCXContext *s = avctx->priv_data;
00104 AVFrame *const pict = &s->picture;
00105 const uint8_t *buf_end;
00106 uint8_t *buf;
00107
00108 int bpp, nplanes, i, y, line_bytes, written, ret, max_pkt_size, sw, sh;
00109 const uint32_t *pal = NULL;
00110 uint32_t palette256[256];
00111 const uint8_t *src;
00112
00113 *pict = *frame;
00114 pict->pict_type = AV_PICTURE_TYPE_I;
00115 pict->key_frame = 1;
00116
00117 if (avctx->width > 65535 || avctx->height > 65535) {
00118 av_log(avctx, AV_LOG_ERROR, "image dimensions do not fit in 16 bits\n");
00119 return -1;
00120 }
00121
00122 switch (avctx->pix_fmt) {
00123 case AV_PIX_FMT_RGB24:
00124 bpp = 8;
00125 nplanes = 3;
00126 break;
00127 case AV_PIX_FMT_RGB8:
00128 case AV_PIX_FMT_BGR8:
00129 case AV_PIX_FMT_RGB4_BYTE:
00130 case AV_PIX_FMT_BGR4_BYTE:
00131 case AV_PIX_FMT_GRAY8:
00132 bpp = 8;
00133 nplanes = 1;
00134 avpriv_set_systematic_pal2(palette256, avctx->pix_fmt);
00135 pal = palette256;
00136 break;
00137 case AV_PIX_FMT_PAL8:
00138 bpp = 8;
00139 nplanes = 1;
00140 pal = (uint32_t *)pict->data[1];
00141 break;
00142 case AV_PIX_FMT_MONOBLACK:
00143 bpp = 1;
00144 nplanes = 1;
00145 pal = monoblack_pal;
00146 break;
00147 default:
00148 av_log(avctx, AV_LOG_ERROR, "unsupported pixfmt\n");
00149 return -1;
00150 }
00151
00152 line_bytes = (avctx->width * bpp + 7) >> 3;
00153 line_bytes = (line_bytes + 1) & ~1;
00154
00155 max_pkt_size = 128 + avctx->height * 2 * line_bytes * nplanes + (pal ? 256*3 + 1 : 0);
00156 if ((ret = ff_alloc_packet2(avctx, pkt, max_pkt_size)) < 0)
00157 return ret;
00158 buf = pkt->data;
00159 buf_end = pkt->data + pkt->size;
00160
00161 sw = avctx->sample_aspect_ratio.num;
00162 sh = avctx->sample_aspect_ratio.den;
00163 if (sw > 0xFFFFu || sh > 0xFFFFu)
00164 av_reduce(&sw, &sh, sw, sh, 0xFFFFu);
00165
00166 bytestream_put_byte(&buf, 10);
00167 bytestream_put_byte(&buf, 5);
00168 bytestream_put_byte(&buf, 1);
00169 bytestream_put_byte(&buf, bpp);
00170 bytestream_put_le16(&buf, 0);
00171 bytestream_put_le16(&buf, 0);
00172 bytestream_put_le16(&buf, avctx->width - 1);
00173 bytestream_put_le16(&buf, avctx->height - 1);
00174 bytestream_put_le16(&buf, sw);
00175 bytestream_put_le16(&buf, sh);
00176 for (i = 0; i < 16; i++)
00177 bytestream_put_be24(&buf, pal ? pal[i] : 0);
00178 bytestream_put_byte(&buf, 0);
00179 bytestream_put_byte(&buf, nplanes);
00180 bytestream_put_le16(&buf, line_bytes);
00181
00182 while (buf - pkt->data < 128)
00183 *buf++= 0;
00184
00185 src = pict->data[0];
00186
00187 for (y = 0; y < avctx->height; y++) {
00188 if ((written = pcx_rle_encode(buf, buf_end - buf,
00189 src, line_bytes, nplanes)) < 0) {
00190 av_log(avctx, AV_LOG_ERROR, "buffer too small\n");
00191 return -1;
00192 }
00193 buf += written;
00194 src += pict->linesize[0];
00195 }
00196
00197 if (nplanes == 1 && bpp == 8) {
00198 if (buf_end - buf < 257) {
00199 av_log(avctx, AV_LOG_ERROR, "buffer too small\n");
00200 return -1;
00201 }
00202 bytestream_put_byte(&buf, 12);
00203 for (i = 0; i < 256; i++) {
00204 bytestream_put_be24(&buf, pal[i]);
00205 }
00206 }
00207
00208 pkt->size = buf - pkt->data;
00209 pkt->flags |= AV_PKT_FLAG_KEY;
00210 *got_packet = 1;
00211
00212 return 0;
00213 }
00214
00215 AVCodec ff_pcx_encoder = {
00216 .name = "pcx",
00217 .type = AVMEDIA_TYPE_VIDEO,
00218 .id = AV_CODEC_ID_PCX,
00219 .priv_data_size = sizeof(PCXContext),
00220 .init = pcx_encode_init,
00221 .encode2 = pcx_encode_frame,
00222 .pix_fmts = (const enum AVPixelFormat[]){
00223 AV_PIX_FMT_RGB24,
00224 AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8, AV_PIX_FMT_RGB4_BYTE, AV_PIX_FMT_BGR4_BYTE,
00225 AV_PIX_FMT_GRAY8, AV_PIX_FMT_PAL8,
00226 AV_PIX_FMT_MONOBLACK,
00227 AV_PIX_FMT_NONE
00228 },
00229 .long_name = NULL_IF_CONFIG_SMALL("PC Paintbrush PCX image"),
00230 };