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