Fixed part of write a bmp file, its still truncating the image slightly
[blender.git] / source / blender / imbuf / intern / bmp.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  */
32
33 #ifdef _WIN32
34 #include "BLI_winstuff.h"
35 #endif
36 #include "BLI_blenlib.h"
37
38 #include "imbuf.h"
39 #include "imbuf_patch.h"
40
41 #include "IMB_imbuf_types.h"
42 #include "IMB_imbuf.h"
43 #include "IMB_allocimbuf.h"
44 #include "IMB_cmap.h"
45 #include "IMB_bmp.h"
46
47 #ifdef HAVE_CONFIG_H
48 #include <config.h>
49 #endif
50
51 /* some code copied from article on microsoft.com, copied
52   here for enhanced BMP support in the future
53   http://www.microsoft.com/msj/defaultframe.asp?page=/msj/0197/mfcp1/mfcp1.htm&nav=/msj/0197/newnav.htm
54 */
55
56 typedef struct BMPINFOHEADER{
57         unsigned int    biSize;
58         unsigned int    biWidth;
59         unsigned int    biHeight;
60         unsigned short  biPlanes;
61         unsigned short  biBitCount;
62         unsigned int    biCompression;
63         unsigned int    biSizeImage;
64         unsigned int    biXPelsPerMeter;
65         unsigned int    biYPelsPerMeter;
66         unsigned int    biClrUsed;
67         unsigned int    biClrImportant;
68 } BMPINFOHEADER;
69
70 typedef struct BMPHEADER {
71         unsigned short biType;
72         unsigned int biSize;
73         unsigned short biRes1;
74         unsigned short biRes2;
75         unsigned int biOffBits;
76 } BMPHEADER;
77
78 #define BMP_FILEHEADER_SIZE 14
79
80 static int checkbmp(unsigned char *mem)
81 {
82         int ret_val = 0;
83         BMPINFOHEADER bmi;
84         unsigned int u;
85
86         if (mem) {
87                 if ((mem[0] == 'B') && (mem[1] == 'M')) {
88                         /* skip fileheader */
89                         mem += BMP_FILEHEADER_SIZE;
90                 } else {
91                 }
92
93                 /* for systems where an int needs to be 4 bytes aligned */
94                 memcpy(&bmi, mem, sizeof(bmi));
95
96                 u = LITTLE_LONG(bmi.biSize);
97                 /* we only support uncompressed 24 or 32 bits images for now */
98                 if (u >= sizeof(BMPINFOHEADER)) {
99                         if ((bmi.biCompression == 0) && (bmi.biClrUsed == 0)) {
100                                 u = LITTLE_SHORT(bmi.biBitCount);
101                                 if (u >= 16) {
102                                         ret_val = 1;
103                                 }
104                         }
105                 }
106         }
107
108         return(ret_val);
109 }
110
111 int imb_is_a_bmp(void *buf) {
112         
113         return checkbmp(buf);
114 }
115
116 struct ImBuf *imb_bmp_decode(unsigned char *mem, int size, int flags)
117 {
118         struct ImBuf *ibuf = 0;
119         BMPINFOHEADER bmi;
120         int x, y, depth, skip, i;
121         unsigned char *bmp, *rect;
122         unsigned short col;
123
124         if (checkbmp(mem) == 0) return(0);
125
126         if ((mem[0] == 'B') && (mem[1] == 'M')) {
127                 /* skip fileheader */
128                 mem += BMP_FILEHEADER_SIZE;
129         }
130
131         /* for systems where an int needs to be 4 bytes aligned */
132         memcpy(&bmi, mem, sizeof(bmi));
133
134         skip = LITTLE_LONG(bmi.biSize);
135         x = LITTLE_LONG(bmi.biWidth);
136         y = LITTLE_LONG(bmi.biHeight);
137         depth = LITTLE_SHORT(bmi.biBitCount);
138
139         /* printf("skip: %d, x: %d y: %d, depth: %d (%x)\n", skip, x, y, 
140                 depth, bmi.biBitCount); */
141         printf("skip: %d, x: %d y: %d, depth: %d (%x)\n", skip, x, y, 
142                 depth, bmi.biBitCount);
143         if (flags & IB_test) {
144                 ibuf = IMB_allocImBuf(x, y, depth, 0, 0);
145         } else {
146                 ibuf = IMB_allocImBuf(x, y, depth, IB_rect, 0);
147                 bmp = mem + skip;
148                 rect = (unsigned char *) ibuf->rect;
149
150                 if (depth == 16) {
151                         for (i = x * y; i > 0; i--) {
152                                 col = bmp[0] + (bmp[1] << 8);
153                                 rect[0] = ((col >> 10) & 0x1f) << 3;
154                                 rect[1] = ((col >>  5) & 0x1f) << 3;
155                                 rect[2] = ((col >>  0) & 0x1f) << 3;
156                                 
157                                 rect[3] = 255;
158                                 rect += 4; bmp += 2;
159                         }
160
161                 } else if (depth == 24) {
162                         for (i = x * y; i > 0; i--) {
163                                 rect[0] = bmp[2];
164                                 rect[1] = bmp[1];
165                                 rect[2] = bmp[0];
166                                 
167                                 rect[3] = 255;
168                                 rect += 4; bmp += 3;
169                         }
170                 } else if (depth == 32) {
171                         for (i = x * y; i > 0; i--) {
172                                 rect[0] = bmp[0];
173                                 rect[1] = bmp[1];
174                                 rect[2] = bmp[2];
175                                 rect[3] = bmp[3];
176                                 rect += 4; bmp += 4;
177                         }
178                 }
179         }
180
181         if (ibuf) {
182                 ibuf->ftype = BMP;
183         }
184         
185         return(ibuf);
186 }
187
188 /* Couple of helper functions for writing our data */
189 int putIntLSB(unsigned int ui,FILE *ofile) { 
190    putc((ui>>0)&0xFF,ofile); 
191    putc((ui>>8)&0xFF,ofile); 
192    putc((ui>>16)&0xFF,ofile); 
193    return putc((ui>>24)&0xFF,ofile); 
194 }
195
196 int putShortLSB(unsigned short us,FILE *ofile) { 
197    putc((us>>0)&0xFF,ofile); 
198    return putc((us>>8)&0xFF,ofile); 
199
200
201 /* Found write info at http://users.ece.gatech.edu/~slabaugh/personal/c/bitmapUnix.c */
202 short imb_savebmp(struct ImBuf *ibuf, int outfile, int flags) {
203
204    BMPINFOHEADER infoheader;
205    int bytesize, extrabytes, x, y, t, ptr;
206    uchar *data;
207    FILE *ofile;
208
209    extrabytes = (4 - ibuf->x*3 % 4) % 4;
210    bytesize = (ibuf->x * 3 + extrabytes) * ibuf->y;
211
212    data = (uchar *) ibuf->rect;
213    ofile = fdopen(outfile,"ab");
214
215    putShortLSB(19778,ofile); /* "BM" */
216    putIntLSB(0,ofile); /* This can be 0 for BI_RGB bitmaps */
217    putShortLSB(0,ofile); /* Res1 */
218    putShortLSB(0,ofile); /* Res2 */
219    putIntLSB(BMP_FILEHEADER_SIZE + sizeof(infoheader),ofile); 
220
221    putIntLSB(sizeof(infoheader),ofile);
222    putIntLSB(ibuf->x,ofile);
223    putIntLSB(ibuf->y,ofile);
224    putShortLSB(1,ofile);
225    putShortLSB(24,ofile);
226    putIntLSB(0,ofile);
227    putIntLSB(bytesize + BMP_FILEHEADER_SIZE + sizeof(infoheader),ofile);
228    putIntLSB(0,ofile);
229    putIntLSB(0,ofile);
230    putIntLSB(0,ofile);
231    putIntLSB(0,ofile);
232
233    /* Need to write out padded image data in bgr format */
234    for (y=0;y<ibuf->y;y++) {
235       for (x=0;x<ibuf->x;x++) {
236
237          ptr=(x + y * ibuf->x) * 4;
238          if (putc(data[ptr+2],ofile) == EOF) return 0;
239          if (putc(data[ptr+1],ofile) == EOF) return 0;
240          if (putc(data[ptr],ofile) == EOF) return 0;
241
242       }
243       /* add padding here */
244       for (t=0;t<extrabytes;t++) if (putc(0,ofile) == EOF) return 0;
245    }
246    return 1;
247 }