]> git.tdb.fi Git - libs/gui.git/commitdiff
Implement a loader for BMP images
authorMikko Rasa <tdb@tdb.fi>
Mon, 25 Jul 2016 21:51:09 +0000 (00:51 +0300)
committerMikko Rasa <tdb@tdb.fi>
Mon, 25 Jul 2016 21:51:09 +0000 (00:51 +0300)
source/graphics/bmploader.cpp [new file with mode: 0644]
source/graphics/bmploader.h [new file with mode: 0644]
source/graphics/imageloader.cpp

diff --git a/source/graphics/bmploader.cpp b/source/graphics/bmploader.cpp
new file mode 100644 (file)
index 0000000..71de507
--- /dev/null
@@ -0,0 +1,109 @@
+#include <msp/core/inttypes.h>
+#include "bmploader.h"
+
+using namespace std;
+
+namespace {
+
+void read_full(Msp::IO::Base &io, char *buffer, unsigned size)
+{
+       unsigned len = io.read(buffer, size);
+       if(len<size)
+               throw Msp::Graphics::bad_image_data("premature end of file");
+}
+
+template<typename T>
+T decode(const char *data)
+{
+       const Msp::UInt8 *udata = reinterpret_cast<const Msp::UInt8 *>(data);
+       T result = 0;
+       for(unsigned i=0; i<sizeof(T); ++i)
+               result |= udata[i]<<(i*8);
+       return result;
+}
+
+}
+
+namespace Msp {
+namespace Graphics {
+
+BmpLoader::BmpLoader(IO::Base &i, unsigned sb):
+       io(i),
+       sig_bytes(sb)
+{
+       // Image data location is stored at offset 10 and can't be skipped
+       if(sig_bytes>10)
+               throw invalid_argument("BmpLoader::BmpLoader");
+}
+
+bool BmpLoader::detect(const std::string &sig)
+{
+       static const char bmp_sig[] = "BM";
+       if(sig.size()<sizeof(bmp_sig))
+               return false;
+       return !sig.compare(0, 2, bmp_sig);
+}
+
+void BmpLoader::load(Image::Data &data)
+{
+       char bm_header[14];
+       read_full(io, bm_header+sig_bytes, sizeof(bm_header)-sig_bytes);
+
+       if(!sig_bytes && (bm_header[0]!='B' || bm_header[1]!='M'))
+               throw bad_image_data("bitmap header mismatch");
+
+       unsigned data_offset = decode<UInt32>(bm_header+10);
+
+       char dib_header[124];
+       read_full(io, dib_header, 12);
+       unsigned dib_length = min<unsigned>(decode<UInt32>(dib_header), sizeof(dib_header));
+       if(dib_length<40)
+               throw bad_image_data("DIB header too short (very old bmp file?)");
+
+       read_full(io, dib_header+12, dib_length-12);
+
+       data.width = decode<UInt32>(dib_header+4);
+       Int32 height = decode<UInt32>(dib_header+8);
+       data.height = abs(height);
+
+       unsigned color_planes = decode<UInt16>(dib_header+12);
+       if(color_planes!=1)
+               throw bad_image_data("color_planes!=1");
+       unsigned bits_per_pixel = decode<UInt16>(dib_header+14);
+       unsigned compression = decode<UInt32>(dib_header+16);
+       if(compression)
+               throw unsupported_image_format("compression not supported");
+
+       data.stride = (data.width*bits_per_pixel+31)/32*4;
+       switch(bits_per_pixel)
+       {
+       case 8:  data.fmt = COLOR_INDEX; break;
+       case 24: data.fmt = BGR; break;
+       case 32: data.fmt = BGRX; break;  // TODO is it BGRX or XBGR?
+       default: throw unsupported_image_format("bits per pixel value not supported");
+       }
+
+       unsigned skip = data_offset-sizeof(bm_header)-dib_length;
+       while(skip>0)
+       {
+               char buffer[1024];
+               unsigned size = min<unsigned>(sizeof(buffer), skip);
+               read_full(io, buffer, size);
+               skip -= size;
+       }
+
+       data.data = new char[data.stride*data.height];
+       if(height<0)
+       {
+               for(unsigned y=0; y<data.height; ++y)
+                       read_full(io, data.data+(data.height-1-y)*data.stride, data.stride);
+       }
+       else
+       {
+               for(unsigned y=0; y<data.height; ++y)
+                       read_full(io, data.data+y*data.stride, data.stride);
+       }
+}
+
+} // namespace Graphics
+} // namespace Msp
diff --git a/source/graphics/bmploader.h b/source/graphics/bmploader.h
new file mode 100644 (file)
index 0000000..db9af1d
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef MSP_GRAPHICS_BMPLOADER_H_
+#define MSP_GRAPHICS_BMPLOADER_H_
+
+#include <msp/graphics/imageloader.h>
+
+namespace Msp {
+namespace Graphics {
+
+class BmpLoader: public RegisteredImageLoader<BmpLoader>
+{
+private:
+       IO::Base &io;
+       unsigned sig_bytes;
+
+public:
+       BmpLoader(IO::Base &, unsigned = 0);
+
+       static unsigned get_signature_size() { return 2; }
+       static bool detect(const std::string &);
+
+       virtual void load(Image::Data &);
+};
+
+} // namespace Graphics
+} // namespace Msp
+
+#endif
index fbba25cd75ae59ce6e194134caea9141e79a2177..965c7b3cb78dbaa76c659efd0ff3be60a9a00787 100644 (file)
@@ -1,6 +1,7 @@
 #include <msp/core/refptr.h>
 #include <msp/io/file.h>
 #include <msp/strings/format.h>
+#include "bmploader.h"
 #include "imageloader.h"
 #ifdef WITH_LIBPNG
 #include "png/pngloader.h"
@@ -48,6 +49,7 @@ ImageLoader *ImageLoader::open_file(const string &fn)
 
 ImageLoader *ImageLoader::open_io(IO::Seekable &io)
 {
+       (void)RegisteredImageLoader<BmpLoader>::reg;
 #ifdef WITH_LIBPNG
        (void)RegisteredImageLoader<PngLoader>::reg;
 #endif