]> git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/image/bmp.cpp
reformat code! now the code is only ugly on the *inside*
[xonotic/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     char id[2];
35     unsigned long fileSize;
36     unsigned long reserved0;
37     unsigned long bitmapDataOffset;
38     unsigned long bitmapHeaderSize;
39     unsigned long width;
40     unsigned long height;
41     unsigned short planes;
42     unsigned short bitsPerPixel;
43     unsigned long compression;
44     unsigned long bitmapDataSize;
45     unsigned long hRes;
46     unsigned long vRes;
47     unsigned long colors;
48     unsigned long importantColors;
49     PaletteEntry palette[256];
50 } BMPHeader_t;
51
52 class ReadPixel8 {
53     PaletteEntry *m_palette;
54 public:
55     ReadPixel8(PaletteEntry *palette) : m_palette(palette)
56     {
57     }
58
59     void operator()(PointerInputStream &inputStream, byte *&pixbuf) const
60     {
61         byte palIndex;
62         inputStream.read(&palIndex, 1);
63         *pixbuf++ = m_palette[palIndex][2];
64         *pixbuf++ = m_palette[palIndex][1];
65         *pixbuf++ = m_palette[palIndex][0];
66         *pixbuf++ = 0xff;
67     }
68 };
69
70 class ReadPixel16 {
71 public:
72     void operator()(PointerInputStream &inputStream, byte *&pixbuf) const
73     {
74         unsigned short shortPixel;
75         inputStream.read(reinterpret_cast<byte *>( &shortPixel ), sizeof(unsigned short)); //!\todo Is this endian safe?
76         *pixbuf++ = static_cast<byte>( shortPixel & (31 << 10)) >> 7;
77         *pixbuf++ = static_cast<byte>( shortPixel & (31 << 5)) >> 2;
78         *pixbuf++ = static_cast<byte>( shortPixel & (31)) << 3;
79         *pixbuf++ = 0xff;
80     }
81 };
82
83 class ReadPixel24 {
84 public:
85     void operator()(PointerInputStream &inputStream, byte *&pixbuf) const
86     {
87         byte bgr[3];
88         inputStream.read(bgr, 3);
89         *pixbuf++ = bgr[2];
90         *pixbuf++ = bgr[1];
91         *pixbuf++ = bgr[0];
92         *pixbuf++ = 255;
93     }
94 };
95
96 class ReadPixel32 {
97 public:
98     void operator()(PointerInputStream &inputStream, byte *&pixbuf) const
99     {
100         byte bgra[4];
101         inputStream.read(bgra, 4);
102         *pixbuf++ = bgra[2];
103         *pixbuf++ = bgra[1];
104         *pixbuf++ = bgra[0];
105         *pixbuf++ = bgra[3];
106     }
107 };
108
109 template<typename ReadPixel>
110 void ReadBMP(PointerInputStream &inputStream, byte *bmpRGBA, int rows, int columns, ReadPixel readPixel)
111 {
112     for (int row = rows - 1; row >= 0; row--) {
113         byte *pixbuf = bmpRGBA + row * columns * 4;
114
115         for (int column = 0; column < columns; column++) {
116             readPixel(inputStream, pixbuf);
117         }
118     }
119 }
120
121 Image *LoadBMPBuff(PointerInputStream &inputStream, std::size_t length)
122 {
123     BMPHeader_t bmpHeader;
124     inputStream.read(reinterpret_cast<byte *>( bmpHeader.id ), 2);
125     bmpHeader.fileSize = istream_read_uint32_le(inputStream);
126     bmpHeader.reserved0 = istream_read_uint32_le(inputStream);
127     bmpHeader.bitmapDataOffset = istream_read_uint32_le(inputStream);
128     bmpHeader.bitmapHeaderSize = istream_read_uint32_le(inputStream);
129     bmpHeader.width = istream_read_uint32_le(inputStream);
130     bmpHeader.height = istream_read_uint32_le(inputStream);
131     bmpHeader.planes = istream_read_uint16_le(inputStream);
132     bmpHeader.bitsPerPixel = istream_read_uint16_le(inputStream);
133     bmpHeader.compression = istream_read_uint32_le(inputStream);
134     bmpHeader.bitmapDataSize = istream_read_uint32_le(inputStream);
135     bmpHeader.hRes = istream_read_uint32_le(inputStream);
136     bmpHeader.vRes = istream_read_uint32_le(inputStream);
137     bmpHeader.colors = istream_read_uint32_le(inputStream);
138     bmpHeader.importantColors = istream_read_uint32_le(inputStream);
139
140     if (bmpHeader.bitsPerPixel == 8) {
141         int paletteSize = bmpHeader.colors * 4;
142         inputStream.read(reinterpret_cast<byte *>( bmpHeader.palette ), paletteSize);
143     }
144
145     if (bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M') {
146         globalErrorStream() << "LoadBMP: only Windows-style BMP files supported\n";
147         return 0;
148     }
149     if (bmpHeader.fileSize != length) {
150         globalErrorStream() << "LoadBMP: header size does not match file size (" << Unsigned(bmpHeader.fileSize)
151                             << " vs. " << Unsigned(length) << ")\n";
152         return 0;
153     }
154     if (bmpHeader.compression != 0) {
155         globalErrorStream() << "LoadBMP: only uncompressed BMP files supported\n";
156         return 0;
157     }
158     if (bmpHeader.bitsPerPixel < 8) {
159         globalErrorStream() << "LoadBMP: monochrome and 4-bit BMP files not supported\n";
160         return 0;
161     }
162
163     int columns = bmpHeader.width;
164     int rows = bmpHeader.height;
165     if (rows < 0) {
166         rows = -rows;
167     }
168
169     RGBAImage *image = new RGBAImage(columns, rows);
170
171     switch (bmpHeader.bitsPerPixel) {
172         case 8:
173             ReadBMP(inputStream, image->getRGBAPixels(), rows, columns, ReadPixel8(bmpHeader.palette));
174             break;
175         case 16:
176             ReadBMP(inputStream, image->getRGBAPixels(), rows, columns, ReadPixel16());
177             break;
178         case 24:
179             ReadBMP(inputStream, image->getRGBAPixels(), rows, columns, ReadPixel24());
180             break;
181         case 32:
182             ReadBMP(inputStream, image->getRGBAPixels(), rows, columns, ReadPixel32());
183             break;
184         default:
185             globalErrorStream() << "LoadBMP: illegal pixel_size '" << bmpHeader.bitsPerPixel << "'\n";
186             image->release();
187             return 0;
188     }
189     return image;
190 }
191
192 Image *LoadBMP(ArchiveFile &file)
193 {
194     ScopedArchiveBuffer buffer(file);
195     PointerInputStream inputStream(buffer.buffer);
196     return LoadBMPBuff(inputStream, buffer.length);
197 }