xwddec.c 8.64 KB
Newer Older
Paul B Mahol's avatar
Paul B Mahol committed
1 2 3 4 5
/*
 * XWD image format
 *
 * Copyright (c) 2012 Paul B Mahol
 *
6
 * This file is part of FFmpeg.
Paul B Mahol's avatar
Paul B Mahol committed
7
 *
8
 * FFmpeg is free software; you can redistribute it and/or
Paul B Mahol's avatar
Paul B Mahol committed
9 10 11 12
 * 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.
 *
13
 * FFmpeg is distributed in the hope that it will be useful,
Paul B Mahol's avatar
Paul B Mahol committed
14 15 16 17 18
 * 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
19
 * License along with FFmpeg; if not, write to the Free Software
Paul B Mahol's avatar
Paul B Mahol committed
20 21 22 23 24 25
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "libavutil/imgutils.h"
#include "avcodec.h"
#include "bytestream.h"
26
#include "internal.h"
Paul B Mahol's avatar
Paul B Mahol committed
27 28 29
#include "xwd.h"

static int xwd_decode_frame(AVCodecContext *avctx, void *data,
30
                            int *got_frame, AVPacket *avpkt)
Paul B Mahol's avatar
Paul B Mahol committed
31
{
32
    AVFrame *p = data;
Paul B Mahol's avatar
Paul B Mahol committed
33 34 35 36 37 38 39
    const uint8_t *buf = avpkt->data;
    int i, ret, buf_size = avpkt->size;
    uint32_t version, header_size, vclass, ncolors;
    uint32_t xoffset, be, bpp, lsize, rsize;
    uint32_t pixformat, pixdepth, bunit, bitorder, bpad;
    uint32_t rgb[3];
    uint8_t *ptr;
40
    GetByteContext gb;
Paul B Mahol's avatar
Paul B Mahol committed
41 42 43 44

    if (buf_size < XWD_HEADER_SIZE)
        return AVERROR_INVALIDDATA;

45 46
    bytestream2_init(&gb, buf, buf_size);
    header_size = bytestream2_get_be32u(&gb);
Paul B Mahol's avatar
Paul B Mahol committed
47

48
    version = bytestream2_get_be32u(&gb);
Paul B Mahol's avatar
Paul B Mahol committed
49 50 51 52 53
    if (version != XWD_VERSION) {
        av_log(avctx, AV_LOG_ERROR, "unsupported version\n");
        return AVERROR_INVALIDDATA;
    }

54
    if (buf_size < header_size || header_size < XWD_HEADER_SIZE) {
Paul B Mahol's avatar
Paul B Mahol committed
55 56 57 58
        av_log(avctx, AV_LOG_ERROR, "invalid header size\n");
        return AVERROR_INVALIDDATA;
    }

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
    pixformat     = bytestream2_get_be32u(&gb);
    pixdepth      = bytestream2_get_be32u(&gb);
    avctx->width  = bytestream2_get_be32u(&gb);
    avctx->height = bytestream2_get_be32u(&gb);
    xoffset       = bytestream2_get_be32u(&gb);
    be            = bytestream2_get_be32u(&gb);
    bunit         = bytestream2_get_be32u(&gb);
    bitorder      = bytestream2_get_be32u(&gb);
    bpad          = bytestream2_get_be32u(&gb);
    bpp           = bytestream2_get_be32u(&gb);
    lsize         = bytestream2_get_be32u(&gb);
    vclass        = bytestream2_get_be32u(&gb);
    rgb[0]        = bytestream2_get_be32u(&gb);
    rgb[1]        = bytestream2_get_be32u(&gb);
    rgb[2]        = bytestream2_get_be32u(&gb);
    bytestream2_skipu(&gb, 8);
    ncolors       = bytestream2_get_be32u(&gb);
    bytestream2_skipu(&gb, header_size - (XWD_HEADER_SIZE - 20));
Paul B Mahol's avatar
Paul B Mahol committed
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94

    av_log(avctx, AV_LOG_DEBUG, "pixformat %d, pixdepth %d, bunit %d, bitorder %d, bpad %d\n",
           pixformat, pixdepth, bunit, bitorder, bpad);
    av_log(avctx, AV_LOG_DEBUG, "vclass %d, ncolors %d, bpp %d, be %d, lsize %d, xoffset %d\n",
           vclass, ncolors, bpp, be, lsize, xoffset);
    av_log(avctx, AV_LOG_DEBUG, "red %0x, green %0x, blue %0x\n", rgb[0], rgb[1], rgb[2]);

    if (pixformat > XWD_Z_PIXMAP) {
        av_log(avctx, AV_LOG_ERROR, "invalid pixmap format\n");
        return AVERROR_INVALIDDATA;
    }

    if (pixdepth == 0 || pixdepth > 32) {
        av_log(avctx, AV_LOG_ERROR, "invalid pixmap depth\n");
        return AVERROR_INVALIDDATA;
    }

    if (xoffset) {
95
        avpriv_request_sample(avctx, "xoffset %d", xoffset);
Paul B Mahol's avatar
Paul B Mahol committed
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
        return AVERROR_PATCHWELCOME;
    }

    if (be > 1) {
        av_log(avctx, AV_LOG_ERROR, "invalid byte order\n");
        return AVERROR_INVALIDDATA;
    }

    if (bitorder > 1) {
        av_log(avctx, AV_LOG_ERROR, "invalid bitmap bit order\n");
        return AVERROR_INVALIDDATA;
    }

    if (bunit != 8 && bunit != 16 && bunit != 32) {
        av_log(avctx, AV_LOG_ERROR, "invalid bitmap unit\n");
        return AVERROR_INVALIDDATA;
    }

    if (bpad != 8 && bpad != 16 && bpad != 32) {
        av_log(avctx, AV_LOG_ERROR, "invalid bitmap scan-line pad\n");
        return AVERROR_INVALIDDATA;
    }

    if (bpp == 0 || bpp > 32) {
        av_log(avctx, AV_LOG_ERROR, "invalid bits per pixel\n");
        return AVERROR_INVALIDDATA;
    }

    if (ncolors > 256) {
        av_log(avctx, AV_LOG_ERROR, "invalid number of entries in colormap\n");
        return AVERROR_INVALIDDATA;
    }

    if ((ret = av_image_check_size(avctx->width, avctx->height, 0, NULL)) < 0)
        return ret;

    rsize = FFALIGN(avctx->width * bpp, bpad) / 8;
    if (lsize < rsize) {
        av_log(avctx, AV_LOG_ERROR, "invalid bytes per scan-line\n");
        return AVERROR_INVALIDDATA;
    }

138
    if (bytestream2_get_bytes_left(&gb) < ncolors * XWD_CMAP_SIZE + avctx->height * lsize) {
Paul B Mahol's avatar
Paul B Mahol committed
139 140 141 142 143
        av_log(avctx, AV_LOG_ERROR, "input buffer too small\n");
        return AVERROR_INVALIDDATA;
    }

    if (pixformat != XWD_Z_PIXMAP) {
144
        avpriv_report_missing_feature(avctx, "Pixmap format %d", pixformat);
Paul B Mahol's avatar
Paul B Mahol committed
145 146 147
        return AVERROR_PATCHWELCOME;
    }

148
    avctx->pix_fmt = AV_PIX_FMT_NONE;
Paul B Mahol's avatar
Paul B Mahol committed
149 150 151
    switch (vclass) {
    case XWD_STATIC_GRAY:
    case XWD_GRAY_SCALE:
152
        if (bpp != 1 && bpp != 8)
Paul B Mahol's avatar
Paul B Mahol committed
153
            return AVERROR_INVALIDDATA;
154
        if (pixdepth == 1) {
155
            avctx->pix_fmt = AV_PIX_FMT_MONOWHITE;
156
        } else if (pixdepth == 8) {
157
            avctx->pix_fmt = AV_PIX_FMT_GRAY8;
158
        }
Paul B Mahol's avatar
Paul B Mahol committed
159 160 161 162
        break;
    case XWD_STATIC_COLOR:
    case XWD_PSEUDO_COLOR:
        if (bpp == 8)
163
            avctx->pix_fmt = AV_PIX_FMT_PAL8;
Paul B Mahol's avatar
Paul B Mahol committed
164 165 166 167 168 169 170
        break;
    case XWD_TRUE_COLOR:
    case XWD_DIRECT_COLOR:
        if (bpp != 16 && bpp != 24 && bpp != 32)
            return AVERROR_INVALIDDATA;
        if (bpp == 16 && pixdepth == 15) {
            if (rgb[0] == 0x7C00 && rgb[1] == 0x3E0 && rgb[2] == 0x1F)
171
                avctx->pix_fmt = be ? AV_PIX_FMT_RGB555BE : AV_PIX_FMT_RGB555LE;
Paul B Mahol's avatar
Paul B Mahol committed
172
            else if (rgb[0] == 0x1F && rgb[1] == 0x3E0 && rgb[2] == 0x7C00)
173
                avctx->pix_fmt = be ? AV_PIX_FMT_BGR555BE : AV_PIX_FMT_BGR555LE;
Paul B Mahol's avatar
Paul B Mahol committed
174 175
        } else if (bpp == 16 && pixdepth == 16) {
            if (rgb[0] == 0xF800 && rgb[1] == 0x7E0 && rgb[2] == 0x1F)
176
                avctx->pix_fmt = be ? AV_PIX_FMT_RGB565BE : AV_PIX_FMT_RGB565LE;
Paul B Mahol's avatar
Paul B Mahol committed
177
            else if (rgb[0] == 0x1F && rgb[1] == 0x7E0 && rgb[2] == 0xF800)
178
                avctx->pix_fmt = be ? AV_PIX_FMT_BGR565BE : AV_PIX_FMT_BGR565LE;
Paul B Mahol's avatar
Paul B Mahol committed
179 180
        } else if (bpp == 24) {
            if (rgb[0] == 0xFF0000 && rgb[1] == 0xFF00 && rgb[2] == 0xFF)
181
                avctx->pix_fmt = be ? AV_PIX_FMT_RGB24 : AV_PIX_FMT_BGR24;
Paul B Mahol's avatar
Paul B Mahol committed
182
            else if (rgb[0] == 0xFF && rgb[1] == 0xFF00 && rgb[2] == 0xFF0000)
183
                avctx->pix_fmt = be ? AV_PIX_FMT_BGR24 : AV_PIX_FMT_RGB24;
Paul B Mahol's avatar
Paul B Mahol committed
184 185
        } else if (bpp == 32) {
            if (rgb[0] == 0xFF0000 && rgb[1] == 0xFF00 && rgb[2] == 0xFF)
186
                avctx->pix_fmt = be ? AV_PIX_FMT_ARGB : AV_PIX_FMT_BGRA;
Paul B Mahol's avatar
Paul B Mahol committed
187
            else if (rgb[0] == 0xFF && rgb[1] == 0xFF00 && rgb[2] == 0xFF0000)
188
                avctx->pix_fmt = be ? AV_PIX_FMT_ABGR : AV_PIX_FMT_RGBA;
Paul B Mahol's avatar
Paul B Mahol committed
189
        }
190
        bytestream2_skipu(&gb, ncolors * XWD_CMAP_SIZE);
Paul B Mahol's avatar
Paul B Mahol committed
191 192 193 194 195 196
        break;
    default:
        av_log(avctx, AV_LOG_ERROR, "invalid visual class\n");
        return AVERROR_INVALIDDATA;
    }

197
    if (avctx->pix_fmt == AV_PIX_FMT_NONE) {
198 199 200
        avpriv_request_sample(avctx,
                              "Unknown file: bpp %d, pixdepth %d, vclass %d",
                              bpp, pixdepth, vclass);
Paul B Mahol's avatar
Paul B Mahol committed
201 202 203
        return AVERROR_PATCHWELCOME;
    }

204
    if ((ret = ff_get_buffer(avctx, p, 0)) < 0)
Paul B Mahol's avatar
Paul B Mahol committed
205 206 207 208 209
        return ret;

    p->key_frame = 1;
    p->pict_type = AV_PICTURE_TYPE_I;

210
    if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
Paul B Mahol's avatar
Paul B Mahol committed
211 212 213 214 215
        uint32_t *dst = (uint32_t *)p->data[1];
        uint8_t red, green, blue;

        for (i = 0; i < ncolors; i++) {

216 217 218 219 220 221 222
            bytestream2_skipu(&gb, 4); // skip colormap entry number
            red    = bytestream2_get_byteu(&gb);
            bytestream2_skipu(&gb, 1);
            green  = bytestream2_get_byteu(&gb);
            bytestream2_skipu(&gb, 1);
            blue   = bytestream2_get_byteu(&gb);
            bytestream2_skipu(&gb, 3); // skip bitmask flag and padding
Paul B Mahol's avatar
Paul B Mahol committed
223 224 225 226 227 228 229

            dst[i] = red << 16 | green << 8 | blue;
        }
    }

    ptr = p->data[0];
    for (i = 0; i < avctx->height; i++) {
230 231
        bytestream2_get_bufferu(&gb, ptr, rsize);
        bytestream2_skipu(&gb, lsize - rsize);
Paul B Mahol's avatar
Paul B Mahol committed
232 233 234
        ptr += p->linesize[0];
    }

235
    *got_frame       = 1;
Paul B Mahol's avatar
Paul B Mahol committed
236 237 238 239 240 241

    return buf_size;
}

AVCodec ff_xwd_decoder = {
    .name           = "xwd",
242
    .long_name      = NULL_IF_CONFIG_SMALL("XWD (X Window Dump) image"),
Paul B Mahol's avatar
Paul B Mahol committed
243
    .type           = AVMEDIA_TYPE_VIDEO,
244
    .id             = AV_CODEC_ID_XWD,
Paul B Mahol's avatar
Paul B Mahol committed
245 246 247
    .decode         = xwd_decode_frame,
    .capabilities   = CODEC_CAP_DR1,
};