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 *) &in;
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