1 /* sane - Scanner Access Now Easy.
2 *
3 * Copyright (C) 2020 Thierry HUCHARD <thierry@ordissimo.com>
4 * See LICENSE for license terms and conditions
5 */
6
7 #include "airscan.h"
8
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #if defined(OS_HAVE_ENDIAN_H)
15 # include <endian.h>
16 #elif defined(OS_HAVE_SYS_ENDIAN_H)
17 # include <sys/endian.h>
18 #endif
19
20 /* BITMAPFILEHEADER structure, see MSDN for details
21 */
22 #pragma pack (push,1)
23 typedef struct {
24 uint16_t bfType; /* File magic, always 'BM' */
25 uint32_t bfSize; /* File size in bytes */
26 uint16_t bfReserved1; /* Reserved; must be zero */
27 uint16_t bfReserved2; /* Reserved; must be zero */
28 uint32_t bfOffBits; /* Offset to bitmap bits */
29 } BITMAPFILEHEADER;
30 #pragma pack (pop)
31
32 /* BITMAPINFOHEADER structure, see MSDN for details
33 */
34 #pragma pack (push,1)
35 typedef struct {
36 uint32_t biSize; /* Header size, bytes */
37 int32_t biWidth; /* Image width, pixels */
38 int32_t biHeight; /* Image height, pixels */
39 uint16_t biPlanes; /* Number of planes, always 1 */
40 uint16_t biBitCount; /* Bits per pixel */
41 uint32_t biCompression; /* Compression type, see MSDN */
42 uint32_t biSizeImage; /* Image size, can be o */
43 int32_t biXPelsPerMeter; /* Horizontal resolution, pixels per meter */
44 int32_t biYPelsPerMeter; /* Vertical resolution, pixels per meter */
45 uint32_t biClrUsed; /* Number of used palette indices */
46 uint32_t biClrImportant; /* Number of important palette indices */
47 } BITMAPINFOHEADER;
48 #pragma pack (pop)
49
50 /* BMP image decoder
51 */
52 typedef struct {
53 image_decoder decoder; /* Base class */
54 char error[256]; /* Error message buffer */
55 const uint8_t *image_data; /* Image data */
56 BITMAPINFOHEADER info_header; /* DIB header, decoded */
57 size_t bmp_row_size; /* Row size in BMP file */
58 SANE_Frame format; /* SANE_FRAME_GRAY/RBG */
59 unsigned int next_line; /* Next line to read */
60 } image_decoder_bmp;
61
62 /* Free BMP decoder
63 */
64 static void
image_decoder_bmp_free(image_decoder * decoder)65 image_decoder_bmp_free (image_decoder *decoder)
66 {
67 image_decoder_bmp *bmp = (image_decoder_bmp*) decoder;
68 mem_free(bmp);
69 }
70
71 /* Begin BMP decoding
72 */
73 static error
image_decoder_bmp_begin(image_decoder * decoder,const void * data,size_t size)74 image_decoder_bmp_begin (image_decoder *decoder, const void *data,
75 size_t size)
76 {
77 image_decoder_bmp *bmp = (image_decoder_bmp*) decoder;
78 BITMAPFILEHEADER file_header;
79 size_t header_size, padding;
80 uint64_t size_required;
81
82 /* Decode BMP header */
83 if (size < sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)) {
84 return ERROR("BMP: header truncated");
85 }
86
87 memcpy(&file_header, data, sizeof(BITMAPFILEHEADER));
88 memcpy(&bmp->info_header, ((char*) data) + sizeof(BITMAPFILEHEADER),
89 sizeof(BITMAPINFOHEADER));
90
91 file_header.bfType = le16toh(file_header.bfType);
92 file_header.bfSize = le32toh(file_header.bfSize);
93 file_header.bfOffBits = le32toh(file_header.bfOffBits);
94
95 bmp->info_header.biSize = le32toh(bmp->info_header.biSize);
96 bmp->info_header.biWidth = le32toh(bmp->info_header.biWidth);
97 bmp->info_header.biHeight = le32toh(bmp->info_header.biHeight);
98 bmp->info_header.biPlanes = le16toh(bmp->info_header.biPlanes);
99 bmp->info_header.biBitCount = le16toh(bmp->info_header.biBitCount);
100 bmp->info_header.biCompression = le32toh(bmp->info_header.biCompression);
101 bmp->info_header.biSizeImage = le32toh(bmp->info_header.biSizeImage);
102 bmp->info_header.biXPelsPerMeter = le32toh(bmp->info_header.biXPelsPerMeter);
103 bmp->info_header.biYPelsPerMeter = le32toh(bmp->info_header.biYPelsPerMeter);
104 bmp->info_header.biClrUsed = le32toh(bmp->info_header.biClrUsed);
105 bmp->info_header.biClrImportant = le32toh(bmp->info_header.biClrImportant);
106
107 /* Validate BMP header */
108 if (file_header.bfType != ('M' << 8 | 'B')) {
109 return ERROR("BMP: invalid header signature");
110 }
111
112 if (bmp->info_header.biSize < sizeof(BITMAPINFOHEADER)) {
113 sprintf(bmp->error, "BMP: invalid header size %d",
114 bmp->info_header.biSize);
115 return ERROR(bmp->error);
116 }
117
118 if (bmp->info_header.biCompression != 0) {
119 sprintf(bmp->error, "BMP: compression %d not supported",
120 bmp->info_header.biCompression);
121 return ERROR(bmp->error);
122 }
123
124 /* Ignore palette for 8-bit (grayscale) images, reject it otherwise */
125 if (bmp->info_header.biClrUsed != 0 && bmp->info_header.biBitCount != 8) {
126 return ERROR("BMP: paletted images not supported");
127 }
128
129 switch (bmp->info_header.biBitCount) {
130 case 8:
131 bmp->format = SANE_FRAME_GRAY;
132 break;
133
134 case 24:
135 case 32:
136 bmp->format = SANE_FRAME_RGB;
137 break;
138
139 default:
140 sprintf(bmp->error, "BMP: %d bits per pixel not supported",
141 bmp->info_header.biBitCount);
142 return ERROR(bmp->error);
143 }
144
145 /* Compute BMP row size */
146 bmp->bmp_row_size = bmp->info_header.biWidth;
147 bmp->bmp_row_size *= bmp->info_header.biBitCount / 8;
148 padding = (4 - (bmp->bmp_row_size & 3)) & 3;
149 bmp->bmp_row_size += padding;
150
151 /* Make sure image is not truncated */
152 header_size = sizeof(BITMAPFILEHEADER) + bmp->info_header.biSize;
153 header_size += (size_t) bmp->info_header.biClrUsed * 4;
154 size_required = header_size;
155 size_required += ((uint64_t) labs(bmp->info_header.biHeight)) *
156 (uint64_t) bmp->bmp_row_size;
157 size_required -= padding; /* Last row may be unpadded */
158
159 if (size_required > (uint64_t) size) {
160 return ERROR("BMP: image truncated");
161 }
162
163 /* Save pointer to image data */
164 bmp->image_data = header_size + (const uint8_t*) data;
165
166 return NULL;
167 }
168
169 /* Reset BMP decoder
170 */
171 static void
image_decoder_bmp_reset(image_decoder * decoder)172 image_decoder_bmp_reset (image_decoder *decoder)
173 {
174 image_decoder_bmp *bmp = (image_decoder_bmp*) decoder;
175 size_t off = sizeof(bmp->decoder);
176
177 memset(((char*) bmp) + off, 0, sizeof(*bmp) - off);
178 }
179
180 /* Get bytes count per pixel
181 */
182 static int
image_decoder_bmp_get_bytes_per_pixel(image_decoder * decoder)183 image_decoder_bmp_get_bytes_per_pixel (image_decoder *decoder)
184 {
185 image_decoder_bmp *bmp = (image_decoder_bmp*) decoder;
186
187 return bmp->format == SANE_FRAME_GRAY ? 1 : 3;
188 }
189
190 /* Get image parameters
191 */
192 static void
image_decoder_bmp_get_params(image_decoder * decoder,SANE_Parameters * params)193 image_decoder_bmp_get_params (image_decoder *decoder, SANE_Parameters *params)
194 {
195 image_decoder_bmp *bmp = (image_decoder_bmp*) decoder;
196
197 params->last_frame = SANE_TRUE;
198 params->pixels_per_line = bmp->info_header.biWidth;
199 params->lines = labs(bmp->info_header.biHeight);
200 params->depth = 8;
201 params->format = bmp->format;
202 params->bytes_per_line = params->pixels_per_line;
203 if (params->format == SANE_FRAME_RGB) {
204 params->bytes_per_line *= 3;
205 }
206 }
207
208 /* Set clipping window
209 */
210 static error
image_decoder_bmp_set_window(image_decoder * decoder,image_window * win)211 image_decoder_bmp_set_window (image_decoder *decoder, image_window *win)
212 {
213 image_decoder_bmp *bmp = (image_decoder_bmp*) decoder;
214
215 win->x_off = win->y_off = 0;
216 win->wid = bmp->info_header.biWidth;
217 win->hei = labs(bmp->info_header.biHeight);
218
219 return NULL;
220 }
221
222 /* Read next line of image
223 */
224 static error
image_decoder_bmp_read_line(image_decoder * decoder,void * buffer)225 image_decoder_bmp_read_line (image_decoder *decoder, void *buffer)
226 {
227 image_decoder_bmp *bmp = (image_decoder_bmp*) decoder;
228 size_t row_num;
229 const uint8_t *row_data;
230 int i, wid = bmp->info_header.biWidth;
231 uint8_t *out = buffer;
232
233 if (bmp->next_line == (unsigned int) labs(bmp->info_header.biHeight)) {
234 return ERROR("BMP: end of file");
235 }
236
237 /* Compute row number */
238 row_num = bmp->next_line ++;
239 if (bmp->info_header.biHeight > 0) {
240 row_num = bmp->info_header.biHeight - row_num - 1;
241 }
242
243 /* Compute row address */
244 row_data = bmp->image_data + row_num * bmp->bmp_row_size;
245
246 /* Decode the row */
247 switch (bmp->info_header.biBitCount) {
248 case 8:
249 memcpy(out, row_data, wid);
250 break;
251
252 case 24:
253 for (i = 0; i < wid; i ++) {
254 out[0] = row_data[2]; /* Red */
255 out[1] = row_data[1]; /* Green */
256 out[2] = row_data[0]; /* Blue */
257 out += 3;
258 row_data += 3;
259 }
260 break;
261
262 case 32:
263 for (i = 0; i < wid; i ++) {
264 out[0] = row_data[2]; /* Red */
265 out[1] = row_data[1]; /* Green */
266 out[2] = row_data[0]; /* Blue */
267 out += 3;
268 row_data += 4;
269 }
270 break;
271
272 default:
273 log_internal_error(NULL);
274 }
275
276 return NULL;
277 }
278
279 /* Create BMP image decoder
280 */
281 image_decoder*
image_decoder_bmp_new(void)282 image_decoder_bmp_new (void)
283 {
284 image_decoder_bmp *bmp = mem_new(image_decoder_bmp, 1);
285
286 bmp->decoder.content_type = "image/bmp";
287 bmp->decoder.free = image_decoder_bmp_free;
288 bmp->decoder.begin = image_decoder_bmp_begin;
289 bmp->decoder.reset = image_decoder_bmp_reset;
290 bmp->decoder.get_bytes_per_pixel = image_decoder_bmp_get_bytes_per_pixel;
291 bmp->decoder.get_params = image_decoder_bmp_get_params;
292 bmp->decoder.set_window = image_decoder_bmp_set_window;
293 bmp->decoder.read_line = image_decoder_bmp_read_line;
294
295 return &bmp->decoder;
296 }
297
298 /* vim:ts=8:sw=4:et
299 */
300