local variables ALSO work better when declared
[divverent/netradiant.git] / plugins / image / bmp.cpp
1 /*
2 Copyright (C) 2001-2006, William Joseph.
3 All Rights Reserved.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant 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 GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #include "bmp.h"
23
24 #include "ifilesystem.h"
25
26 typedef unsigned char byte;
27
28 #include "imagelib.h"
29 #include "bytestreamutils.h"
30
31
32 typedef unsigned char PaletteEntry[4];
33 typedef struct
34 {
35   char id[2];
36   unsigned long fileSize;
37   unsigned long reserved0;
38   unsigned long bitmapDataOffset;
39   unsigned long bitmapHeaderSize;
40   unsigned long width;
41   unsigned long height;
42   unsigned short planes;
43   unsigned short bitsPerPixel;
44   unsigned long compression;
45   unsigned long bitmapDataSize;
46   unsigned long hRes;
47   unsigned long vRes;
48   unsigned long colors;
49   unsigned long importantColors;
50   PaletteEntry palette[256];
51 } BMPHeader_t;
52
53 class ReadPixel8
54 {
55   PaletteEntry* m_palette;
56 public:
57   ReadPixel8(PaletteEntry* palette) : m_palette(palette)
58   {
59   }
60   void operator()(PointerInputStream& inputStream, byte*& pixbuf) const
61   {
62     byte palIndex;
63     inputStream.read(&palIndex, 1);
64     *pixbuf++ = m_palette[palIndex][2];
65     *pixbuf++ = m_palette[palIndex][1];
66     *pixbuf++ = m_palette[palIndex][0];
67     *pixbuf++ = 0xff;
68   }
69 };
70
71 class ReadPixel16
72 {
73 public:
74   void operator()(PointerInputStream& inputStream, byte*& pixbuf) const
75   {
76     unsigned short shortPixel;
77     inputStream.read(reinterpret_cast<byte*>(&shortPixel), sizeof(unsigned short)); //!\todo Is this endian safe?
78     *pixbuf++ = static_cast<byte>(shortPixel & (31 << 10)) >> 7;
79     *pixbuf++ = static_cast<byte>(shortPixel & (31 << 5)) >> 2;
80     *pixbuf++ = static_cast<byte>(shortPixel & (31)) << 3;
81     *pixbuf++ = 0xff;
82   }
83 };
84
85 class ReadPixel24
86 {
87 public:
88   void operator()(PointerInputStream& inputStream, byte*& pixbuf) const
89   {
90     byte bgr[3];
91     inputStream.read(bgr, 3);
92     *pixbuf++ = bgr[2];
93     *pixbuf++ = bgr[1];
94     *pixbuf++ = bgr[0];
95     *pixbuf++ = 255;
96   }
97 };
98
99 class ReadPixel32
100 {
101 public:
102   void operator()(PointerInputStream& inputStream, byte*& pixbuf) const
103   {
104     byte bgra[4];
105     inputStream.read(bgra, 4);
106     *pixbuf++ = bgra[2];
107     *pixbuf++ = bgra[1];
108     *pixbuf++ = bgra[0];
109     *pixbuf++ = bgra[3];
110   }
111 };
112
113 template<typename ReadPixel>
114 void ReadBMP(PointerInputStream& inputStream, byte* bmpRGBA, int rows, int columns, ReadPixel readPixel)
115 {
116   for (int row = rows - 1; row >= 0; row--)
117   {
118     byte* pixbuf = bmpRGBA + row * columns * 4;
119
120     for (int column = 0; column < columns; column++)
121     {
122       readPixel(inputStream, pixbuf);
123     }
124   }
125 }
126
127 Image* LoadBMPBuff(PointerInputStream& inputStream, std::size_t length)
128 {
129   BMPHeader_t bmpHeader;
130   inputStream.read(reinterpret_cast<byte*>(bmpHeader.id), 2);
131   bmpHeader.fileSize = istream_read_uint32_le(inputStream);
132   bmpHeader.reserved0 = istream_read_uint32_le(inputStream);
133   bmpHeader.bitmapDataOffset = istream_read_uint32_le(inputStream);
134   bmpHeader.bitmapHeaderSize = istream_read_uint32_le(inputStream);
135   bmpHeader.width = istream_read_uint32_le(inputStream);
136   bmpHeader.height = istream_read_uint32_le(inputStream);
137   bmpHeader.planes = istream_read_uint16_le(inputStream);
138   bmpHeader.bitsPerPixel = istream_read_uint16_le(inputStream);
139   bmpHeader.compression = istream_read_uint32_le(inputStream);
140   bmpHeader.bitmapDataSize = istream_read_uint32_le(inputStream);
141   bmpHeader.hRes = istream_read_uint32_le(inputStream);
142   bmpHeader.vRes = istream_read_uint32_le(inputStream);
143   bmpHeader.colors = istream_read_uint32_le(inputStream);
144   bmpHeader.importantColors = istream_read_uint32_le(inputStream);
145
146   if (bmpHeader.bitsPerPixel == 8)
147   {
148     int paletteSize = bmpHeader.colors * 4;
149     inputStream.read(reinterpret_cast<byte*>(bmpHeader.palette), paletteSize);
150   }
151
152   if (bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M')
153   {
154     globalErrorStream() << "LoadBMP: only Windows-style BMP files supported\n";
155     return 0;
156   }
157   if (bmpHeader.fileSize != length)
158   {
159     globalErrorStream() << "LoadBMP: header size does not match file size (" << Unsigned(bmpHeader.fileSize) << " vs. " << Unsigned(length) << ")\n";
160     return 0;
161   }
162   if (bmpHeader.compression != 0)
163   {
164     globalErrorStream() << "LoadBMP: only uncompressed BMP files supported\n";
165     return 0;
166   }
167   if (bmpHeader.bitsPerPixel < 8)
168   {
169     globalErrorStream() << "LoadBMP: monochrome and 4-bit BMP files not supported\n";
170     return 0;
171   }
172
173   int columns = bmpHeader.width;
174   int rows = bmpHeader.height;
175   if (rows < 0)
176     rows = -rows;
177
178   RGBAImage* image = new RGBAImage(columns, rows);
179
180   switch(bmpHeader.bitsPerPixel)
181   {
182   case 8:
183     ReadBMP(inputStream, image->getRGBAPixels(), rows, columns, ReadPixel8(bmpHeader.palette));
184     break;
185   case 16:
186     ReadBMP(inputStream, image->getRGBAPixels(), rows, columns, ReadPixel16());
187     break;
188   case 24:
189     ReadBMP(inputStream, image->getRGBAPixels(), rows, columns, ReadPixel24());
190     break;
191   case 32:
192     ReadBMP(inputStream, image->getRGBAPixels(), rows, columns, ReadPixel32());
193     break;
194   default:
195     globalErrorStream() << "LoadBMP: illegal pixel_size '" << bmpHeader.bitsPerPixel << "'\n";
196     image->release();
197     return 0;
198   }
199   return image;
200 }
201
202 Image* LoadBMP(ArchiveFile& file)
203 {
204   ScopedArchiveBuffer buffer(file);
205   PointerInputStream inputStream(buffer.buffer);
206   return LoadBMPBuff(inputStream, buffer.length);
207 }