1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3 /* Copyright (C) 2018-2021 Hans Petter Jansson
4 *
5 * This file is part of Chafa, a program that turns images into character art.
6 *
7 * Chafa is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Chafa 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
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with Chafa. If not, see <http://www.gnu.org/licenses/>. */
19
20 #include "config.h"
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdbool.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30
31 #include <chafa.h>
32 #include "xwd-loader.h"
33
34 #define DEBUG(x)
35
36 typedef struct
37 {
38 guint32 header_size; /* Size of the header in bytes */
39 guint32 file_version; /* X11WD file version (always 07h) */
40 guint32 pixmap_format; /* Pixmap format */
41 guint32 pixmap_depth; /* Pixmap depth in pixels */
42 guint32 pixmap_width; /* Pixmap width in pixels */
43 guint32 pixmap_height; /* Pixmap height in pixels */
44 guint32 x_offset; /* Bitmap X offset */
45 guint32 byte_order; /* Byte order of image data */
46 guint32 bitmap_unit; /* Bitmap base data size */
47 guint32 bitmap_bit_order; /* Bit-order of image data */
48 guint32 bitmap_pad; /* Bitmap scan-line pad*/
49 guint32 bits_per_pixel; /* Bits per pixel */
50 guint32 bytes_per_line; /* Bytes per scan-line */
51 guint32 visual_class; /* Class of the image */
52 guint32 red_mask; /* Red mask */
53 guint32 green_mask; /* Green mask */
54 guint32 blue_mask; /* Blue mask */
55 guint32 bits_per_rgb; /* Size of each color mask in bits */
56 guint32 n_colors; /* Number of colors in image */
57 guint32 color_map_entries; /* Number of entries in color map */
58 guint32 window_width; /* Window width */
59 guint32 window_height; /* Window height */
60 gint32 window_x; /* Window upper left X coordinate */
61 gint32 window_y; /* Window upper left Y coordinate */
62 guint32 window_border_width; /* Window border width */
63 }
64 XwdHeader;
65
66 typedef struct
67 {
68 guint32 pixel;
69 guint16 red;
70 guint16 green;
71 guint16 blue;
72 guint8 flags;
73 guint8 pad;
74 }
75 XwdColor;
76
77 struct XwdLoader
78 {
79 FileMapping *mapping;
80 gconstpointer file_data;
81 gconstpointer image_data;
82 gsize file_data_len;
83 XwdHeader header;
84 };
85
DEBUG(static void dump_header (XwdHeader * header){ g_printerr ("Header size: %u\\n" "File version: %u\\n" "Pixmap format: %u\\n" "Pixmap depth: %u\\n" "Pixmap width: %u\\n" "Pixmap height: %u\\n" "X offset: %u\\n" "Byte order: %u\\n" "Bitmap unit: %u\\n" "Bitmap bit order: %u\\n" "Bitmap pad: %u\\n" "Bits per pixel: %u\\n" "Bytes per line: %u\\n" "Visual class: %u\\n" "Red mask: %u\\n" "Green mask: %u\\n" "Blue mask: %u\\n" "Bits per RGB: %u\\n" "Number of colors: %u\\n" "Color map entries: %u\\n" "Window width: %u\\n" "Window height: %u\\n" "Window X: %d\\n" "Window Y: %d\\n" "Window border width: %u\\n---\\n", header->header_size, header->file_version, header->pixmap_format, header->pixmap_depth, header->pixmap_width, header->pixmap_height, header->x_offset, header->byte_order, header->bitmap_unit, header->bitmap_bit_order, header->bitmap_pad, header->bits_per_pixel, header->bytes_per_line, header->visual_class, header->red_mask, header->green_mask, header->blue_mask, header->bits_per_rgb, header->n_colors, header->color_map_entries, header->window_width, header->window_height, header->window_x, header->window_y, header->window_border_width); } )86 DEBUG (
87 static void
88 dump_header (XwdHeader *header)
89 {
90 g_printerr ("Header size: %u\n"
91 "File version: %u\n"
92 "Pixmap format: %u\n"
93 "Pixmap depth: %u\n"
94 "Pixmap width: %u\n"
95 "Pixmap height: %u\n"
96 "X offset: %u\n"
97 "Byte order: %u\n"
98 "Bitmap unit: %u\n"
99 "Bitmap bit order: %u\n"
100 "Bitmap pad: %u\n"
101 "Bits per pixel: %u\n"
102 "Bytes per line: %u\n"
103 "Visual class: %u\n"
104 "Red mask: %u\n"
105 "Green mask: %u\n"
106 "Blue mask: %u\n"
107 "Bits per RGB: %u\n"
108 "Number of colors: %u\n"
109 "Color map entries: %u\n"
110 "Window width: %u\n"
111 "Window height: %u\n"
112 "Window X: %d\n"
113 "Window Y: %d\n"
114 "Window border width: %u\n---\n",
115 header->header_size,
116 header->file_version,
117 header->pixmap_format,
118 header->pixmap_depth,
119 header->pixmap_width,
120 header->pixmap_height,
121 header->x_offset,
122 header->byte_order,
123 header->bitmap_unit,
124 header->bitmap_bit_order,
125 header->bitmap_pad,
126 header->bits_per_pixel,
127 header->bytes_per_line,
128 header->visual_class,
129 header->red_mask,
130 header->green_mask,
131 header->blue_mask,
132 header->bits_per_rgb,
133 header->n_colors,
134 header->color_map_entries,
135 header->window_width,
136 header->window_height,
137 header->window_x,
138 header->window_y,
139 header->window_border_width);
140 }
141 )
142
143 static ChafaPixelType
144 compute_pixel_type (XwdLoader *loader)
145 {
146 XwdHeader *h = &loader->header;
147
148 if (h->bits_per_pixel == 24)
149 {
150 if (h->byte_order == 0)
151 return CHAFA_PIXEL_BGR8;
152 else
153 return CHAFA_PIXEL_RGB8;
154 }
155
156 if (h->bits_per_pixel == 32)
157 {
158 if (h->byte_order == 0)
159 return CHAFA_PIXEL_BGRA8_PREMULTIPLIED;
160 else
161 return CHAFA_PIXEL_ARGB8_PREMULTIPLIED;
162 }
163
164 return CHAFA_PIXEL_MAX;
165 }
166
167 #define ASSERT_HEADER(x) if (!(x)) return FALSE
168
169 static gboolean
load_header(XwdLoader * loader)170 load_header (XwdLoader *loader) // gconstpointer in, gsize in_max_len, XwdHeader *header_out)
171 {
172 XwdHeader *h = &loader->header;
173 XwdHeader in;
174 const guint32 *p = (const guint32 *) ∈
175
176 if (!file_mapping_taste (loader->mapping, &in, 0, sizeof (in)))
177 return FALSE;
178
179 h->header_size = g_ntohl (*(p++));
180 h->file_version = g_ntohl (*(p++));
181 h->pixmap_format = g_ntohl (*(p++));
182 h->pixmap_depth = g_ntohl (*(p++));
183 h->pixmap_width = g_ntohl (*(p++));
184 h->pixmap_height = g_ntohl (*(p++));
185 h->x_offset = g_ntohl (*(p++));
186 h->byte_order = g_ntohl (*(p++));
187 h->bitmap_unit = g_ntohl (*(p++));
188 h->bitmap_bit_order = g_ntohl (*(p++));
189 h->bitmap_pad = g_ntohl (*(p++));
190 h->bits_per_pixel = g_ntohl (*(p++));
191 h->bytes_per_line = g_ntohl (*(p++));
192 h->visual_class = g_ntohl (*(p++));
193 h->red_mask = g_ntohl (*(p++));
194 h->green_mask = g_ntohl (*(p++));
195 h->blue_mask = g_ntohl (*(p++));
196 h->bits_per_rgb = g_ntohl (*(p++));
197 h->color_map_entries = g_ntohl (*(p++));
198 h->n_colors = g_ntohl (*(p++));
199 h->window_width = g_ntohl (*(p++));
200 h->window_height = g_ntohl (*(p++));
201 h->window_x = g_ntohl (*(p++));
202 h->window_y = g_ntohl (*(p++));
203 h->window_border_width = g_ntohl (*(p++));
204
205 /* Only support the most common/useful subset of XWD files out there;
206 * namely, that corresponding to screen dumps from modern X.Org servers. */
207
208 ASSERT_HEADER (h->header_size >= sizeof (XwdHeader));
209 ASSERT_HEADER (h->file_version == 7);
210 ASSERT_HEADER (h->pixmap_depth == 24);
211 ASSERT_HEADER (h->bits_per_rgb == 8);
212 ASSERT_HEADER (h->bytes_per_line >= h->pixmap_width * (h->bits_per_pixel / 8));
213 ASSERT_HEADER (compute_pixel_type (loader) < CHAFA_PIXEL_MAX);
214
215 loader->file_data = file_mapping_get_data (loader->mapping, &loader->file_data_len);
216 if (!loader->file_data)
217 return FALSE;
218
219 ASSERT_HEADER (loader->file_data_len >= h->header_size
220 + h->n_colors * sizeof (XwdColor)
221 + h->pixmap_height * h->bytes_per_line);
222
223 loader->image_data = (const guint8 *) loader->file_data
224 + h->header_size + h->n_colors * sizeof (XwdColor);
225
226 return TRUE;
227 }
228
229 static XwdLoader *
xwd_loader_new(void)230 xwd_loader_new (void)
231 {
232 return g_new0 (XwdLoader, 1);
233 }
234
235 XwdLoader *
xwd_loader_new_from_mapping(FileMapping * mapping)236 xwd_loader_new_from_mapping (FileMapping *mapping)
237 {
238 XwdLoader *loader;
239
240 g_return_val_if_fail (mapping != NULL, NULL);
241
242 loader = xwd_loader_new ();
243 loader->mapping = mapping;
244
245 if (!load_header (loader))
246 {
247 g_free (loader);
248 return NULL;
249 }
250
251 DEBUG (dump_header (&loader->header));
252
253 return loader;
254 }
255
256 void
xwd_loader_destroy(XwdLoader * loader)257 xwd_loader_destroy (XwdLoader *loader)
258 {
259 if (loader->mapping)
260 file_mapping_destroy (loader->mapping);
261
262 g_free (loader);
263 }
264
265 gconstpointer
xwd_loader_get_image_data(XwdLoader * loader,ChafaPixelType * pixel_type_out,gint * width_out,gint * height_out,gint * rowstride_out)266 xwd_loader_get_image_data (XwdLoader *loader, ChafaPixelType *pixel_type_out,
267 gint *width_out, gint *height_out, gint *rowstride_out)
268 {
269 g_return_val_if_fail (loader != NULL, NULL);
270
271 if (pixel_type_out)
272 *pixel_type_out = compute_pixel_type (loader);
273 if (width_out)
274 *width_out = loader->header.pixmap_width;
275 if (height_out)
276 *height_out = loader->header.pixmap_height;
277 if (rowstride_out)
278 *rowstride_out = loader->header.bytes_per_line;
279
280 return loader->image_data;
281 }
282