]> git.tdb.fi Git - libs/gui.git/blob - source/graphics/bmploader.cpp
Add some state checking to ImageLoader
[libs/gui.git] / source / graphics / bmploader.cpp
1 #include <msp/core/inttypes.h>
2 #include "bmploader.h"
3
4 using namespace std;
5
6 namespace {
7
8 void read_full(Msp::IO::Base &io, char *buffer, unsigned size)
9 {
10         unsigned len = io.read(buffer, size);
11         if(len<size)
12                 throw Msp::Graphics::bad_image_data("premature end of file");
13 }
14
15 template<typename T>
16 T decode(const char *data)
17 {
18         const Msp::UInt8 *udata = reinterpret_cast<const Msp::UInt8 *>(data);
19         T result = 0;
20         for(unsigned i=0; i<sizeof(T); ++i)
21                 result |= udata[i]<<(i*8);
22         return result;
23 }
24
25 }
26
27 namespace Msp {
28 namespace Graphics {
29
30 BmpLoader::BmpLoader(IO::Base &i, unsigned sb):
31         io(i),
32         sig_bytes(sb)
33 {
34         // Image data location is stored at offset 10 and can't be skipped
35         if(sig_bytes>10)
36                 throw invalid_argument("BmpLoader::BmpLoader");
37 }
38
39 bool BmpLoader::detect(const std::string &sig)
40 {
41         static const char bmp_sig[] = { 'B', 'M' };
42         if(sig.size()<sizeof(bmp_sig))
43                 return false;
44         return !sig.compare(0, sizeof(bmp_sig), bmp_sig, sizeof(bmp_sig));
45 }
46
47 void BmpLoader::load_(Image::Data &data)
48 {
49         char bm_header[14];
50         read_full(io, bm_header+sig_bytes, sizeof(bm_header)-sig_bytes);
51
52         if(!sig_bytes && (bm_header[0]!='B' || bm_header[1]!='M'))
53                 throw bad_image_data("bitmap header mismatch");
54
55         unsigned data_offset = decode<UInt32>(bm_header+10);
56
57         char dib_header[124];
58         read_full(io, dib_header, 12);
59         unsigned dib_length = min<unsigned>(decode<UInt32>(dib_header), sizeof(dib_header));
60         if(dib_length<40)
61                 throw bad_image_data("DIB header too short (very old bmp file?)");
62
63         read_full(io, dib_header+12, dib_length-12);
64
65         data.width = decode<UInt32>(dib_header+4);
66         Int32 height = decode<UInt32>(dib_header+8);
67         data.height = abs(height);
68
69         unsigned color_planes = decode<UInt16>(dib_header+12);
70         if(color_planes!=1)
71                 throw bad_image_data("color_planes!=1");
72         unsigned bits_per_pixel = decode<UInt16>(dib_header+14);
73         unsigned compression = decode<UInt32>(dib_header+16);
74         if(compression)
75                 throw unsupported_image_format("compression not supported");
76
77         data.stride = (data.width*bits_per_pixel+31)/32*4;
78         switch(bits_per_pixel)
79         {
80         case 8:  data.fmt = COLOR_INDEX; break;
81         case 24: data.fmt = BGR; break;
82         case 32: data.fmt = BGRX; break;  // TODO is it BGRX or XBGR?
83         default: throw unsupported_image_format("bits per pixel value not supported");
84         }
85
86         unsigned skip = data_offset-sizeof(bm_header)-dib_length;
87         while(skip>0)
88         {
89                 char buffer[1024];
90                 unsigned size = min<unsigned>(sizeof(buffer), skip);
91                 read_full(io, buffer, size);
92                 skip -= size;
93         }
94
95         data.pixels = new char[data.stride*data.height];
96         if(height<0)
97         {
98                 for(unsigned y=0; y<data.height; ++y)
99                         read_full(io, data.pixels+(data.height-1-y)*data.stride, data.stride);
100         }
101         else
102         {
103                 for(unsigned y=0; y<data.height; ++y)
104                         read_full(io, data.pixels+y*data.stride, data.stride);
105         }
106 }
107
108 } // namespace Graphics
109 } // namespace Msp