4 * Copyright (c) 2012 Paul B Mahol
6 * This file is part of FFmpeg.
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * 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,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "libavutil/imgutils.h"
27 #include "bytestream.h"
28 #include "codec_internal.h"
32 static int xwd_decode_frame(AVCodecContext
*avctx
, AVFrame
*p
,
33 int *got_frame
, AVPacket
*avpkt
)
35 uint32_t version
, header_size
, vclass
, ncolors
;
36 uint32_t xoffset
, be
, bpp
, lsize
, rsize
;
37 uint32_t pixformat
, pixdepth
, bunit
, bitorder
, bpad
;
44 if (avpkt
->size
< XWD_HEADER_SIZE
)
45 return AVERROR_INVALIDDATA
;
47 bytestream2_init(&gb
, avpkt
->data
, avpkt
->size
);
48 header_size
= bytestream2_get_be32u(&gb
);
50 version
= bytestream2_get_be32u(&gb
);
51 if (version
!= XWD_VERSION
) {
52 av_log(avctx
, AV_LOG_ERROR
, "unsupported version\n");
53 return AVERROR_INVALIDDATA
;
56 if (avpkt
->size
< header_size
|| header_size
< XWD_HEADER_SIZE
) {
57 av_log(avctx
, AV_LOG_ERROR
, "invalid header size\n");
58 return AVERROR_INVALIDDATA
;
61 pixformat
= bytestream2_get_be32u(&gb
);
62 pixdepth
= bytestream2_get_be32u(&gb
);
63 width
= bytestream2_get_be32u(&gb
);
64 height
= bytestream2_get_be32u(&gb
);
65 xoffset
= bytestream2_get_be32u(&gb
);
66 be
= bytestream2_get_be32u(&gb
);
67 bunit
= bytestream2_get_be32u(&gb
);
68 bitorder
= bytestream2_get_be32u(&gb
);
69 bpad
= bytestream2_get_be32u(&gb
);
70 bpp
= bytestream2_get_be32u(&gb
);
71 lsize
= bytestream2_get_be32u(&gb
);
72 vclass
= bytestream2_get_be32u(&gb
);
73 rgb
[0] = bytestream2_get_be32u(&gb
);
74 rgb
[1] = bytestream2_get_be32u(&gb
);
75 rgb
[2] = bytestream2_get_be32u(&gb
);
76 bytestream2_skipu(&gb
, 8);
77 ncolors
= bytestream2_get_be32u(&gb
);
78 bytestream2_skipu(&gb
, header_size
- (XWD_HEADER_SIZE
- 20));
80 if ((ret
= ff_set_dimensions(avctx
, width
, height
)) < 0)
83 av_log(avctx
, AV_LOG_DEBUG
,
84 "pixformat %"PRIu32
", pixdepth %"PRIu32
", bunit %"PRIu32
", bitorder %"PRIu32
", bpad %"PRIu32
"\n",
85 pixformat
, pixdepth
, bunit
, bitorder
, bpad
);
86 av_log(avctx
, AV_LOG_DEBUG
,
87 "vclass %"PRIu32
", ncolors %"PRIu32
", bpp %"PRIu32
", be %"PRIu32
", lsize %"PRIu32
", xoffset %"PRIu32
"\n",
88 vclass
, ncolors
, bpp
, be
, lsize
, xoffset
);
89 av_log(avctx
, AV_LOG_DEBUG
,
90 "red %0"PRIx32
", green %0"PRIx32
", blue %0"PRIx32
"\n",
91 rgb
[0], rgb
[1], rgb
[2]);
93 if (pixformat
> XWD_Z_PIXMAP
) {
94 av_log(avctx
, AV_LOG_ERROR
, "invalid pixmap format\n");
95 return AVERROR_INVALIDDATA
;
98 if (pixdepth
== 0 || pixdepth
> 32) {
99 av_log(avctx
, AV_LOG_ERROR
, "invalid pixmap depth\n");
100 return AVERROR_INVALIDDATA
;
104 avpriv_request_sample(avctx
, "xoffset %"PRIu32
"", xoffset
);
105 return AVERROR_PATCHWELCOME
;
109 av_log(avctx
, AV_LOG_ERROR
, "invalid byte order\n");
110 return AVERROR_INVALIDDATA
;
114 av_log(avctx
, AV_LOG_ERROR
, "invalid bitmap bit order\n");
115 return AVERROR_INVALIDDATA
;
118 if (bunit
!= 8 && bunit
!= 16 && bunit
!= 32) {
119 av_log(avctx
, AV_LOG_ERROR
, "invalid bitmap unit\n");
120 return AVERROR_INVALIDDATA
;
123 if (bpad
!= 8 && bpad
!= 16 && bpad
!= 32) {
124 av_log(avctx
, AV_LOG_ERROR
, "invalid bitmap scan-line pad\n");
125 return AVERROR_INVALIDDATA
;
128 if (bpp
== 0 || bpp
> 32) {
129 av_log(avctx
, AV_LOG_ERROR
, "invalid bits per pixel\n");
130 return AVERROR_INVALIDDATA
;
134 av_log(avctx
, AV_LOG_ERROR
, "invalid number of entries in colormap\n");
135 return AVERROR_INVALIDDATA
;
138 if ((ret
= av_image_check_size(avctx
->width
, avctx
->height
, 0, NULL
)) < 0)
141 rsize
= FFALIGN(avctx
->width
* bpp
, bpad
) / 8;
143 av_log(avctx
, AV_LOG_ERROR
, "invalid bytes per scan-line\n");
144 return AVERROR_INVALIDDATA
;
147 if (bytestream2_get_bytes_left(&gb
) < ncolors
* XWD_CMAP_SIZE
+ (uint64_t)avctx
->height
* lsize
) {
148 av_log(avctx
, AV_LOG_ERROR
, "input buffer too small\n");
149 return AVERROR_INVALIDDATA
;
152 if (pixformat
!= XWD_Z_PIXMAP
) {
153 avpriv_report_missing_feature(avctx
, "Pixmap format %"PRIu32
, pixformat
);
154 return AVERROR_PATCHWELCOME
;
157 avctx
->pix_fmt
= AV_PIX_FMT_NONE
;
159 case XWD_STATIC_GRAY
:
161 if (bpp
!= 1 && bpp
!= 8)
162 return AVERROR_INVALIDDATA
;
163 if (bpp
== 1 && pixdepth
== 1) {
164 avctx
->pix_fmt
= AV_PIX_FMT_MONOWHITE
;
165 } else if (bpp
== 8 && pixdepth
== 8) {
166 avctx
->pix_fmt
= AV_PIX_FMT_GRAY8
;
169 case XWD_STATIC_COLOR
:
170 case XWD_PSEUDO_COLOR
:
172 avctx
->pix_fmt
= AV_PIX_FMT_PAL8
;
175 case XWD_DIRECT_COLOR
:
176 if (bpp
!= 16 && bpp
!= 24 && bpp
!= 32)
177 return AVERROR_INVALIDDATA
;
178 if (bpp
== 16 && pixdepth
== 15) {
179 if (rgb
[0] == 0x7C00 && rgb
[1] == 0x3E0 && rgb
[2] == 0x1F)
180 avctx
->pix_fmt
= be
? AV_PIX_FMT_RGB555BE
: AV_PIX_FMT_RGB555LE
;
181 else if (rgb
[0] == 0x1F && rgb
[1] == 0x3E0 && rgb
[2] == 0x7C00)
182 avctx
->pix_fmt
= be
? AV_PIX_FMT_BGR555BE
: AV_PIX_FMT_BGR555LE
;
183 } else if (bpp
== 16 && pixdepth
== 16) {
184 if (rgb
[0] == 0xF800 && rgb
[1] == 0x7E0 && rgb
[2] == 0x1F)
185 avctx
->pix_fmt
= be
? AV_PIX_FMT_RGB565BE
: AV_PIX_FMT_RGB565LE
;
186 else if (rgb
[0] == 0x1F && rgb
[1] == 0x7E0 && rgb
[2] == 0xF800)
187 avctx
->pix_fmt
= be
? AV_PIX_FMT_BGR565BE
: AV_PIX_FMT_BGR565LE
;
188 } else if (bpp
== 24) {
189 if (rgb
[0] == 0xFF0000 && rgb
[1] == 0xFF00 && rgb
[2] == 0xFF)
190 avctx
->pix_fmt
= be
? AV_PIX_FMT_RGB24
: AV_PIX_FMT_BGR24
;
191 else if (rgb
[0] == 0xFF && rgb
[1] == 0xFF00 && rgb
[2] == 0xFF0000)
192 avctx
->pix_fmt
= be
? AV_PIX_FMT_BGR24
: AV_PIX_FMT_RGB24
;
193 } else if (bpp
== 32) {
194 if (rgb
[0] == 0xFF0000 && rgb
[1] == 0xFF00 && rgb
[2] == 0xFF)
195 avctx
->pix_fmt
= be
? AV_PIX_FMT_ARGB
: AV_PIX_FMT_BGRA
;
196 else if (rgb
[0] == 0xFF && rgb
[1] == 0xFF00 && rgb
[2] == 0xFF0000)
197 avctx
->pix_fmt
= be
? AV_PIX_FMT_ABGR
: AV_PIX_FMT_RGBA
;
199 bytestream2_skipu(&gb
, ncolors
* XWD_CMAP_SIZE
);
202 av_log(avctx
, AV_LOG_ERROR
, "invalid visual class\n");
203 return AVERROR_INVALIDDATA
;
206 if (avctx
->pix_fmt
== AV_PIX_FMT_NONE
) {
207 avpriv_request_sample(avctx
,
208 "Unknown file: bpp %"PRIu32
", pixdepth %"PRIu32
", vclass %"PRIu32
"",
209 bpp
, pixdepth
, vclass
);
210 return AVERROR_PATCHWELCOME
;
213 if (avctx
->skip_frame
>= AVDISCARD_ALL
)
216 if ((ret
= ff_get_buffer(avctx
, p
, 0)) < 0)
219 if (avctx
->pix_fmt
== AV_PIX_FMT_PAL8
) {
220 uint32_t *dst
= (uint32_t *)p
->data
[1];
221 uint8_t red
, green
, blue
;
223 for (int i
= 0; i
< ncolors
; i
++) {
224 bytestream2_skipu(&gb
, 4); // skip colormap entry number
225 red
= bytestream2_get_byteu(&gb
);
226 bytestream2_skipu(&gb
, 1);
227 green
= bytestream2_get_byteu(&gb
);
228 bytestream2_skipu(&gb
, 1);
229 blue
= bytestream2_get_byteu(&gb
);
230 bytestream2_skipu(&gb
, 3); // skip bitmask flag and padding
232 dst
[i
] = 0xFFU
<< 24 | red
<< 16 | green
<< 8 | blue
;
237 for (int i
= 0; i
< avctx
->height
; i
++) {
238 bytestream2_get_bufferu(&gb
, ptr
, rsize
);
239 bytestream2_skipu(&gb
, lsize
- rsize
);
240 ptr
+= p
->linesize
[0];
248 const FFCodec ff_xwd_decoder
= {
250 CODEC_LONG_NAME("XWD (X Window Dump) image"),
251 .p
.type
= AVMEDIA_TYPE_VIDEO
,
252 .p
.id
= AV_CODEC_ID_XWD
,
253 .p
.capabilities
= AV_CODEC_CAP_DR1
,
254 .caps_internal
= FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM
,
255 FF_CODEC_DECODE_CB(xwd_decode_frame
),