1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "ultima/nuvie/core/nuvie_defs.h"
24 #include "ultima/nuvie/files/nuvie_bmp_file.h"
25
26 namespace Ultima {
27 namespace Nuvie {
28
29 #define NUVIEBMPFILE_MAGIC 0x4d42 // 'BM'
30
NuvieBmpFile()31 NuvieBmpFile::NuvieBmpFile() {
32 data = NULL;
33 prev_width = 0;
34 prev_height = 0;
35 prev_bits = 0;
36 bmp_line_width = 0;
37 memset(&header, 0, sizeof(header));
38 memset(&infoHeader, 0, sizeof(infoHeader));
39 }
40
~NuvieBmpFile()41 NuvieBmpFile::~NuvieBmpFile() {
42 if (data != NULL)
43 free(data);
44 }
45
initNewBlankImage(sint32 width,sint32 height,const unsigned char * pal)46 bool NuvieBmpFile::initNewBlankImage(sint32 width, sint32 height, const unsigned char *pal) {
47 infoHeader.size = 40;
48 infoHeader.width = width;
49 infoHeader.height = height;
50 infoHeader.planes = 1;
51 infoHeader.bits = 8;
52 infoHeader.compression = 0;
53 infoHeader.imagesize = 0;
54 infoHeader.xresolution = 0; //FIXME
55 infoHeader.yresolution = 0; //FIXME
56 infoHeader.ncolours = 256;
57 infoHeader.importantcolours = 256;
58
59 bmp_line_width = infoHeader.width;
60 if (bmp_line_width % 4 != 0) {
61 bmp_line_width += (4 - (bmp_line_width % 4));
62 }
63
64 header.type = NUVIEBMPFILE_MAGIC;
65 header.reserved1 = 0;
66 header.reserved2 = 0;
67 header.offset = NUVIEBMP_HEADER_SIZE + NUVIEBMP_INFOHEADER_SIZE + 256 * 4;
68 header.size = header.offset + bmp_line_width * infoHeader.height;
69
70 memcpy(&palette, pal, sizeof(palette));
71
72 data = (unsigned char *)malloc(infoHeader.width * infoHeader.height);
73 if (!data) {
74 return handleError("Allocating pixel data");
75 }
76
77 memset(data, 0, infoHeader.width * infoHeader.height);
78
79 return true;
80 }
81
load(Std::string filename)82 bool NuvieBmpFile::load(Std::string filename) {
83 NuvieIOFileRead file;
84
85 if (filename.length() == 0)
86 return handleError("zero byte file");
87
88 if (!file.open(filename)) {
89 return handleError("opening file");
90 }
91
92 if (file.get_size() < 0x36) { //invalid header.
93 return handleError("filesize < 0x36");
94 }
95
96 header.type = file.read2();
97 header.size = file.read4();
98 header.reserved1 = file.read2();
99 header.reserved2 = file.read2();
100 header.offset = file.read4();
101
102 infoHeader.size = file.read4();
103 infoHeader.width = file.read4();
104 infoHeader.height = file.read4();
105 infoHeader.planes = file.read2();
106 infoHeader.bits = file.read2();
107 infoHeader.compression = file.read4();
108 infoHeader.imagesize = file.read4();
109 infoHeader.xresolution = file.read4();
110 infoHeader.yresolution = file.read4();
111 infoHeader.ncolours = file.read4();
112 infoHeader.importantcolours = file.read4();
113
114 if (header.type != NUVIEBMPFILE_MAGIC) { //invalid magic.
115 return handleError("invalid BMP magic.");
116 }
117
118 if (infoHeader.bits != 8 && infoHeader.bits != 24) {
119 return handleError("only 256 colour bitmaps supported.");
120 }
121
122 if (infoHeader.compression != 0) { // && infoHeader.compression != 2)
123 return handleError("only uncompressed BMP files are supported");
124 //return handleError("only raw and bi_rle8 compression formats are supported.");
125
126 //FIXME need to handle rle compression.
127 }
128
129 if (infoHeader.bits == 8) {
130 for (uint32 i = 0; i < infoHeader.ncolours; i++) {
131 uint8 b = file.read1();
132 uint8 g = file.read1();
133 uint8 r = file.read1();
134 file.read1(); // 0
135 palette[i] = (uint32)r | (uint32)(g << 8) | (uint32)(b << 16);
136 }
137 }
138
139 file.seekStart();
140 file.seek(header.offset);
141
142 uint16 bytes_per_pixel = infoHeader.bits / 8;
143 bmp_line_width = infoHeader.width * bytes_per_pixel;
144 if (bmp_line_width % 4 != 0) {
145 bmp_line_width += (4 - (bmp_line_width % 4));
146 }
147
148 if (data == NULL || infoHeader.width != prev_width || infoHeader.height != prev_height || prev_bits != infoHeader.bits) {
149 if (data) {
150 free(data);
151 }
152 data = (unsigned char *)malloc(infoHeader.width * infoHeader.height * bytes_per_pixel);
153 prev_width = infoHeader.width;
154 prev_height = infoHeader.height;
155 prev_bits = infoHeader.bits;
156 if (data == NULL) {
157 return handleError("allocating memory for image");
158 }
159 }
160
161 uint32 end = header.offset + bmp_line_width * infoHeader.height;
162
163 uint32 data_width = infoHeader.width * bytes_per_pixel;
164
165 for (sint32 i = 0; i < infoHeader.height; i++) {
166 file.seek(end - bmp_line_width - (bmp_line_width * i));
167 file.readToBuf(&data[i * data_width], data_width);
168 }
169
170 return true;
171 }
172
save(Std::string filename)173 bool NuvieBmpFile::save(Std::string filename) {
174 NuvieIOFileWrite file;
175
176 if (!file.open(filename)) {
177 return handleError("Opening " + filename + ".");
178 }
179
180 file.write2(header.type);
181 file.write4(header.size);
182 file.write2(header.reserved1);
183 file.write2(header.reserved2);
184 file.write4(header.offset);
185
186 file.write4(infoHeader.size);
187 file.write4(infoHeader.width);
188 file.write4(infoHeader.height);
189 file.write2(infoHeader.planes);
190 file.write2(infoHeader.bits);
191 file.write4(infoHeader.compression);
192 file.write4(infoHeader.imagesize);
193 file.write4(infoHeader.xresolution);
194 file.write4(infoHeader.yresolution);
195 file.write4(infoHeader.ncolours);
196 file.write4(infoHeader.importantcolours);
197
198 if (infoHeader.bits == 8) {
199 for (uint32 i = 0; i < infoHeader.ncolours; i++) {
200 file.write1((palette[i] >> 16) & 0xff); //B
201 file.write1((palette[i] >> 8) & 0xff); //G
202 file.write1(palette[i] & 0xff); //R
203 file.write1((palette[i] >> 24) & 0xff); //A
204 }
205 write8BitData(&file);
206 } else {
207 //FIXME write out 24bit data here.
208 }
209
210 file.close();
211 return true;
212 }
213
write8BitData(NuvieIOFileWrite * file)214 void NuvieBmpFile::write8BitData(NuvieIOFileWrite *file) {
215 uint32 i;
216 for (i = infoHeader.height; i > 0; i--) {
217 file->writeBuf(&data[(i - 1)*infoHeader.width], infoHeader.width);
218 if ((sint32)bmp_line_width > infoHeader.width) {
219 //write out padding bytes.
220 for (uint8 j = 0; j < bmp_line_width - infoHeader.width; j++) {
221 file->write1(0);
222 }
223 }
224 }
225 }
226
handleError(Std::string error)227 bool NuvieBmpFile::handleError(Std::string error) {
228 if (data) {
229 free(data);
230 data = NULL;
231 }
232
233 DEBUG(0, LEVEL_ERROR, error.c_str());
234
235 return false;
236 }
237
getTile()238 Tile *NuvieBmpFile::getTile() {
239 if (infoHeader.width != 16 || infoHeader.height != 16 || infoHeader.bits != 8) {
240 return NULL;
241 }
242
243 Tile *t = (Tile *)malloc(sizeof(Tile));
244 if (t == NULL) {
245 return NULL;
246 }
247 memset(t, 0, sizeof(Tile));
248 memcpy(t->data, data, 256);
249
250 return t;
251 }
252
getRawIndexedData()253 unsigned char *NuvieBmpFile::getRawIndexedData() {
254 if (infoHeader.bits != 8) {
255 return NULL;
256 }
257
258 return data;
259 }
260
getRawIndexedDataCopy()261 unsigned char *NuvieBmpFile::getRawIndexedDataCopy() {
262 if (data == NULL || infoHeader.bits != 8) {
263 return NULL;
264 }
265
266 unsigned char *copy = (unsigned char *)malloc(infoHeader.width * infoHeader.height);
267 if (copy == NULL) {
268 return NULL;
269 }
270 memcpy(copy, data, infoHeader.width * infoHeader.height);
271 return copy;
272 }
273
getSdlSurface32(Std::string filename)274 Graphics::ManagedSurface *NuvieBmpFile::getSdlSurface32(Std::string filename) {
275 load(filename);
276 return getSdlSurface32();
277 }
278
getSdlSurface32()279 Graphics::ManagedSurface *NuvieBmpFile::getSdlSurface32() {
280 if (data == NULL) {
281 return NULL;
282 }
283
284 Graphics::ManagedSurface *surface = new Graphics::ManagedSurface(
285 infoHeader.width, infoHeader.height,
286 Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)
287 );
288
289 unsigned char *src_buf = data;
290 Graphics::Surface s = surface->getSubArea(Common::Rect(0, 0, surface->w, surface->h));
291 uint32 *pixels = (uint32 *)s.getPixels();
292
293 if (infoHeader.bits == 8) {
294 for (sint32 i = 0; i < infoHeader.height; i++) {
295 for (sint32 j = 0; j < infoHeader.width; j++) {
296 pixels[j] = palette[src_buf[j]];
297 }
298 src_buf += infoHeader.width;
299 pixels += infoHeader.width;
300 }
301 } else { //bits == 24
302 for (sint32 i = 0; i < infoHeader.height; i++) {
303 for (sint32 j = 0; j < infoHeader.width; j++) {
304 pixels[j] = (uint32)src_buf[j * 3 + 2] | (uint32)(src_buf[j * 3 + 1] << 8) | (uint32)(src_buf[j * 3 + 0] << 16);
305 }
306 src_buf += infoHeader.width * 3;
307 pixels += infoHeader.width;
308 }
309 }
310 return surface;
311 }
312
313 } // End of namespace Nuvie
314 } // End of namespace Ultima
315