Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 1 | /* |
2 | * XWD image format |
||
3 | * |
||
4 | * Copyright (c) 2012 Paul B Mahol |
||
5 | * |
||
6 | * This file is part of FFmpeg. |
||
7 | * |
||
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. |
||
12 | * |
||
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. |
||
17 | * |
||
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 |
||
21 | */ |
||
22 | |||
23 | #include "libavutil/imgutils.h" |
||
24 | #include "avcodec.h" |
||
25 | #include "bytestream.h" |
||
26 | #include "internal.h" |
||
27 | #include "xwd.h" |
||
28 | |||
29 | static int xwd_decode_frame(AVCodecContext *avctx, void *data, |
||
30 | int *got_frame, AVPacket *avpkt) |
||
31 | { |
||
32 | AVFrame *p = data; |
||
33 | const uint8_t *buf = avpkt->data; |
||
34 | int i, ret, buf_size = avpkt->size; |
||
35 | uint32_t version, header_size, vclass, ncolors; |
||
36 | uint32_t xoffset, be, bpp, lsize, rsize; |
||
37 | uint32_t pixformat, pixdepth, bunit, bitorder, bpad; |
||
38 | uint32_t rgb[3]; |
||
39 | uint8_t *ptr; |
||
40 | GetByteContext gb; |
||
41 | |||
42 | if (buf_size < XWD_HEADER_SIZE) |
||
43 | return AVERROR_INVALIDDATA; |
||
44 | |||
45 | bytestream2_init(&gb, buf, buf_size); |
||
46 | header_size = bytestream2_get_be32u(&gb); |
||
47 | |||
48 | version = bytestream2_get_be32u(&gb); |
||
49 | if (version != XWD_VERSION) { |
||
50 | av_log(avctx, AV_LOG_ERROR, "unsupported version\n"); |
||
51 | return AVERROR_INVALIDDATA; |
||
52 | } |
||
53 | |||
54 | if (buf_size < header_size || header_size < XWD_HEADER_SIZE) { |
||
55 | av_log(avctx, AV_LOG_ERROR, "invalid header size\n"); |
||
56 | return AVERROR_INVALIDDATA; |
||
57 | } |
||
58 | |||
59 | pixformat = bytestream2_get_be32u(&gb); |
||
60 | pixdepth = bytestream2_get_be32u(&gb); |
||
61 | avctx->width = bytestream2_get_be32u(&gb); |
||
62 | avctx->height = bytestream2_get_be32u(&gb); |
||
63 | xoffset = bytestream2_get_be32u(&gb); |
||
64 | be = bytestream2_get_be32u(&gb); |
||
65 | bunit = bytestream2_get_be32u(&gb); |
||
66 | bitorder = bytestream2_get_be32u(&gb); |
||
67 | bpad = bytestream2_get_be32u(&gb); |
||
68 | bpp = bytestream2_get_be32u(&gb); |
||
69 | lsize = bytestream2_get_be32u(&gb); |
||
70 | vclass = bytestream2_get_be32u(&gb); |
||
71 | rgb[0] = bytestream2_get_be32u(&gb); |
||
72 | rgb[1] = bytestream2_get_be32u(&gb); |
||
73 | rgb[2] = bytestream2_get_be32u(&gb); |
||
74 | bytestream2_skipu(&gb, 8); |
||
75 | ncolors = bytestream2_get_be32u(&gb); |
||
76 | bytestream2_skipu(&gb, header_size - (XWD_HEADER_SIZE - 20)); |
||
77 | |||
78 | av_log(avctx, AV_LOG_DEBUG, "pixformat %d, pixdepth %d, bunit %d, bitorder %d, bpad %d\n", |
||
79 | pixformat, pixdepth, bunit, bitorder, bpad); |
||
80 | av_log(avctx, AV_LOG_DEBUG, "vclass %d, ncolors %d, bpp %d, be %d, lsize %d, xoffset %d\n", |
||
81 | vclass, ncolors, bpp, be, lsize, xoffset); |
||
82 | av_log(avctx, AV_LOG_DEBUG, "red %0x, green %0x, blue %0x\n", rgb[0], rgb[1], rgb[2]); |
||
83 | |||
84 | if (pixformat > XWD_Z_PIXMAP) { |
||
85 | av_log(avctx, AV_LOG_ERROR, "invalid pixmap format\n"); |
||
86 | return AVERROR_INVALIDDATA; |
||
87 | } |
||
88 | |||
89 | if (pixdepth == 0 || pixdepth > 32) { |
||
90 | av_log(avctx, AV_LOG_ERROR, "invalid pixmap depth\n"); |
||
91 | return AVERROR_INVALIDDATA; |
||
92 | } |
||
93 | |||
94 | if (xoffset) { |
||
95 | avpriv_request_sample(avctx, "xoffset %d", xoffset); |
||
96 | return AVERROR_PATCHWELCOME; |
||
97 | } |
||
98 | |||
99 | if (be > 1) { |
||
100 | av_log(avctx, AV_LOG_ERROR, "invalid byte order\n"); |
||
101 | return AVERROR_INVALIDDATA; |
||
102 | } |
||
103 | |||
104 | if (bitorder > 1) { |
||
105 | av_log(avctx, AV_LOG_ERROR, "invalid bitmap bit order\n"); |
||
106 | return AVERROR_INVALIDDATA; |
||
107 | } |
||
108 | |||
109 | if (bunit != 8 && bunit != 16 && bunit != 32) { |
||
110 | av_log(avctx, AV_LOG_ERROR, "invalid bitmap unit\n"); |
||
111 | return AVERROR_INVALIDDATA; |
||
112 | } |
||
113 | |||
114 | if (bpad != 8 && bpad != 16 && bpad != 32) { |
||
115 | av_log(avctx, AV_LOG_ERROR, "invalid bitmap scan-line pad\n"); |
||
116 | return AVERROR_INVALIDDATA; |
||
117 | } |
||
118 | |||
119 | if (bpp == 0 || bpp > 32) { |
||
120 | av_log(avctx, AV_LOG_ERROR, "invalid bits per pixel\n"); |
||
121 | return AVERROR_INVALIDDATA; |
||
122 | } |
||
123 | |||
124 | if (ncolors > 256) { |
||
125 | av_log(avctx, AV_LOG_ERROR, "invalid number of entries in colormap\n"); |
||
126 | return AVERROR_INVALIDDATA; |
||
127 | } |
||
128 | |||
129 | if ((ret = av_image_check_size(avctx->width, avctx->height, 0, NULL)) < 0) |
||
130 | return ret; |
||
131 | |||
132 | rsize = FFALIGN(avctx->width * bpp, bpad) / 8; |
||
133 | if (lsize < rsize) { |
||
134 | av_log(avctx, AV_LOG_ERROR, "invalid bytes per scan-line\n"); |
||
135 | return AVERROR_INVALIDDATA; |
||
136 | } |
||
137 | |||
138 | if (bytestream2_get_bytes_left(&gb) < ncolors * XWD_CMAP_SIZE + avctx->height * lsize) { |
||
139 | av_log(avctx, AV_LOG_ERROR, "input buffer too small\n"); |
||
140 | return AVERROR_INVALIDDATA; |
||
141 | } |
||
142 | |||
143 | if (pixformat != XWD_Z_PIXMAP) { |
||
144 | avpriv_report_missing_feature(avctx, "Pixmap format %d", pixformat); |
||
145 | return AVERROR_PATCHWELCOME; |
||
146 | } |
||
147 | |||
148 | avctx->pix_fmt = AV_PIX_FMT_NONE; |
||
149 | switch (vclass) { |
||
150 | case XWD_STATIC_GRAY: |
||
151 | case XWD_GRAY_SCALE: |
||
152 | if (bpp != 1 && bpp != 8) |
||
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 | } |
||
159 | break; |
||
160 | case XWD_STATIC_COLOR: |
||
161 | case XWD_PSEUDO_COLOR: |
||
162 | if (bpp == 8) |
||
163 | avctx->pix_fmt = AV_PIX_FMT_PAL8; |
||
164 | break; |
||
165 | case XWD_TRUE_COLOR: |
||
166 | case XWD_DIRECT_COLOR: |
||
167 | if (bpp != 16 && bpp != 24 && bpp != 32) |
||
168 | return AVERROR_INVALIDDATA; |
||
169 | if (bpp == 16 && pixdepth == 15) { |
||
170 | if (rgb[0] == 0x7C00 && rgb[1] == 0x3E0 && rgb[2] == 0x1F) |
||
171 | avctx->pix_fmt = be ? AV_PIX_FMT_RGB555BE : AV_PIX_FMT_RGB555LE; |
||
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; |
||
174 | } else if (bpp == 16 && pixdepth == 16) { |
||
175 | if (rgb[0] == 0xF800 && rgb[1] == 0x7E0 && rgb[2] == 0x1F) |
||
176 | avctx->pix_fmt = be ? AV_PIX_FMT_RGB565BE : AV_PIX_FMT_RGB565LE; |
||
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; |
||
179 | } else if (bpp == 24) { |
||
180 | if (rgb[0] == 0xFF0000 && rgb[1] == 0xFF00 && rgb[2] == 0xFF) |
||
181 | avctx->pix_fmt = be ? AV_PIX_FMT_RGB24 : AV_PIX_FMT_BGR24; |
||
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; |
||
184 | } else if (bpp == 32) { |
||
185 | if (rgb[0] == 0xFF0000 && rgb[1] == 0xFF00 && rgb[2] == 0xFF) |
||
186 | avctx->pix_fmt = be ? AV_PIX_FMT_ARGB : AV_PIX_FMT_BGRA; |
||
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; |
||
189 | } |
||
190 | bytestream2_skipu(&gb, ncolors * XWD_CMAP_SIZE); |
||
191 | break; |
||
192 | default: |
||
193 | av_log(avctx, AV_LOG_ERROR, "invalid visual class\n"); |
||
194 | return AVERROR_INVALIDDATA; |
||
195 | } |
||
196 | |||
197 | if (avctx->pix_fmt == AV_PIX_FMT_NONE) { |
||
198 | avpriv_request_sample(avctx, |
||
199 | "Unknown file: bpp %d, pixdepth %d, vclass %d", |
||
200 | bpp, pixdepth, vclass); |
||
201 | return AVERROR_PATCHWELCOME; |
||
202 | } |
||
203 | |||
204 | if ((ret = ff_get_buffer(avctx, p, 0)) < 0) |
||
205 | return ret; |
||
206 | |||
207 | p->key_frame = 1; |
||
208 | p->pict_type = AV_PICTURE_TYPE_I; |
||
209 | |||
210 | if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { |
||
211 | uint32_t *dst = (uint32_t *)p->data[1]; |
||
212 | uint8_t red, green, blue; |
||
213 | |||
214 | for (i = 0; i < ncolors; i++) { |
||
215 | |||
216 | bytestream2_skipu(&gb, 4); // skip colormap entry number |
||
217 | red = bytestream2_get_byteu(&gb); |
||
218 | bytestream2_skipu(&gb, 1); |
||
219 | green = bytestream2_get_byteu(&gb); |
||
220 | bytestream2_skipu(&gb, 1); |
||
221 | blue = bytestream2_get_byteu(&gb); |
||
222 | bytestream2_skipu(&gb, 3); // skip bitmask flag and padding |
||
223 | |||
224 | dst[i] = red << 16 | green << 8 | blue; |
||
225 | } |
||
226 | } |
||
227 | |||
228 | ptr = p->data[0]; |
||
229 | for (i = 0; i < avctx->height; i++) { |
||
230 | bytestream2_get_bufferu(&gb, ptr, rsize); |
||
231 | bytestream2_skipu(&gb, lsize - rsize); |
||
232 | ptr += p->linesize[0]; |
||
233 | } |
||
234 | |||
235 | *got_frame = 1; |
||
236 | |||
237 | return buf_size; |
||
238 | } |
||
239 | |||
240 | AVCodec ff_xwd_decoder = { |
||
241 | .name = "xwd", |
||
242 | .long_name = NULL_IF_CONFIG_SMALL("XWD (X Window Dump) image"), |
||
243 | .type = AVMEDIA_TYPE_VIDEO, |
||
244 | .id = AV_CODEC_ID_XWD, |
||
245 | .decode = xwd_decode_frame, |
||
246 | .capabilities = CODEC_CAP_DR1, |
||
247 | };>><>><>>>>>>>>> |