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 "avcodec.h" 30#include "bytestream.h" 31#include "libavutil/imgutils.h" 32#include "codec_internal.h" 33#include "encode.h" 34 35static 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 */ 47static int pcx_rle_encode( uint8_t *dst, int dst_size, 48 const uint8_t *src, int src_plane_size, int nplanes) 49{ 50 int p; 51 const uint8_t *dst_start = dst; 52 53 // check worst-case upper bound on dst_size 54 if (dst_size < 2LL * src_plane_size * nplanes || src_plane_size <= 0) 55 return AVERROR(EINVAL); 56 57 for (p = 0; p < nplanes; p++) { 58 int count = 1; 59 const uint8_t *src_plane = src + p; 60 const uint8_t *src_plane_end = src_plane + src_plane_size * nplanes; 61 uint8_t prev = *src_plane; 62 src_plane += nplanes; 63 64 for (; ; src_plane += nplanes) { 65 if (src_plane < src_plane_end && *src_plane == prev && count < 0x3F) { 66 // current byte is same as prev 67 ++count; 68 } else { 69 // output prev * count 70 if (count != 1 || prev >= 0xC0) 71 *dst++ = 0xC0 | count; 72 *dst++ = prev; 73 74 if (src_plane == src_plane_end) 75 break; 76 77 // start new run 78 count = 1; 79 prev = *src_plane; 80 } 81 } 82 } 83 84 return dst - dst_start; 85} 86 87static int pcx_encode_frame(AVCodecContext *avctx, AVPacket *pkt, 88 const AVFrame *frame, int *got_packet) 89{ 90 const uint8_t *buf_end; 91 uint8_t *buf; 92 93 int bpp, nplanes, i, y, line_bytes, written, ret, max_pkt_size, sw, sh; 94 const uint32_t *pal = NULL; 95 uint32_t palette256[256]; 96 const uint8_t *src; 97 98 if (avctx->width > 65535 || avctx->height > 65535) { 99 av_log(avctx, AV_LOG_ERROR, "image dimensions do not fit in 16 bits\n"); 100 return AVERROR(EINVAL); 101 } 102 103 switch (avctx->pix_fmt) { 104 case AV_PIX_FMT_RGB24: 105 bpp = 8; 106 nplanes = 3; 107 break; 108 case AV_PIX_FMT_RGB8: 109 case AV_PIX_FMT_BGR8: 110 case AV_PIX_FMT_RGB4_BYTE: 111 case AV_PIX_FMT_BGR4_BYTE: 112 case AV_PIX_FMT_GRAY8: 113 bpp = 8; 114 nplanes = 1; 115 avpriv_set_systematic_pal2(palette256, avctx->pix_fmt); 116 pal = palette256; 117 break; 118 case AV_PIX_FMT_PAL8: 119 bpp = 8; 120 nplanes = 1; 121 pal = (uint32_t *)frame->data[1]; 122 break; 123 case AV_PIX_FMT_MONOBLACK: 124 bpp = 1; 125 nplanes = 1; 126 pal = monoblack_pal; 127 break; 128 default: 129 av_log(avctx, AV_LOG_ERROR, "unsupported pixfmt\n"); 130 return AVERROR(EINVAL); 131 } 132 133 line_bytes = (avctx->width * bpp + 7) >> 3; 134 line_bytes = (line_bytes + 1) & ~1; 135 136 max_pkt_size = 128 + avctx->height * 2 * line_bytes * nplanes + (pal ? 256*3 + 1 : 0); 137 if ((ret = ff_alloc_packet(avctx, pkt, max_pkt_size)) < 0) 138 return ret; 139 buf = pkt->data; 140 buf_end = pkt->data + pkt->size; 141 142 sw = avctx->sample_aspect_ratio.num; 143 sh = avctx->sample_aspect_ratio.den; 144 if (sw > 0xFFFFu || sh > 0xFFFFu) 145 av_reduce(&sw, &sh, sw, sh, 0xFFFFu); 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, sw); // horizontal DPI 156 bytestream_put_le16(&buf, sh); // 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 - pkt->data < 128) 164 *buf++= 0; 165 166 src = frame->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 AVERROR_BUG; 173 } 174 buf += written; 175 src += frame->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 AVERROR_BUG; 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 pkt->size = buf - pkt->data; 190 *got_packet = 1; 191 192 return 0; 193} 194 195const FFCodec ff_pcx_encoder = { 196 .p.name = "pcx", 197 .p.long_name = NULL_IF_CONFIG_SMALL("PC Paintbrush PCX image"), 198 .p.type = AVMEDIA_TYPE_VIDEO, 199 .p.id = AV_CODEC_ID_PCX, 200 FF_CODEC_ENCODE_CB(pcx_encode_frame), 201 .p.pix_fmts = (const enum AVPixelFormat[]){ 202 AV_PIX_FMT_RGB24, 203 AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8, AV_PIX_FMT_RGB4_BYTE, AV_PIX_FMT_BGR4_BYTE, 204 AV_PIX_FMT_GRAY8, AV_PIX_FMT_PAL8, 205 AV_PIX_FMT_MONOBLACK, 206 AV_PIX_FMT_NONE 207 }, 208 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 209}; 210