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 * PCX image encoder 24 * @file 25 * @author Daniel Verkamp 26 * @sa http://www.qzx.com/pc-gpe/pcx.txt 27 */ 28 29#include "avcodec.h" 30#include "bytestream.h" 31 32typedef struct PCXContext { 33 AVFrame picture; 34} PCXContext; 35 36static const uint32_t monoblack_pal[] = { 0x000000, 0xFFFFFF }; 37 38static av_cold int pcx_encode_init(AVCodecContext *avctx) 39{ 40 PCXContext *s = avctx->priv_data; 41 42 avcodec_get_frame_defaults(&s->picture); 43 avctx->coded_frame = &s->picture; 44 45 return 0; 46} 47 48/** 49 * PCX run-length encoder 50 * @param dst output buffer 51 * @param dst_size size of output buffer 52 * @param src input buffer 53 * @param src_plane_size size of one plane of input buffer in bytes 54 * @param nplanes number of planes in input buffer 55 * @return number of bytes written to dst or -1 on error 56 * @bug will not work for nplanes != 1 && bpp != 8 57 */ 58static int pcx_rle_encode( uint8_t *dst, int dst_size, 59 const uint8_t *src, int src_plane_size, int nplanes) 60{ 61 int p; 62 const uint8_t *dst_start = dst; 63 64 // check worst-case upper bound on dst_size 65 if (dst_size < 2LL * src_plane_size * nplanes || src_plane_size <= 0) 66 return -1; 67 68 for (p = 0; p < nplanes; p++) { 69 int count = 1; 70 const uint8_t *src_plane = src + p; 71 const uint8_t *src_plane_end = src_plane + src_plane_size * nplanes; 72 uint8_t prev = *src_plane; 73 src_plane += nplanes; 74 75 for (; ; src_plane += nplanes) { 76 if (src_plane < src_plane_end && *src_plane == prev && count < 0x3F) { 77 // current byte is same as prev 78 ++count; 79 } else { 80 // output prev * count 81 if (count != 1 || prev >= 0xC0) 82 *dst++ = 0xC0 | count; 83 *dst++ = prev; 84 85 if (src_plane == src_plane_end) 86 break; 87 88 // start new run 89 count = 1; 90 prev = *src_plane; 91 } 92 } 93 } 94 95 return dst - dst_start; 96} 97 98static int pcx_encode_frame(AVCodecContext *avctx, 99 unsigned char *buf, int buf_size, void *data) 100{ 101 PCXContext *s = avctx->priv_data; 102 AVFrame *const pict = &s->picture; 103 const uint8_t *buf_start = buf; 104 const uint8_t *buf_end = buf + buf_size; 105 106 int bpp, nplanes, i, y, line_bytes, written; 107 const uint32_t *pal = NULL; 108 const uint8_t *src; 109 110 *pict = *(AVFrame *)data; 111 pict->pict_type = FF_I_TYPE; 112 pict->key_frame = 1; 113 114 if (avctx->width > 65535 || avctx->height > 65535) { 115 av_log(avctx, AV_LOG_ERROR, "image dimensions do not fit in 16 bits\n"); 116 return -1; 117 } 118 119 switch (avctx->pix_fmt) { 120 case PIX_FMT_RGB24: 121 bpp = 8; 122 nplanes = 3; 123 break; 124 case PIX_FMT_RGB8: 125 case PIX_FMT_BGR8: 126 case PIX_FMT_RGB4_BYTE: 127 case PIX_FMT_BGR4_BYTE: 128 case PIX_FMT_GRAY8: 129 case PIX_FMT_PAL8: 130 bpp = 8; 131 nplanes = 1; 132 pal = (uint32_t *)pict->data[1]; 133 break; 134 case PIX_FMT_MONOBLACK: 135 bpp = 1; 136 nplanes = 1; 137 pal = monoblack_pal; 138 break; 139 default: 140 av_log(avctx, AV_LOG_ERROR, "unsupported pixfmt\n"); 141 return -1; 142 } 143 144 line_bytes = (avctx->width * bpp + 7) >> 3; 145 line_bytes = (line_bytes + 1) & ~1; 146 147 bytestream_put_byte(&buf, 10); // manufacturer 148 bytestream_put_byte(&buf, 5); // version 149 bytestream_put_byte(&buf, 1); // encoding 150 bytestream_put_byte(&buf, bpp); // bits per pixel per plane 151 bytestream_put_le16(&buf, 0); // x min 152 bytestream_put_le16(&buf, 0); // y min 153 bytestream_put_le16(&buf, avctx->width - 1); // x max 154 bytestream_put_le16(&buf, avctx->height - 1); // y max 155 bytestream_put_le16(&buf, 0); // horizontal DPI 156 bytestream_put_le16(&buf, 0); // vertical DPI 157 for (i = 0; i < 16; i++) 158 bytestream_put_be24(&buf, pal ? pal[i] : 0);// palette (<= 16 color only) 159 bytestream_put_byte(&buf, 0); // reserved 160 bytestream_put_byte(&buf, nplanes); // number of planes 161 bytestream_put_le16(&buf, line_bytes); // scanline plane size in bytes 162 163 while (buf - buf_start < 128) 164 *buf++= 0; 165 166 src = pict->data[0]; 167 168 for (y = 0; y < avctx->height; y++) { 169 if ((written = pcx_rle_encode(buf, buf_end - buf, 170 src, line_bytes, nplanes)) < 0) { 171 av_log(avctx, AV_LOG_ERROR, "buffer too small\n"); 172 return -1; 173 } 174 buf += written; 175 src += pict->linesize[0]; 176 } 177 178 if (nplanes == 1 && bpp == 8) { 179 if (buf_end - buf < 257) { 180 av_log(avctx, AV_LOG_ERROR, "buffer too small\n"); 181 return -1; 182 } 183 bytestream_put_byte(&buf, 12); 184 for (i = 0; i < 256; i++) { 185 bytestream_put_be24(&buf, pal[i]); 186 } 187 } 188 189 return buf - buf_start; 190} 191 192AVCodec pcx_encoder = { 193 "pcx", 194 AVMEDIA_TYPE_VIDEO, 195 CODEC_ID_PCX, 196 sizeof(PCXContext), 197 pcx_encode_init, 198 pcx_encode_frame, 199 NULL, 200 .pix_fmts = (const enum PixelFormat[]){ 201 PIX_FMT_RGB24, 202 PIX_FMT_RGB8, PIX_FMT_BGR8, PIX_FMT_RGB4_BYTE, PIX_FMT_BGR4_BYTE, PIX_FMT_GRAY8, PIX_FMT_PAL8, 203 PIX_FMT_MONOBLACK, 204 PIX_FMT_NONE}, 205 .long_name = NULL_IF_CONFIG_SMALL("PC Paintbrush PCX image"), 206}; 207