a526d619 |
/*
* Pictor/PC Paint decoder
* Copyright (c) 2010 Peter Ross <pross@xvid.org>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/** |
33edd31f |
* @file |
a526d619 |
* Pictor/PC Paint decoder
*/
|
7ffe76e5 |
#include "libavutil/imgutils.h" |
a526d619 |
#include "avcodec.h"
#include "bytestream.h"
#include "cga_data.h" |
594d4d5d |
#include "internal.h" |
a526d619 |
typedef struct PicContext {
int width, height;
int nb_planes; |
d8591913 |
GetByteContext g; |
a526d619 |
} PicContext;
|
759001c5 |
static void picmemset_8bpp(PicContext *s, AVFrame *frame, int value, int run,
int *x, int *y) |
a526d619 |
{
while (run > 0) { |
759001c5 |
uint8_t *d = frame->data[0] + *y * frame->linesize[0]; |
a526d619 |
if (*x + run >= s->width) {
int n = s->width - *x;
memset(d + *x, value, n);
run -= n;
*x = 0;
*y -= 1;
if (*y < 0)
break;
} else {
memset(d + *x, value, run);
*x += run;
break;
}
}
}
|
759001c5 |
static void picmemset(PicContext *s, AVFrame *frame, int value, int run, |
d8591913 |
int *x, int *y, int *plane, int bits_per_plane) |
a526d619 |
{
uint8_t *d;
int shift = *plane * bits_per_plane;
int mask = ((1 << bits_per_plane) - 1) << shift;
value <<= shift;
while (run > 0) {
int j;
for (j = 8-bits_per_plane; j >= 0; j -= bits_per_plane) { |
759001c5 |
d = frame->data[0] + *y * frame->linesize[0]; |
a526d619 |
d[*x] |= (value >> j) & mask;
*x += 1;
if (*x == s->width) {
*y -= 1;
*x = 0;
if (*y < 0) {
*y = s->height - 1;
*plane += 1;
value <<= bits_per_plane;
mask <<= bits_per_plane;
if (*plane >= s->nb_planes)
break;
}
}
}
run--;
}
}
static const uint8_t cga_mode45_index[6][4] = {
[0] = { 0, 3, 5, 7 }, // mode4, palette#1, low intensity
[1] = { 0, 2, 4, 6 }, // mode4, palette#2, low intensity
[2] = { 0, 3, 4, 7 }, // mode5, low intensity
[3] = { 0, 11, 13, 15 }, // mode4, palette#1, high intensity
[4] = { 0, 10, 12, 14 }, // mode4, palette#2, high intensity
[5] = { 0, 11, 12, 15 }, // mode5, high intensity
};
static int decode_frame(AVCodecContext *avctx, |
df9b9567 |
void *data, int *got_frame, |
a526d619 |
AVPacket *avpkt)
{
PicContext *s = avctx->priv_data; |
759001c5 |
AVFrame *frame = data; |
a526d619 |
uint32_t *palette; |
d8591913 |
int bits_per_plane, bpp, etype, esize, npal, pos_after_pal; |
80e9e63c |
int i, x, y, plane, tmp, ret, val; |
a526d619 |
|
d8591913 |
bytestream2_init(&s->g, avpkt->data, avpkt->size);
if (bytestream2_get_bytes_left(&s->g) < 11) |
a526d619 |
return AVERROR_INVALIDDATA;
|
d8591913 |
if (bytestream2_get_le16u(&s->g) != 0x1234) |
a526d619 |
return AVERROR_INVALIDDATA; |
d8591913 |
s->width = bytestream2_get_le16u(&s->g);
s->height = bytestream2_get_le16u(&s->g);
bytestream2_skip(&s->g, 4);
tmp = bytestream2_get_byteu(&s->g);
bits_per_plane = tmp & 0xF;
s->nb_planes = (tmp >> 4) + 1;
bpp = bits_per_plane * s->nb_planes; |
a526d619 |
if (bits_per_plane > 8 || bpp < 1 || bpp > 32) { |
13795dbb |
avpriv_request_sample(avctx, "Unsupported bit depth"); |
f3298f12 |
return AVERROR_PATCHWELCOME; |
a526d619 |
}
|
bf0d098a |
if (bytestream2_peek_byte(&s->g) == 0xFF || bpp == 1 || bpp == 4 || bpp == 8) { |
d8591913 |
bytestream2_skip(&s->g, 2);
etype = bytestream2_get_le16(&s->g);
esize = bytestream2_get_le16(&s->g);
if (bytestream2_get_bytes_left(&s->g) < esize) |
a526d619 |
return AVERROR_INVALIDDATA;
} else {
etype = -1;
esize = 0;
}
|
716d413c |
avctx->pix_fmt = AV_PIX_FMT_PAL8; |
a526d619 |
|
8088d6f5 |
if (av_image_check_size(s->width, s->height, 0, avctx) < 0)
return -1; |
a526d619 |
if (s->width != avctx->width && s->height != avctx->height) { |
0f21d8b1 |
ret = ff_set_dimensions(avctx, s->width, s->height);
if (ret < 0)
return ret; |
a526d619 |
}
|
1ec94b0f |
if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) |
759001c5 |
return ret;
memset(frame->data[0], 0, s->height * frame->linesize[0]);
frame->pict_type = AV_PICTURE_TYPE_I;
frame->palette_has_changed = 1; |
a526d619 |
|
d8591913 |
pos_after_pal = bytestream2_tell(&s->g) + esize; |
759001c5 |
palette = (uint32_t*)frame->data[1]; |
d8591913 |
if (etype == 1 && esize > 1 && bytestream2_peek_byte(&s->g) < 6) {
int idx = bytestream2_get_byte(&s->g); |
a526d619 |
npal = 4;
for (i = 0; i < npal; i++)
palette[i] = ff_cga_palette[ cga_mode45_index[idx][i] ];
} else if (etype == 2) {
npal = FFMIN(esize, 16); |
d8591913 |
for (i = 0; i < npal; i++) {
int pal_idx = bytestream2_get_byte(&s->g); |
478fc7f5 |
palette[i] = ff_cga_palette[FFMIN(pal_idx, 15)]; |
d8591913 |
} |
a526d619 |
} else if (etype == 3) {
npal = FFMIN(esize, 16); |
d8591913 |
for (i = 0; i < npal; i++) {
int pal_idx = bytestream2_get_byte(&s->g);
palette[i] = ff_ega_palette[FFMIN(pal_idx, 63)];
} |
a526d619 |
} else if (etype == 4 || etype == 5) {
npal = FFMIN(esize / 3, 256); |
7ea77a61 |
for (i = 0; i < npal; i++) { |
d8591913 |
palette[i] = bytestream2_get_be24(&s->g) << 2; |
67f5650a |
palette[i] |= 0xFFU << 24 | palette[i] >> 6 & 0x30303; |
7ea77a61 |
} |
a526d619 |
} else {
if (bpp == 1) {
npal = 2; |
7ea77a61 |
palette[0] = 0xFF000000;
palette[1] = 0xFFFFFFFF; |
a526d619 |
} else if (bpp == 2) {
npal = 4;
for (i = 0; i < npal; i++)
palette[i] = ff_cga_palette[ cga_mode45_index[0][i] ];
} else {
npal = 16;
memcpy(palette, ff_cga_palette, npal * 4);
}
}
// fill remaining palette entries
memset(palette + npal, 0, AVPALETTE_SIZE - npal * 4); |
d8591913 |
// skip remaining palette bytes
bytestream2_seek(&s->g, pos_after_pal, SEEK_SET); |
a526d619 |
|
8b67ec77 |
val = 0; |
a526d619 |
y = s->height - 1; |
d8591913 |
if (bytestream2_get_le16(&s->g)) { |
1fd69243 |
x = 0;
plane = 0; |
56061234 |
while (bytestream2_get_bytes_left(&s->g) >= 6) { |
d8591913 |
int stop_size, marker, t1, t2;
t1 = bytestream2_get_bytes_left(&s->g);
t2 = bytestream2_get_le16(&s->g);
stop_size = t1 - FFMIN(t1, t2);
// ignore uncompressed block size
bytestream2_skip(&s->g, 2);
marker = bytestream2_get_byte(&s->g); |
a526d619 |
|
56061234 |
while (plane < s->nb_planes && |
d8591913 |
bytestream2_get_bytes_left(&s->g) > stop_size) { |
a526d619 |
int run = 1; |
8b67ec77 |
val = bytestream2_get_byte(&s->g); |
a526d619 |
if (val == marker) { |
d8591913 |
run = bytestream2_get_byte(&s->g); |
a526d619 |
if (run == 0) |
d8591913 |
run = bytestream2_get_le16(&s->g);
val = bytestream2_get_byte(&s->g); |
a526d619 |
} |
d8591913 |
if (!bytestream2_get_bytes_left(&s->g)) |
a526d619 |
break;
if (bits_per_plane == 8) { |
759001c5 |
picmemset_8bpp(s, frame, val, run, &x, &y); |
a526d619 |
if (y < 0) |
5f7aecde |
goto finish; |
a526d619 |
} else { |
759001c5 |
picmemset(s, frame, val, run, &x, &y, &plane, bits_per_plane); |
a526d619 |
}
}
} |
8b67ec77 |
|
56061234 |
if (x < avctx->width) { |
8b67ec77 |
int run = (y + 1) * avctx->width - x;
if (bits_per_plane == 8) |
80e9e63c |
picmemset_8bpp(s, frame, val, run, &x, &y); |
8b67ec77 |
else |
80e9e63c |
picmemset(s, frame, val, run / (8 / bits_per_plane), &x, &y, &plane, bits_per_plane); |
8b67ec77 |
} |
a526d619 |
} else { |
67f5650a |
while (y >= 0 && bytestream2_get_bytes_left(&s->g) > 0) { |
80e9e63c |
memcpy(frame->data[0] + y * frame->linesize[0], s->g.buffer, FFMIN(avctx->width, bytestream2_get_bytes_left(&s->g))); |
67f5650a |
bytestream2_skip(&s->g, avctx->width); |
1fd69243 |
y--;
} |
a526d619 |
} |
5f7aecde |
finish: |
a526d619 |
|
df9b9567 |
*got_frame = 1; |
d8591913 |
return avpkt->size; |
a526d619 |
}
|
e7e2df27 |
AVCodec ff_pictor_decoder = { |
ec6402b7 |
.name = "pictor", |
b2bed932 |
.long_name = NULL_IF_CONFIG_SMALL("Pictor/PC Paint"), |
ec6402b7 |
.type = AVMEDIA_TYPE_VIDEO, |
36ef5369 |
.id = AV_CODEC_ID_PICTOR, |
ec6402b7 |
.priv_data_size = sizeof(PicContext),
.decode = decode_frame,
.capabilities = CODEC_CAP_DR1, |
a526d619 |
}; |