Iriver: Calculate proper height (i.e., size) for grayscale bitmaps.
[kugel-rb.git] / apps / recorder / bmp.c
blobeb9bf903a64269d633dd596a263dc75c8fa3c357
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Linus Nielsen Feltzing
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
21 2005-04-16 Tomas Salfischberger:
22 - New BMP loader function, based on the old one (borrowed a lot of
23 calculations and checks there.)
24 - Conversion part needs some optimization, doing unneeded calulations now.
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include "debug.h"
31 #include "lcd.h"
32 #include "file.h"
33 #include "config.h"
34 #include "bmp.h"
35 #include "lcd.h"
37 #ifdef __GNUC__
38 #define STRUCT_PACKED __attribute__((packed))
39 #else
40 #define STRUCT_PACKED
41 #pragma pack (push, 2)
42 #endif
44 /* Struct from original code. */
45 struct Fileheader {
46 unsigned short Type; /* signature - 'BM' */
47 unsigned long Size; /* file size in bytes */
48 unsigned short Reserved1; /* 0 */
49 unsigned short Reserved2; /* 0 */
50 unsigned long OffBits; /* offset to bitmap */
51 unsigned long StructSize; /* size of this struct (40) */
52 unsigned long Width; /* bmap width in pixels */
53 unsigned long Height; /* bmap height in pixels */
54 unsigned short Planes; /* num planes - always 1 */
55 unsigned short BitCount; /* bits per pixel */
56 unsigned long Compression; /* compression flag */
57 unsigned long SizeImage; /* image size in bytes */
58 long XPelsPerMeter; /* horz resolution */
59 long YPelsPerMeter; /* vert resolution */
60 unsigned long ClrUsed; /* 0 -> color table size */
61 unsigned long ClrImportant; /* important color count */
62 } STRUCT_PACKED;
64 struct rgb_quad { /* Little endian */
65 unsigned char blue;
66 unsigned char green;
67 unsigned char red;
68 unsigned char reserved;
69 } STRUCT_PACKED;
71 #ifdef ROCKBOX_LITTLE_ENDIAN
72 #define readshort(x) *(x)
73 #define readlong(x) *(x)
74 #else
76 /* big endian functions */
77 static short readshort(short *value) {
78 unsigned char* bytes = (unsigned char*) value;
79 return bytes[0] | (bytes[1] << 8);
82 static long readlong(long *value) {
83 unsigned char* bytes = (unsigned char*) value;
84 return (long)bytes[0] | ((long)bytes[1] << 8) |
85 ((long)bytes[2] << 16) | ((long)bytes[3] << 24);
88 #endif
90 unsigned char brightness(struct rgb_quad color)
92 return (3 * (unsigned int)color.red + 6 * (unsigned int)color.green
93 + (unsigned int)color.blue) / 10;
96 /* Function to get a pixel from a line. (Tomas: maybe a better way?) */
97 inline int getpix(int px, unsigned char *bmpbuf) {
98 int a = (px / 8);
99 int b = (7 - (px % 8));
101 return (bmpbuf[a] & (1 << b)) != 0;
105 /******************************************************************************
106 * read_bmp_file()
108 * Reads a monochrome BMP file and puts the data in rockbox format in *bitmap.
110 *****************************************************************************/
111 int read_bmp_file(char* filename,
112 struct bitmap *bm,
113 int maxsize,
114 int format)
116 struct Fileheader fh;
117 int width, height, PaddedWidth, PaddedHeight;
118 int fd, row, col, ret;
119 struct rgb_quad palette[256];
120 int invert_pixel = 0;
121 int numcolors;
122 int depth;
123 int totalsize;
124 char *bitmap = bm->data;
126 unsigned char bmpbuf[LCD_WIDTH*sizeof(struct rgb_quad)]; /* Buffer for one line */
128 #if LCD_DEPTH == 1
129 (void)format;
130 #else
131 bool transparent;
133 if(format & FORMAT_TRANSPARENT) {
134 transparent = true;
135 format &= ~FORMAT_TRANSPARENT;
137 #endif
140 fd = open(filename, O_RDONLY);
142 /* Exit if file opening failed */
143 if (fd < 0) {
144 DEBUGF("error - can't open '%s' open returned: %d\n", filename, fd);
145 return (fd * 10) - 1;
148 /* read fileheader */
149 ret = read(fd, &fh, sizeof(struct Fileheader));
150 if(ret < 0) {
151 close(fd);
152 return (ret * 10 - 2);
155 if(ret != sizeof(struct Fileheader)) {
156 DEBUGF("error - can't read Fileheader structure.");
157 close(fd);
158 return -3;
161 /* Exit if too wide */
162 if (readlong(&fh.Width) > LCD_WIDTH) {
163 DEBUGF("error - Bitmap is too wide (%d pixels, max is %d)\n",
164 readlong(&fh.Width), LCD_WIDTH);
165 close(fd);
166 return -5;
169 /* Exit if too high */
170 if (readlong(&fh.Height) > LCD_HEIGHT) {
171 DEBUGF("error - Bitmap is too high (%d pixels, max is %d)\n",
172 readlong(&fh.Height), LCD_HEIGHT);
173 close(fd);
174 return -6;
177 /* Calculate image size */
178 height = readlong(&fh.Height);
179 width = readlong(&fh.Width);
180 depth = readshort(&fh.BitCount);
182 /* 4-byte boundary aligned */
183 PaddedWidth = (width * depth / 8 + 3) & ~3;
185 #if LCD_DEPTH > 1
186 if(format == FORMAT_ANY) {
187 if(depth == 1)
188 format = FORMAT_MONO;
189 else
190 format = FORMAT_NATIVE;
192 #endif
194 /* PaddedHeight is for rockbox format. */
195 if(format == FORMAT_MONO) {
196 PaddedHeight = (height + 7) / 8;
197 totalsize = PaddedHeight * width;
198 } else {
199 #if LCD_DEPTH == 2
200 PaddedHeight = (height + 3) / 4;
201 #else
202 PaddedHeight = height;
203 #endif
204 totalsize = PaddedHeight * width * sizeof(fb_data);
207 /* Check if this fits the buffer */
209 if (totalsize > maxsize) {
210 DEBUGF("error - Bitmap is too large to fit the supplied buffer: "
211 "%d bytes.\n", (PaddedHeight * width));
212 close(fd);
213 return -7;
216 if (depth <= 8)
218 numcolors = readlong(&fh.ClrUsed);
219 if (numcolors == 0)
220 numcolors = 1 << depth;
222 if(read(fd, palette, numcolors * sizeof(struct rgb_quad))
223 != numcolors * (int)sizeof(struct rgb_quad))
225 DEBUGF("error - Can't read bitmap's color palette\n");
226 close(fd);
227 return -8;
231 /* Use the darker palette color as foreground on mono bitmaps */
232 if(readshort(&fh.BitCount) == 1) {
233 if(brightness(palette[0]) > brightness(palette[1]))
234 invert_pixel = 1;
237 /* Search to the beginning of the image data */
238 lseek(fd, (off_t)readlong(&fh.OffBits), SEEK_SET);
240 #if LCD_DEPTH == 2
241 if(format == FORMAT_NATIVE)
242 memset(bitmap, 0, width * height / 4);
243 #endif
245 #if LCD_DEPTH > 1
246 fb_data *dest = (fb_data *)bitmap;
247 #endif
249 /* loop to read rows and put them to buffer */
250 for (row = 0; row < height; row++) {
251 unsigned char *p;
253 /* read one row */
254 ret = read(fd, bmpbuf, PaddedWidth);
255 if (ret != PaddedWidth) {
256 DEBUGF("error reading image, read returned: %d expected was: "
257 "%d\n", ret, PaddedWidth);
258 close(fd);
259 return -9;
262 switch(depth) {
263 case 1:
264 #if LCD_DEPTH > 1
265 if(format == FORMAT_MONO) {
266 #endif
267 /* Mono -> Mono */
268 for (col = 0; col < width; col++) {
269 ret = getpix(col, bmpbuf) ^ invert_pixel;
270 if (ret == 1) {
271 bitmap[width * ((height - row - 1) / 8) + col]
272 &= ~ 1 << ((height - row - 1) % 8);
273 } else {
274 bitmap[width * ((height - row - 1) / 8) + col]
275 |= 1 << ((height - row - 1) % 8);
278 #if LCD_DEPTH == 2
279 } else {
280 /* Mono -> 2gray (iriver H1xx) */
281 for (col = 0; col < width; col++) {
282 ret = brightness(palette[getpix(col, bmpbuf)]);
284 if (ret > 96) {
285 bitmap[width * ((height - row - 1) / 8) + col]
286 &= ~ 1 << ((height - row - 1) % 8);
287 } else {
288 bitmap[width * ((height - row - 1) / 8) + col]
289 |= 1 << ((height - row - 1) % 8);
293 #elif LCD_DEPTH == 16
294 } else {
295 /* Mono -> RGB16 */
296 for (col = 0; col < width; col++) {
297 ret = getpix(col, bmpbuf);
298 unsigned short rgb = (((palette[ret].red >> 3) << 11) |
299 ((palette[ret].green >> 2) << 5) |
300 ((palette[ret].blue >> 3)));
301 dest[width * (height - row - 1) + col] = rgb;
304 #endif
305 break;
307 case 24:
308 p = bmpbuf;
309 #if LCD_DEPTH > 1
310 if(format == FORMAT_MONO) {
311 #endif
312 /* RGB24 -> mono */
313 for (col = 0; col < width; col++) {
314 struct rgb_quad rgb;
315 rgb.red = p[2];
316 rgb.green = p[1];
317 rgb.blue = p[0];
318 ret = brightness(rgb);
319 if (ret > 96) {
320 bitmap[width * ((height - row - 1) / 8) + col]
321 &= ~ 1 << ((height - row - 1) % 8);
322 } else {
323 bitmap[width * ((height - row - 1) / 8) + col]
324 |= 1 << ((height - row - 1) % 8);
326 p += 3;
328 #if LCD_DEPTH == 2
329 } else {
330 /* RGB24 -> 2gray (iriver H1xx) */
331 for (col = 0; col < width; col++) {
332 struct rgb_quad rgb;
333 rgb.red = p[2];
334 rgb.green = p[1];
335 rgb.blue = p[0];
336 ret = brightness(rgb);
338 dest[((height - row - 1)/4) * width + col] |=
339 (~ret & 0xC0) >> (2 * (~(height - row - 1) & 3));
340 p += 3;
343 #elif LCD_DEPTH == 16
344 } else {
345 /* RGB24 -> RGB16 */
346 for (col = 0; col < width; col++) {
347 unsigned short rgb = (((p[2] >> 3) << 11) |
348 ((p[1] >> 2) << 5) |
349 ((p[0] >> 3)));
350 dest[width * (height - row - 1) + col] = rgb;
351 p += 3;
354 #endif
355 break;
359 close(fd);
361 /* returning image size: */
362 bm->width = width;
363 bm->height = height;
364 #if LCD_DEPTH > 1
365 bm->format = format;
366 #endif
368 DEBUGF("totalsize: %d\n", totalsize);
369 return totalsize; /* return the used buffer size. */