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://www.qzx.com/pc-gpe/pcx.txt 27 */ 28 29#include "avcodec.h" 30#include "bytestream.h" 31#include "libavutil/imgutils.h" 32#include "internal.h" 33 34static const uint32_t monoblack_pal[16] = { 0x000000, 0xFFFFFF }; 35 36static av_cold int pcx_encode_init(AVCodecContext *avctx) 37{ 38 avctx->coded_frame = av_frame_alloc(); 39 if (!avctx->coded_frame) 40 return AVERROR(ENOMEM); 41 42 avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I; 43 avctx->coded_frame->key_frame = 1; 44 45 return 0; 46} 47 48static av_cold int pcx_encode_close(AVCodecContext *avctx) 49{ 50 av_frame_free(&avctx->coded_frame); 51 return 0; 52} 53 54/** 55 * PCX run-length encoder 56 * @param dst output buffer 57 * @param dst_size size of output buffer 58 * @param src input buffer 59 * @param src_plane_size size of one plane of input buffer in bytes 60 * @param nplanes number of planes in input buffer 61 * @return number of bytes written to dst or -1 on error 62 * @bug will not work for nplanes != 1 && bpp != 8 63 */ 64static int pcx_rle_encode( uint8_t *dst, int dst_size, 65 const uint8_t *src, int src_plane_size, int nplanes) 66{ 67 int p; 68 const uint8_t *dst_start = dst; 69 70 // check worst-case upper bound on dst_size 71 if (dst_size < 2LL * src_plane_size * nplanes || src_plane_size <= 0) 72 return -1; 73 74 for (p = 0; p < nplanes; p++) { 75 int count = 1; 76 const uint8_t *src_plane = src + p; 77 const uint8_t *src_plane_end = src_plane + src_plane_size * nplanes; 78 uint8_t prev = *src_plane; 79 src_plane += nplanes; 80 81 for (; ; src_plane += nplanes) { 82 if (src_plane < src_plane_end && *src_plane == prev && count < 0x3F) { 83 // current byte is same as prev 84 ++count; 85 } else { 86 // output prev * count 87 if (count != 1 || prev >= 0xC0) 88 *dst++ = 0xC0 | count; 89 *dst++ = prev; 90 91 if (src_plane == src_plane_end) 92 break; 93 94 // start new run 95 count = 1; 96 prev = *src_plane; 97 } 98 } 99 } 100 101 return dst - dst_start; 102} 103 104static int pcx_encode_frame(AVCodecContext *avctx, AVPacket *pkt, 105 const AVFrame *frame, int *got_packet) 106{ 107 const uint8_t *buf_end; 108 uint8_t *buf; 109 110 int bpp, nplanes, i, y, line_bytes, written, ret, max_pkt_size, sw, sh; 111 const uint32_t *pal = NULL; 112 uint32_t palette256[256]; 113 const uint8_t *src; 114 115 if (avctx->width > 65535 || avctx->height > 65535) { 116 av_log(avctx, AV_LOG_ERROR, "image dimensions do not fit in 16 bits\n"); 117 return -1; 118 } 119 120 switch (avctx->pix_fmt) { 121 case AV_PIX_FMT_RGB24: 122 bpp = 8; 123 nplanes = 3; 124 break; 125 case AV_PIX_FMT_RGB8: 126 case AV_PIX_FMT_BGR8: 127 case AV_PIX_FMT_RGB4_BYTE: 128 case AV_PIX_FMT_BGR4_BYTE: 129 case AV_PIX_FMT_GRAY8: 130 bpp = 8; 131 nplanes = 1; 132 avpriv_set_systematic_pal2(palette256, avctx->pix_fmt); 133 pal = palette256; 134 break; 135 case AV_PIX_FMT_PAL8: 136 bpp = 8; 137 nplanes = 1; 138 pal = (uint32_t *)frame->data[1]; 139 break; 140 case AV_PIX_FMT_MONOBLACK: 141 bpp = 1; 142 nplanes = 1; 143 pal = monoblack_pal; 144 break; 145 default: 146 av_log(avctx, AV_LOG_ERROR, "unsupported pixfmt\n"); 147 return -1; 148 } 149 150 line_bytes = (avctx->width * bpp + 7) >> 3; 151 line_bytes = (line_bytes + 1) & ~1; 152 153 max_pkt_size = 128 + avctx->height * 2 * line_bytes * nplanes + (pal ? 256*3 + 1 : 0); 154 if ((ret = ff_alloc_packet2(avctx, pkt, max_pkt_size)) < 0) 155 return ret; 156 buf = pkt->data; 157 buf_end = pkt->data + pkt->size; 158 159 sw = avctx->sample_aspect_ratio.num; 160 sh = avctx->sample_aspect_ratio.den; 161 if (sw > 0xFFFFu || sh > 0xFFFFu) 162 av_reduce(&sw, &sh, sw, sh, 0xFFFFu); 163 164 bytestream_put_byte(&buf, 10); // manufacturer 165 bytestream_put_byte(&buf, 5); // version 166 bytestream_put_byte(&buf, 1); // encoding 167 bytestream_put_byte(&buf, bpp); // bits per pixel per plane 168 bytestream_put_le16(&buf, 0); // x min 169 bytestream_put_le16(&buf, 0); // y min 170 bytestream_put_le16(&buf, avctx->width - 1); // x max 171 bytestream_put_le16(&buf, avctx->height - 1); // y max 172 bytestream_put_le16(&buf, sw); // horizontal DPI 173 bytestream_put_le16(&buf, sh); // vertical DPI 174 for (i = 0; i < 16; i++) 175 bytestream_put_be24(&buf, pal ? pal[i] : 0);// palette (<= 16 color only) 176 bytestream_put_byte(&buf, 0); // reserved 177 bytestream_put_byte(&buf, nplanes); // number of planes 178 bytestream_put_le16(&buf, line_bytes); // scanline plane size in bytes 179 180 while (buf - pkt->data < 128) 181 *buf++= 0; 182 183 src = frame->data[0]; 184 185 for (y = 0; y < avctx->height; y++) { 186 if ((written = pcx_rle_encode(buf, buf_end - buf, 187 src, line_bytes, nplanes)) < 0) { 188 av_log(avctx, AV_LOG_ERROR, "buffer too small\n"); 189 return -1; 190 } 191 buf += written; 192 src += frame->linesize[0]; 193 } 194 195 if (nplanes == 1 && bpp == 8) { 196 if (buf_end - buf < 257) { 197 av_log(avctx, AV_LOG_ERROR, "buffer too small\n"); 198 return -1; 199 } 200 bytestream_put_byte(&buf, 12); 201 for (i = 0; i < 256; i++) { 202 bytestream_put_be24(&buf, pal[i]); 203 } 204 } 205 206 pkt->size = buf - pkt->data; 207 pkt->flags |= AV_PKT_FLAG_KEY; 208 *got_packet = 1; 209 210 return 0; 211} 212 213AVCodec ff_pcx_encoder = { 214 .name = "pcx", 215 .long_name = NULL_IF_CONFIG_SMALL("PC Paintbrush PCX image"), 216 .type = AVMEDIA_TYPE_VIDEO, 217 .id = AV_CODEC_ID_PCX, 218 .init = pcx_encode_init, 219 .close = pcx_encode_close, 220 .encode2 = pcx_encode_frame, 221 .pix_fmts = (const enum AVPixelFormat[]){ 222 AV_PIX_FMT_RGB24, 223 AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8, AV_PIX_FMT_RGB4_BYTE, AV_PIX_FMT_BGR4_BYTE, 224 AV_PIX_FMT_GRAY8, AV_PIX_FMT_PAL8, 225 AV_PIX_FMT_MONOBLACK, 226 AV_PIX_FMT_NONE 227 }, 228}; 229