slouken@0
|
1 |
/*
|
slouken@5535
|
2 |
Simple DirectMedia Layer
|
slouken@9619
|
3 |
Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
|
slouken@0
|
4 |
|
slouken@5535
|
5 |
This software is provided 'as-is', without any express or implied
|
slouken@5535
|
6 |
warranty. In no event will the authors be held liable for any damages
|
slouken@5535
|
7 |
arising from the use of this software.
|
slouken@0
|
8 |
|
slouken@5535
|
9 |
Permission is granted to anyone to use this software for any purpose,
|
slouken@5535
|
10 |
including commercial applications, and to alter it and redistribute it
|
slouken@5535
|
11 |
freely, subject to the following restrictions:
|
slouken@0
|
12 |
|
slouken@5535
|
13 |
1. The origin of this software must not be misrepresented; you must not
|
slouken@5535
|
14 |
claim that you wrote the original software. If you use this software
|
slouken@5535
|
15 |
in a product, an acknowledgment in the product documentation would be
|
slouken@5535
|
16 |
appreciated but is not required.
|
slouken@5535
|
17 |
2. Altered source versions must be plainly marked as such, and must not be
|
slouken@5535
|
18 |
misrepresented as being the original software.
|
slouken@5535
|
19 |
3. This notice may not be removed or altered from any source distribution.
|
slouken@0
|
20 |
*/
|
icculus@8093
|
21 |
#include "../SDL_internal.h"
|
slouken@0
|
22 |
|
slouken@7191
|
23 |
/*
|
slouken@0
|
24 |
Code to load and save surfaces in Windows BMP format.
|
slouken@0
|
25 |
|
slouken@0
|
26 |
Why support BMP format? Well, it's a native format for Windows, and
|
slouken@0
|
27 |
most image processing programs can read and write it. It would be nice
|
slouken@0
|
28 |
to be able to have at least one image format that we can natively load
|
slouken@0
|
29 |
and save, and since PNG is so complex that it would bloat the library,
|
slouken@7191
|
30 |
BMP is a good alternative.
|
slouken@0
|
31 |
|
slouken@0
|
32 |
This code currently supports Win32 DIBs in uncompressed 8 and 24 bpp.
|
slouken@0
|
33 |
*/
|
slouken@0
|
34 |
|
slouken@0
|
35 |
#include "SDL_video.h"
|
icculus@8675
|
36 |
#include "SDL_assert.h"
|
slouken@0
|
37 |
#include "SDL_endian.h"
|
slouken@2823
|
38 |
#include "SDL_pixels_c.h"
|
slouken@0
|
39 |
|
slouken@6094
|
40 |
#define SAVE_32BIT_BMP
|
slouken@6094
|
41 |
|
slouken@0
|
42 |
/* Compression encodings for BMP files */
|
slouken@0
|
43 |
#ifndef BI_RGB
|
slouken@7191
|
44 |
#define BI_RGB 0
|
slouken@7191
|
45 |
#define BI_RLE8 1
|
slouken@7191
|
46 |
#define BI_RLE4 2
|
slouken@7191
|
47 |
#define BI_BITFIELDS 3
|
slouken@0
|
48 |
#endif
|
slouken@0
|
49 |
|
slouken@0
|
50 |
|
slouken@7378
|
51 |
static void CorrectAlphaChannel(SDL_Surface *surface)
|
slouken@7378
|
52 |
{
|
slouken@7378
|
53 |
/* Check to see if there is any alpha channel data */
|
slouken@7378
|
54 |
SDL_bool hasAlpha = SDL_FALSE;
|
slouken@7378
|
55 |
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
slouken@7378
|
56 |
int alphaChannelOffset = 0;
|
slouken@7378
|
57 |
#else
|
slouken@7378
|
58 |
int alphaChannelOffset = 3;
|
slouken@7378
|
59 |
#endif
|
slouken@7378
|
60 |
Uint8 *alpha = ((Uint8*)surface->pixels) + alphaChannelOffset;
|
slouken@7378
|
61 |
Uint8 *end = alpha + surface->h * surface->pitch;
|
slouken@7378
|
62 |
|
slouken@7378
|
63 |
while (alpha < end) {
|
slouken@7378
|
64 |
if (*alpha != 0) {
|
slouken@7378
|
65 |
hasAlpha = SDL_TRUE;
|
slouken@7378
|
66 |
break;
|
slouken@7378
|
67 |
}
|
slouken@7378
|
68 |
alpha += 4;
|
slouken@7378
|
69 |
}
|
slouken@7378
|
70 |
|
slouken@7378
|
71 |
if (!hasAlpha) {
|
slouken@7378
|
72 |
alpha = ((Uint8*)surface->pixels) + alphaChannelOffset;
|
slouken@7378
|
73 |
while (alpha < end) {
|
slouken@7378
|
74 |
*alpha = SDL_ALPHA_OPAQUE;
|
slouken@7378
|
75 |
alpha += 4;
|
slouken@7378
|
76 |
}
|
slouken@7378
|
77 |
}
|
slouken@7378
|
78 |
}
|
slouken@7378
|
79 |
|
slouken@1895
|
80 |
SDL_Surface *
|
slouken@1895
|
81 |
SDL_LoadBMP_RW(SDL_RWops * src, int freesrc)
|
slouken@0
|
82 |
{
|
slouken@3310
|
83 |
SDL_bool was_error;
|
slouken@6666
|
84 |
Sint64 fp_offset = 0;
|
slouken@1895
|
85 |
int bmpPitch;
|
slouken@1895
|
86 |
int i, pad;
|
slouken@1895
|
87 |
SDL_Surface *surface;
|
icculus@9126
|
88 |
Uint32 Rmask = 0;
|
icculus@9126
|
89 |
Uint32 Gmask = 0;
|
icculus@9126
|
90 |
Uint32 Bmask = 0;
|
icculus@9126
|
91 |
Uint32 Amask = 0;
|
slouken@1895
|
92 |
SDL_Palette *palette;
|
slouken@1895
|
93 |
Uint8 *bits;
|
slouken@3310
|
94 |
Uint8 *top, *end;
|
slouken@3310
|
95 |
SDL_bool topDown;
|
slouken@1895
|
96 |
int ExpandBMP;
|
icculus@9126
|
97 |
SDL_bool haveRGBMasks = SDL_FALSE;
|
icculus@9126
|
98 |
SDL_bool haveAlphaMask = SDL_FALSE;
|
slouken@7378
|
99 |
SDL_bool correctAlpha = SDL_FALSE;
|
slouken@0
|
100 |
|
slouken@1895
|
101 |
/* The Win32 BMP file header (14 bytes) */
|
slouken@1895
|
102 |
char magic[2];
|
gabomdq@7678
|
103 |
/* Uint32 bfSize = 0; */
|
gabomdq@7678
|
104 |
/* Uint16 bfReserved1 = 0; */
|
gabomdq@7678
|
105 |
/* Uint16 bfReserved2 = 0; */
|
icculus@6389
|
106 |
Uint32 bfOffBits = 0;
|
slouken@0
|
107 |
|
slouken@1895
|
108 |
/* The Win32 BITMAPINFOHEADER struct (40 bytes) */
|
icculus@6389
|
109 |
Uint32 biSize = 0;
|
icculus@6389
|
110 |
Sint32 biWidth = 0;
|
icculus@6389
|
111 |
Sint32 biHeight = 0;
|
gabomdq@7678
|
112 |
/* Uint16 biPlanes = 0; */
|
icculus@6389
|
113 |
Uint16 biBitCount = 0;
|
icculus@6389
|
114 |
Uint32 biCompression = 0;
|
gabomdq@7678
|
115 |
/* Uint32 biSizeImage = 0; */
|
gabomdq@7678
|
116 |
/* Sint32 biXPelsPerMeter = 0; */
|
gabomdq@7678
|
117 |
/* Sint32 biYPelsPerMeter = 0; */
|
icculus@6389
|
118 |
Uint32 biClrUsed = 0;
|
gabomdq@7678
|
119 |
/* Uint32 biClrImportant = 0; */
|
slouken@0
|
120 |
|
slouken@1895
|
121 |
/* Make sure we are passed a valid data source */
|
slouken@1895
|
122 |
surface = NULL;
|
slouken@3310
|
123 |
was_error = SDL_FALSE;
|
slouken@1895
|
124 |
if (src == NULL) {
|
slouken@3310
|
125 |
was_error = SDL_TRUE;
|
slouken@1895
|
126 |
goto done;
|
slouken@1895
|
127 |
}
|
slouken@0
|
128 |
|
slouken@1895
|
129 |
/* Read in the BMP file header */
|
slouken@1895
|
130 |
fp_offset = SDL_RWtell(src);
|
slouken@1895
|
131 |
SDL_ClearError();
|
slouken@1895
|
132 |
if (SDL_RWread(src, magic, 1, 2) != 2) {
|
slouken@1895
|
133 |
SDL_Error(SDL_EFREAD);
|
slouken@3310
|
134 |
was_error = SDL_TRUE;
|
slouken@1895
|
135 |
goto done;
|
slouken@1895
|
136 |
}
|
slouken@1895
|
137 |
if (SDL_strncmp(magic, "BM", 2) != 0) {
|
slouken@1895
|
138 |
SDL_SetError("File is not a Windows BMP file");
|
slouken@3310
|
139 |
was_error = SDL_TRUE;
|
slouken@1895
|
140 |
goto done;
|
slouken@1895
|
141 |
}
|
gabomdq@7678
|
142 |
/* bfSize = */ SDL_ReadLE32(src);
|
gabomdq@7678
|
143 |
/* bfReserved1 = */ SDL_ReadLE16(src);
|
gabomdq@7678
|
144 |
/* bfReserved2 = */ SDL_ReadLE16(src);
|
slouken@1895
|
145 |
bfOffBits = SDL_ReadLE32(src);
|
slouken@0
|
146 |
|
slouken@1895
|
147 |
/* Read the Win32 BITMAPINFOHEADER */
|
slouken@1895
|
148 |
biSize = SDL_ReadLE32(src);
|
icculus@9126
|
149 |
if (biSize == 12) { /* really old BITMAPCOREHEADER */
|
slouken@1895
|
150 |
biWidth = (Uint32) SDL_ReadLE16(src);
|
slouken@1895
|
151 |
biHeight = (Uint32) SDL_ReadLE16(src);
|
gabomdq@7678
|
152 |
/* biPlanes = */ SDL_ReadLE16(src);
|
slouken@1895
|
153 |
biBitCount = SDL_ReadLE16(src);
|
slouken@1895
|
154 |
biCompression = BI_RGB;
|
icculus@9126
|
155 |
} else if (biSize >= 40) { /* some version of BITMAPINFOHEADER */
|
icculus@9126
|
156 |
Uint32 headerSize;
|
slouken@1895
|
157 |
biWidth = SDL_ReadLE32(src);
|
slouken@1895
|
158 |
biHeight = SDL_ReadLE32(src);
|
gabomdq@7678
|
159 |
/* biPlanes = */ SDL_ReadLE16(src);
|
slouken@1895
|
160 |
biBitCount = SDL_ReadLE16(src);
|
slouken@1895
|
161 |
biCompression = SDL_ReadLE32(src);
|
gabomdq@7678
|
162 |
/* biSizeImage = */ SDL_ReadLE32(src);
|
gabomdq@7678
|
163 |
/* biXPelsPerMeter = */ SDL_ReadLE32(src);
|
gabomdq@7678
|
164 |
/* biYPelsPerMeter = */ SDL_ReadLE32(src);
|
slouken@1895
|
165 |
biClrUsed = SDL_ReadLE32(src);
|
gabomdq@7678
|
166 |
/* biClrImportant = */ SDL_ReadLE32(src);
|
slouken@7913
|
167 |
|
icculus@9126
|
168 |
/* 64 == BITMAPCOREHEADER2, an incompatible OS/2 2.x extension. Skip this stuff for now. */
|
icculus@9126
|
169 |
if (biSize == 64) {
|
icculus@9126
|
170 |
/* ignore these extra fields. */
|
icculus@9126
|
171 |
if (biCompression == BI_BITFIELDS) {
|
icculus@9126
|
172 |
/* this value is actually huffman compression in this variant. */
|
icculus@9126
|
173 |
SDL_SetError("Compressed BMP files not supported");
|
icculus@9126
|
174 |
was_error = SDL_TRUE;
|
icculus@9126
|
175 |
goto done;
|
icculus@9126
|
176 |
}
|
icculus@9126
|
177 |
} else {
|
icculus@9126
|
178 |
/* This is complicated. If compression is BI_BITFIELDS, then
|
icculus@9126
|
179 |
we have 3 DWORDS that specify the RGB masks. This is either
|
icculus@9126
|
180 |
stored here in an BITMAPV2INFOHEADER (which only differs in
|
icculus@9126
|
181 |
that it adds these RGB masks) and biSize >= 52, or we've got
|
icculus@9126
|
182 |
these masks stored in the exact same place, but strictly
|
icculus@9126
|
183 |
speaking, this is the bmiColors field in BITMAPINFO immediately
|
icculus@9126
|
184 |
following the legacy v1 info header, just past biSize. */
|
icculus@9126
|
185 |
if (biCompression == BI_BITFIELDS) {
|
icculus@9126
|
186 |
haveRGBMasks = SDL_TRUE;
|
icculus@9126
|
187 |
Rmask = SDL_ReadLE32(src);
|
icculus@9126
|
188 |
Gmask = SDL_ReadLE32(src);
|
icculus@9126
|
189 |
Bmask = SDL_ReadLE32(src);
|
icculus@9126
|
190 |
|
icculus@9126
|
191 |
/* ...v3 adds an alpha mask. */
|
icculus@9126
|
192 |
if (biSize >= 56) { /* BITMAPV3INFOHEADER; adds alpha mask */
|
icculus@9126
|
193 |
haveAlphaMask = SDL_TRUE;
|
icculus@9126
|
194 |
Amask = SDL_ReadLE32(src);
|
icculus@9126
|
195 |
}
|
icculus@9126
|
196 |
} else {
|
icculus@9126
|
197 |
/* the mask fields are ignored for v2+ headers if not BI_BITFIELD. */
|
icculus@9126
|
198 |
if (biSize >= 52) { /* BITMAPV2INFOHEADER; adds RGB masks */
|
icculus@9126
|
199 |
/*Rmask = */ SDL_ReadLE32(src);
|
icculus@9126
|
200 |
/*Gmask = */ SDL_ReadLE32(src);
|
icculus@9126
|
201 |
/*Bmask = */ SDL_ReadLE32(src);
|
icculus@9126
|
202 |
}
|
icculus@9126
|
203 |
if (biSize >= 56) { /* BITMAPV3INFOHEADER; adds alpha mask */
|
icculus@9126
|
204 |
/*Amask = */ SDL_ReadLE32(src);
|
icculus@9126
|
205 |
}
|
icculus@9126
|
206 |
}
|
icculus@9126
|
207 |
|
icculus@9126
|
208 |
/* Insert other fields here; Wikipedia and MSDN say we're up to
|
icculus@9126
|
209 |
v5 of this header, but we ignore those for now (they add gamma,
|
icculus@9126
|
210 |
color spaces, etc). Ignoring the weird OS/2 2.x format, we
|
icculus@9126
|
211 |
currently parse up to v3 correctly (hopefully!). */
|
icculus@9126
|
212 |
}
|
icculus@9126
|
213 |
|
icculus@9126
|
214 |
/* skip any header bytes we didn't handle... */
|
icculus@9126
|
215 |
headerSize = (Uint32) (SDL_RWtell(src) - (fp_offset + 14));
|
slouken@7913
|
216 |
if (biSize > headerSize) {
|
slouken@7913
|
217 |
SDL_RWseek(src, (biSize - headerSize), RW_SEEK_CUR);
|
slouken@7913
|
218 |
}
|
slouken@1895
|
219 |
}
|
slouken@3310
|
220 |
if (biHeight < 0) {
|
slouken@3310
|
221 |
topDown = SDL_TRUE;
|
slouken@3310
|
222 |
biHeight = -biHeight;
|
slouken@3310
|
223 |
} else {
|
slouken@3310
|
224 |
topDown = SDL_FALSE;
|
slouken@3310
|
225 |
}
|
slouken@0
|
226 |
|
slouken@1895
|
227 |
/* Check for read error */
|
slouken@1895
|
228 |
if (SDL_strcmp(SDL_GetError(), "") != 0) {
|
slouken@3310
|
229 |
was_error = SDL_TRUE;
|
slouken@1895
|
230 |
goto done;
|
slouken@1895
|
231 |
}
|
slouken@0
|
232 |
|
slouken@1895
|
233 |
/* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
|
slouken@1895
|
234 |
switch (biBitCount) {
|
slouken@1895
|
235 |
case 1:
|
slouken@1895
|
236 |
case 4:
|
slouken@1895
|
237 |
ExpandBMP = biBitCount;
|
slouken@1895
|
238 |
biBitCount = 8;
|
slouken@1895
|
239 |
break;
|
slouken@1895
|
240 |
default:
|
slouken@1895
|
241 |
ExpandBMP = 0;
|
slouken@1895
|
242 |
break;
|
slouken@1895
|
243 |
}
|
slouken@0
|
244 |
|
slouken@1895
|
245 |
/* We don't support any BMP compression right now */
|
slouken@1895
|
246 |
switch (biCompression) {
|
slouken@1895
|
247 |
case BI_RGB:
|
slouken@1895
|
248 |
/* If there are no masks, use the defaults */
|
icculus@9126
|
249 |
SDL_assert(!haveRGBMasks);
|
icculus@9126
|
250 |
SDL_assert(!haveAlphaMask);
|
icculus@9126
|
251 |
/* Default values for the BMP format */
|
slouken@1895
|
252 |
switch (biBitCount) {
|
slouken@1895
|
253 |
case 15:
|
slouken@1895
|
254 |
case 16:
|
icculus@9126
|
255 |
Rmask = 0x7C00;
|
icculus@9126
|
256 |
Gmask = 0x03E0;
|
icculus@9126
|
257 |
Bmask = 0x001F;
|
icculus@9126
|
258 |
break;
|
icculus@9126
|
259 |
case 24:
|
icculus@9126
|
260 |
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
icculus@9126
|
261 |
Rmask = 0x000000FF;
|
icculus@9126
|
262 |
Gmask = 0x0000FF00;
|
icculus@9126
|
263 |
Bmask = 0x00FF0000;
|
icculus@9126
|
264 |
#else
|
icculus@9126
|
265 |
Rmask = 0x00FF0000;
|
icculus@9126
|
266 |
Gmask = 0x0000FF00;
|
icculus@9126
|
267 |
Bmask = 0x000000FF;
|
icculus@9126
|
268 |
#endif
|
slouken@3497
|
269 |
break;
|
slouken@1895
|
270 |
case 32:
|
icculus@9126
|
271 |
/* We don't know if this has alpha channel or not */
|
icculus@9126
|
272 |
correctAlpha = SDL_TRUE;
|
icculus@9126
|
273 |
Amask = 0xFF000000;
|
icculus@9126
|
274 |
Rmask = 0x00FF0000;
|
icculus@9126
|
275 |
Gmask = 0x0000FF00;
|
icculus@9126
|
276 |
Bmask = 0x000000FF;
|
slouken@1895
|
277 |
break;
|
slouken@1895
|
278 |
default:
|
slouken@1895
|
279 |
break;
|
slouken@1895
|
280 |
}
|
slouken@1895
|
281 |
break;
|
icculus@9126
|
282 |
|
icculus@9126
|
283 |
case BI_BITFIELDS:
|
icculus@9126
|
284 |
break; /* we handled this in the info header. */
|
icculus@9126
|
285 |
|
slouken@1895
|
286 |
default:
|
slouken@1895
|
287 |
SDL_SetError("Compressed BMP files not supported");
|
slouken@3310
|
288 |
was_error = SDL_TRUE;
|
slouken@1895
|
289 |
goto done;
|
slouken@1895
|
290 |
}
|
slouken@0
|
291 |
|
slouken@1895
|
292 |
/* Create a compatible surface, note that the colors are RGB ordered */
|
slouken@1895
|
293 |
surface =
|
slouken@1895
|
294 |
SDL_CreateRGBSurface(0, biWidth, biHeight, biBitCount, Rmask, Gmask,
|
slouken@3497
|
295 |
Bmask, Amask);
|
slouken@1895
|
296 |
if (surface == NULL) {
|
slouken@3310
|
297 |
was_error = SDL_TRUE;
|
slouken@1895
|
298 |
goto done;
|
slouken@1895
|
299 |
}
|
slouken@0
|
300 |
|
slouken@1895
|
301 |
/* Load the palette, if any */
|
slouken@1895
|
302 |
palette = (surface->format)->palette;
|
slouken@1895
|
303 |
if (palette) {
|
icculus@8675
|
304 |
SDL_assert(biBitCount <= 8);
|
slouken@1895
|
305 |
if (biClrUsed == 0) {
|
slouken@1895
|
306 |
biClrUsed = 1 << biBitCount;
|
slouken@1895
|
307 |
}
|
slouken@2920
|
308 |
if ((int) biClrUsed > palette->ncolors) {
|
philipp@9334
|
309 |
SDL_Color *colors;
|
philipp@9336
|
310 |
int ncolors = biClrUsed;
|
philipp@9334
|
311 |
colors =
|
slouken@2851
|
312 |
(SDL_Color *) SDL_realloc(palette->colors,
|
philipp@9336
|
313 |
ncolors *
|
slouken@2851
|
314 |
sizeof(*palette->colors));
|
philipp@9334
|
315 |
if (!colors) {
|
slouken@2851
|
316 |
SDL_OutOfMemory();
|
slouken@3310
|
317 |
was_error = SDL_TRUE;
|
slouken@2851
|
318 |
goto done;
|
slouken@2851
|
319 |
}
|
philipp@9336
|
320 |
palette->ncolors = ncolors;
|
philipp@9334
|
321 |
palette->colors = colors;
|
slouken@2920
|
322 |
} else if ((int) biClrUsed < palette->ncolors) {
|
slouken@2851
|
323 |
palette->ncolors = biClrUsed;
|
slouken@2851
|
324 |
}
|
slouken@1895
|
325 |
if (biSize == 12) {
|
slouken@1895
|
326 |
for (i = 0; i < (int) biClrUsed; ++i) {
|
slouken@1895
|
327 |
SDL_RWread(src, &palette->colors[i].b, 1, 1);
|
slouken@1895
|
328 |
SDL_RWread(src, &palette->colors[i].g, 1, 1);
|
slouken@1895
|
329 |
SDL_RWread(src, &palette->colors[i].r, 1, 1);
|
slouken@7024
|
330 |
palette->colors[i].a = SDL_ALPHA_OPAQUE;
|
slouken@1895
|
331 |
}
|
slouken@1895
|
332 |
} else {
|
slouken@1895
|
333 |
for (i = 0; i < (int) biClrUsed; ++i) {
|
slouken@1895
|
334 |
SDL_RWread(src, &palette->colors[i].b, 1, 1);
|
slouken@1895
|
335 |
SDL_RWread(src, &palette->colors[i].g, 1, 1);
|
slouken@1895
|
336 |
SDL_RWread(src, &palette->colors[i].r, 1, 1);
|
slouken@7024
|
337 |
SDL_RWread(src, &palette->colors[i].a, 1, 1);
|
slouken@7068
|
338 |
|
slouken@7068
|
339 |
/* According to Microsoft documentation, the fourth element
|
slouken@7068
|
340 |
is reserved and must be zero, so we shouldn't treat it as
|
slouken@7068
|
341 |
alpha.
|
slouken@7068
|
342 |
*/
|
slouken@7068
|
343 |
palette->colors[i].a = SDL_ALPHA_OPAQUE;
|
slouken@1895
|
344 |
}
|
slouken@1895
|
345 |
}
|
slouken@1895
|
346 |
}
|
slouken@0
|
347 |
|
slouken@1895
|
348 |
/* Read the surface pixels. Note that the bmp image is upside down */
|
slouken@1895
|
349 |
if (SDL_RWseek(src, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
|
slouken@1895
|
350 |
SDL_Error(SDL_EFSEEK);
|
slouken@3310
|
351 |
was_error = SDL_TRUE;
|
slouken@1895
|
352 |
goto done;
|
slouken@1895
|
353 |
}
|
slouken@3310
|
354 |
top = (Uint8 *)surface->pixels;
|
slouken@3310
|
355 |
end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
|
slouken@1895
|
356 |
switch (ExpandBMP) {
|
slouken@1895
|
357 |
case 1:
|
slouken@1895
|
358 |
bmpPitch = (biWidth + 7) >> 3;
|
slouken@1895
|
359 |
pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
|
slouken@1895
|
360 |
break;
|
slouken@1895
|
361 |
case 4:
|
slouken@1895
|
362 |
bmpPitch = (biWidth + 1) >> 1;
|
slouken@1895
|
363 |
pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
|
slouken@1895
|
364 |
break;
|
slouken@1895
|
365 |
default:
|
slouken@1895
|
366 |
pad = ((surface->pitch % 4) ? (4 - (surface->pitch % 4)) : 0);
|
slouken@1895
|
367 |
break;
|
slouken@1895
|
368 |
}
|
slouken@3310
|
369 |
if (topDown) {
|
slouken@3310
|
370 |
bits = top;
|
slouken@3310
|
371 |
} else {
|
slouken@3310
|
372 |
bits = end - surface->pitch;
|
slouken@3310
|
373 |
}
|
slouken@3310
|
374 |
while (bits >= top && bits < end) {
|
slouken@1895
|
375 |
switch (ExpandBMP) {
|
slouken@1895
|
376 |
case 1:
|
slouken@3310
|
377 |
case 4:{
|
slouken@1895
|
378 |
Uint8 pixel = 0;
|
slouken@1895
|
379 |
int shift = (8 - ExpandBMP);
|
slouken@1895
|
380 |
for (i = 0; i < surface->w; ++i) {
|
slouken@1895
|
381 |
if (i % (8 / ExpandBMP) == 0) {
|
slouken@1895
|
382 |
if (!SDL_RWread(src, &pixel, 1, 1)) {
|
slouken@1895
|
383 |
SDL_SetError("Error reading from BMP");
|
slouken@3310
|
384 |
was_error = SDL_TRUE;
|
slouken@1895
|
385 |
goto done;
|
slouken@1895
|
386 |
}
|
slouken@1895
|
387 |
}
|
slouken@1895
|
388 |
*(bits + i) = (pixel >> shift);
|
slouken@1895
|
389 |
pixel <<= ExpandBMP;
|
slouken@1895
|
390 |
}
|
slouken@1895
|
391 |
}
|
slouken@1895
|
392 |
break;
|
slouken@0
|
393 |
|
slouken@1895
|
394 |
default:
|
slouken@1895
|
395 |
if (SDL_RWread(src, bits, 1, surface->pitch)
|
slouken@1895
|
396 |
!= surface->pitch) {
|
slouken@1895
|
397 |
SDL_Error(SDL_EFREAD);
|
slouken@3310
|
398 |
was_error = SDL_TRUE;
|
slouken@1895
|
399 |
goto done;
|
slouken@1895
|
400 |
}
|
slouken@0
|
401 |
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
slouken@1895
|
402 |
/* Byte-swap the pixels if needed. Note that the 24bpp
|
slouken@1895
|
403 |
case has already been taken care of above. */
|
slouken@1895
|
404 |
switch (biBitCount) {
|
slouken@1895
|
405 |
case 15:
|
slouken@3310
|
406 |
case 16:{
|
slouken@1895
|
407 |
Uint16 *pix = (Uint16 *) bits;
|
slouken@1895
|
408 |
for (i = 0; i < surface->w; i++)
|
slouken@1895
|
409 |
pix[i] = SDL_Swap16(pix[i]);
|
slouken@1895
|
410 |
break;
|
slouken@1895
|
411 |
}
|
slouken@0
|
412 |
|
slouken@3310
|
413 |
case 32:{
|
slouken@1895
|
414 |
Uint32 *pix = (Uint32 *) bits;
|
slouken@1895
|
415 |
for (i = 0; i < surface->w; i++)
|
slouken@1895
|
416 |
pix[i] = SDL_Swap32(pix[i]);
|
slouken@1895
|
417 |
break;
|
slouken@1895
|
418 |
}
|
slouken@1895
|
419 |
}
|
slouken@0
|
420 |
#endif
|
slouken@1895
|
421 |
break;
|
slouken@1895
|
422 |
}
|
slouken@1895
|
423 |
/* Skip padding bytes, ugh */
|
slouken@1895
|
424 |
if (pad) {
|
slouken@1895
|
425 |
Uint8 padbyte;
|
slouken@1895
|
426 |
for (i = 0; i < pad; ++i) {
|
slouken@1895
|
427 |
SDL_RWread(src, &padbyte, 1, 1);
|
slouken@1895
|
428 |
}
|
slouken@1895
|
429 |
}
|
slouken@3310
|
430 |
if (topDown) {
|
slouken@3310
|
431 |
bits += surface->pitch;
|
slouken@3310
|
432 |
} else {
|
slouken@3310
|
433 |
bits -= surface->pitch;
|
slouken@3310
|
434 |
}
|
slouken@1895
|
435 |
}
|
slouken@7378
|
436 |
if (correctAlpha) {
|
slouken@7378
|
437 |
CorrectAlphaChannel(surface);
|
slouken@7378
|
438 |
}
|
slouken@1895
|
439 |
done:
|
slouken@1895
|
440 |
if (was_error) {
|
slouken@1895
|
441 |
if (src) {
|
slouken@1895
|
442 |
SDL_RWseek(src, fp_offset, RW_SEEK_SET);
|
slouken@1895
|
443 |
}
|
slouken@7720
|
444 |
SDL_FreeSurface(surface);
|
slouken@1895
|
445 |
surface = NULL;
|
slouken@1895
|
446 |
}
|
slouken@1895
|
447 |
if (freesrc && src) {
|
slouken@1895
|
448 |
SDL_RWclose(src);
|
slouken@1895
|
449 |
}
|
slouken@1895
|
450 |
return (surface);
|
slouken@0
|
451 |
}
|
slouken@0
|
452 |
|
slouken@1895
|
453 |
int
|
slouken@1895
|
454 |
SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst)
|
slouken@0
|
455 |
{
|
slouken@6666
|
456 |
Sint64 fp_offset;
|
slouken@1895
|
457 |
int i, pad;
|
slouken@1895
|
458 |
SDL_Surface *surface;
|
slouken@1895
|
459 |
Uint8 *bits;
|
slouken@0
|
460 |
|
slouken@1895
|
461 |
/* The Win32 BMP file header (14 bytes) */
|
slouken@1895
|
462 |
char magic[2] = { 'B', 'M' };
|
slouken@1895
|
463 |
Uint32 bfSize;
|
slouken@1895
|
464 |
Uint16 bfReserved1;
|
slouken@1895
|
465 |
Uint16 bfReserved2;
|
slouken@1895
|
466 |
Uint32 bfOffBits;
|
slouken@0
|
467 |
|
slouken@1895
|
468 |
/* The Win32 BITMAPINFOHEADER struct (40 bytes) */
|
slouken@1895
|
469 |
Uint32 biSize;
|
slouken@1895
|
470 |
Sint32 biWidth;
|
slouken@1895
|
471 |
Sint32 biHeight;
|
slouken@1895
|
472 |
Uint16 biPlanes;
|
slouken@1895
|
473 |
Uint16 biBitCount;
|
slouken@1895
|
474 |
Uint32 biCompression;
|
slouken@1895
|
475 |
Uint32 biSizeImage;
|
slouken@1895
|
476 |
Sint32 biXPelsPerMeter;
|
slouken@1895
|
477 |
Sint32 biYPelsPerMeter;
|
slouken@1895
|
478 |
Uint32 biClrUsed;
|
slouken@1895
|
479 |
Uint32 biClrImportant;
|
slouken@0
|
480 |
|
slouken@1895
|
481 |
/* Make sure we have somewhere to save */
|
slouken@1895
|
482 |
surface = NULL;
|
slouken@1895
|
483 |
if (dst) {
|
slouken@2969
|
484 |
SDL_bool save32bit = SDL_FALSE;
|
slouken@2969
|
485 |
#ifdef SAVE_32BIT_BMP
|
slouken@2969
|
486 |
/* We can save alpha information in a 32-bit BMP */
|
slouken@2969
|
487 |
if (saveme->map->info.flags & SDL_COPY_COLORKEY ||
|
slouken@2969
|
488 |
saveme->format->Amask) {
|
slouken@2969
|
489 |
save32bit = SDL_TRUE;
|
slouken@2969
|
490 |
}
|
slouken@2969
|
491 |
#endif /* SAVE_32BIT_BMP */
|
slouken@2969
|
492 |
|
slouken@2969
|
493 |
if (saveme->format->palette && !save32bit) {
|
slouken@1895
|
494 |
if (saveme->format->BitsPerPixel == 8) {
|
slouken@1895
|
495 |
surface = saveme;
|
slouken@1895
|
496 |
} else {
|
slouken@1895
|
497 |
SDL_SetError("%d bpp BMP files not supported",
|
slouken@1895
|
498 |
saveme->format->BitsPerPixel);
|
slouken@1895
|
499 |
}
|
slouken@1895
|
500 |
} else if ((saveme->format->BitsPerPixel == 24) &&
|
slouken@0
|
501 |
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
|
slouken@1895
|
502 |
(saveme->format->Rmask == 0x00FF0000) &&
|
slouken@1895
|
503 |
(saveme->format->Gmask == 0x0000FF00) &&
|
slouken@1895
|
504 |
(saveme->format->Bmask == 0x000000FF)
|
slouken@0
|
505 |
#else
|
slouken@1895
|
506 |
(saveme->format->Rmask == 0x000000FF) &&
|
slouken@1895
|
507 |
(saveme->format->Gmask == 0x0000FF00) &&
|
slouken@1895
|
508 |
(saveme->format->Bmask == 0x00FF0000)
|
slouken@0
|
509 |
#endif
|
slouken@1895
|
510 |
) {
|
slouken@1895
|
511 |
surface = saveme;
|
slouken@1895
|
512 |
} else {
|
slouken@2967
|
513 |
SDL_PixelFormat format;
|
slouken@0
|
514 |
|
slouken@2969
|
515 |
/* If the surface has a colorkey or alpha channel we'll save a
|
slouken@2969
|
516 |
32-bit BMP with alpha channel, otherwise save a 24-bit BMP. */
|
slouken@2969
|
517 |
if (save32bit) {
|
slouken@7191
|
518 |
SDL_InitFormat(&format,
|
slouken@0
|
519 |
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
|
slouken@5288
|
520 |
SDL_PIXELFORMAT_ARGB8888
|
slouken@0
|
521 |
#else
|
slouken@5288
|
522 |
SDL_PIXELFORMAT_BGRA8888
|
slouken@0
|
523 |
#endif
|
slouken@5288
|
524 |
);
|
slouken@5288
|
525 |
} else {
|
slouken@5288
|
526 |
SDL_InitFormat(&format, SDL_PIXELFORMAT_BGR24);
|
slouken@2969
|
527 |
}
|
slouken@2967
|
528 |
surface = SDL_ConvertSurface(saveme, &format, 0);
|
slouken@2967
|
529 |
if (!surface) {
|
slouken@2990
|
530 |
SDL_SetError("Couldn't convert image to %d bpp",
|
slouken@2990
|
531 |
format.BitsPerPixel);
|
slouken@1895
|
532 |
}
|
slouken@1895
|
533 |
}
|
philipp@9563
|
534 |
} else {
|
philipp@9563
|
535 |
/* Set no error here because it may overwrite a more useful message from
|
philipp@9563
|
536 |
SDL_RWFromFile() if SDL_SaveBMP_RW() is called from SDL_SaveBMP(). */
|
philipp@9563
|
537 |
return -1;
|
slouken@1895
|
538 |
}
|
slouken@0
|
539 |
|
slouken@1895
|
540 |
if (surface && (SDL_LockSurface(surface) == 0)) {
|
slouken@1895
|
541 |
const int bw = surface->w * surface->format->BytesPerPixel;
|
slouken@1895
|
542 |
|
slouken@1895
|
543 |
/* Set the BMP file header values */
|
slouken@1895
|
544 |
bfSize = 0; /* We'll write this when we're done */
|
slouken@1895
|
545 |
bfReserved1 = 0;
|
slouken@1895
|
546 |
bfReserved2 = 0;
|
slouken@1895
|
547 |
bfOffBits = 0; /* We'll write this when we're done */
|
slouken@1012
|
548 |
|
slouken@1895
|
549 |
/* Write the BMP file header values */
|
slouken@1895
|
550 |
fp_offset = SDL_RWtell(dst);
|
slouken@1895
|
551 |
SDL_ClearError();
|
slouken@1895
|
552 |
SDL_RWwrite(dst, magic, 2, 1);
|
slouken@1895
|
553 |
SDL_WriteLE32(dst, bfSize);
|
slouken@1895
|
554 |
SDL_WriteLE16(dst, bfReserved1);
|
slouken@1895
|
555 |
SDL_WriteLE16(dst, bfReserved2);
|
slouken@1895
|
556 |
SDL_WriteLE32(dst, bfOffBits);
|
slouken@0
|
557 |
|
slouken@1895
|
558 |
/* Set the BMP info values */
|
slouken@1895
|
559 |
biSize = 40;
|
slouken@1895
|
560 |
biWidth = surface->w;
|
slouken@1895
|
561 |
biHeight = surface->h;
|
slouken@1895
|
562 |
biPlanes = 1;
|
slouken@1895
|
563 |
biBitCount = surface->format->BitsPerPixel;
|
slouken@1895
|
564 |
biCompression = BI_RGB;
|
slouken@1895
|
565 |
biSizeImage = surface->h * surface->pitch;
|
slouken@1895
|
566 |
biXPelsPerMeter = 0;
|
slouken@1895
|
567 |
biYPelsPerMeter = 0;
|
slouken@1895
|
568 |
if (surface->format->palette) {
|
slouken@1895
|
569 |
biClrUsed = surface->format->palette->ncolors;
|
slouken@1895
|
570 |
} else {
|
slouken@1895
|
571 |
biClrUsed = 0;
|
slouken@1895
|
572 |
}
|
slouken@1895
|
573 |
biClrImportant = 0;
|
slouken@0
|
574 |
|
slouken@1895
|
575 |
/* Write the BMP info values */
|
slouken@1895
|
576 |
SDL_WriteLE32(dst, biSize);
|
slouken@1895
|
577 |
SDL_WriteLE32(dst, biWidth);
|
slouken@1895
|
578 |
SDL_WriteLE32(dst, biHeight);
|
slouken@1895
|
579 |
SDL_WriteLE16(dst, biPlanes);
|
slouken@1895
|
580 |
SDL_WriteLE16(dst, biBitCount);
|
slouken@1895
|
581 |
SDL_WriteLE32(dst, biCompression);
|
slouken@1895
|
582 |
SDL_WriteLE32(dst, biSizeImage);
|
slouken@1895
|
583 |
SDL_WriteLE32(dst, biXPelsPerMeter);
|
slouken@1895
|
584 |
SDL_WriteLE32(dst, biYPelsPerMeter);
|
slouken@1895
|
585 |
SDL_WriteLE32(dst, biClrUsed);
|
slouken@1895
|
586 |
SDL_WriteLE32(dst, biClrImportant);
|
slouken@0
|
587 |
|
slouken@1895
|
588 |
/* Write the palette (in BGR color order) */
|
slouken@1895
|
589 |
if (surface->format->palette) {
|
slouken@1895
|
590 |
SDL_Color *colors;
|
slouken@1895
|
591 |
int ncolors;
|
slouken@0
|
592 |
|
slouken@1895
|
593 |
colors = surface->format->palette->colors;
|
slouken@1895
|
594 |
ncolors = surface->format->palette->ncolors;
|
slouken@1895
|
595 |
for (i = 0; i < ncolors; ++i) {
|
slouken@1895
|
596 |
SDL_RWwrite(dst, &colors[i].b, 1, 1);
|
slouken@1895
|
597 |
SDL_RWwrite(dst, &colors[i].g, 1, 1);
|
slouken@1895
|
598 |
SDL_RWwrite(dst, &colors[i].r, 1, 1);
|
slouken@7024
|
599 |
SDL_RWwrite(dst, &colors[i].a, 1, 1);
|
slouken@1895
|
600 |
}
|
slouken@1895
|
601 |
}
|
slouken@0
|
602 |
|
slouken@1895
|
603 |
/* Write the bitmap offset */
|
slouken@6666
|
604 |
bfOffBits = (Uint32)(SDL_RWtell(dst) - fp_offset);
|
slouken@1895
|
605 |
if (SDL_RWseek(dst, fp_offset + 10, RW_SEEK_SET) < 0) {
|
slouken@1895
|
606 |
SDL_Error(SDL_EFSEEK);
|
slouken@1895
|
607 |
}
|
slouken@1895
|
608 |
SDL_WriteLE32(dst, bfOffBits);
|
slouken@1895
|
609 |
if (SDL_RWseek(dst, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
|
slouken@1895
|
610 |
SDL_Error(SDL_EFSEEK);
|
slouken@1895
|
611 |
}
|
slouken@0
|
612 |
|
slouken@1895
|
613 |
/* Write the bitmap image upside down */
|
slouken@1895
|
614 |
bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
|
slouken@1895
|
615 |
pad = ((bw % 4) ? (4 - (bw % 4)) : 0);
|
slouken@1895
|
616 |
while (bits > (Uint8 *) surface->pixels) {
|
slouken@1895
|
617 |
bits -= surface->pitch;
|
slouken@1895
|
618 |
if (SDL_RWwrite(dst, bits, 1, bw) != bw) {
|
slouken@1895
|
619 |
SDL_Error(SDL_EFWRITE);
|
slouken@1895
|
620 |
break;
|
slouken@1895
|
621 |
}
|
slouken@1895
|
622 |
if (pad) {
|
slouken@1895
|
623 |
const Uint8 padbyte = 0;
|
slouken@1895
|
624 |
for (i = 0; i < pad; ++i) {
|
slouken@1895
|
625 |
SDL_RWwrite(dst, &padbyte, 1, 1);
|
slouken@1895
|
626 |
}
|
slouken@1895
|
627 |
}
|
slouken@1895
|
628 |
}
|
slouken@0
|
629 |
|
slouken@1895
|
630 |
/* Write the BMP file size */
|
slouken@6666
|
631 |
bfSize = (Uint32)(SDL_RWtell(dst) - fp_offset);
|
slouken@1895
|
632 |
if (SDL_RWseek(dst, fp_offset + 2, RW_SEEK_SET) < 0) {
|
slouken@1895
|
633 |
SDL_Error(SDL_EFSEEK);
|
slouken@1895
|
634 |
}
|
slouken@1895
|
635 |
SDL_WriteLE32(dst, bfSize);
|
slouken@1895
|
636 |
if (SDL_RWseek(dst, fp_offset + bfSize, RW_SEEK_SET) < 0) {
|
slouken@1895
|
637 |
SDL_Error(SDL_EFSEEK);
|
slouken@1895
|
638 |
}
|
slouken@0
|
639 |
|
slouken@1895
|
640 |
/* Close it up.. */
|
slouken@1895
|
641 |
SDL_UnlockSurface(surface);
|
slouken@1895
|
642 |
if (surface != saveme) {
|
slouken@1895
|
643 |
SDL_FreeSurface(surface);
|
slouken@1895
|
644 |
}
|
slouken@1895
|
645 |
}
|
slouken@0
|
646 |
|
slouken@1895
|
647 |
if (freedst && dst) {
|
slouken@1895
|
648 |
SDL_RWclose(dst);
|
slouken@1895
|
649 |
}
|
slouken@1895
|
650 |
return ((SDL_strcmp(SDL_GetError(), "") == 0) ? 0 : -1);
|
slouken@0
|
651 |
}
|
slouken@1895
|
652 |
|
slouken@1895
|
653 |
/* vi: set ts=4 sw=4 expandtab: */
|