378 lines
10 KiB
C++
378 lines
10 KiB
C++
/*
|
|
* Bitmap Format Graphics Implementation
|
|
* Nana C++ Library(http://www.nanapro.org)
|
|
* Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com)
|
|
*
|
|
* Distributed under the Boost Software License, Version 1.0.
|
|
* (See accompanying file LICENSE_1_0.txt or copy at
|
|
* http://www.boost.org/LICENSE_1_0.txt)
|
|
*
|
|
* @file: nana/paint/detail/image_bmp.hpp
|
|
* @contributors: Ryan Gonzalez
|
|
*/
|
|
#ifndef NANA_PAINT_DETAIL_IMAGE_BMP_HPP
|
|
#define NANA_PAINT_DETAIL_IMAGE_BMP_HPP
|
|
|
|
#include <nana/paint/detail/image_impl_interface.hpp>
|
|
#include <memory>
|
|
|
|
namespace nana{ namespace paint
|
|
{
|
|
namespace detail
|
|
{
|
|
|
|
#ifndef NANA_WINDOWS
|
|
struct bitmap_file_header
|
|
{
|
|
unsigned short bfType;
|
|
unsigned bfSize;
|
|
unsigned short bfReserved1;
|
|
unsigned short bfReserved2;
|
|
unsigned bfOffBits;
|
|
} __attribute__((packed));
|
|
|
|
struct bitmap_info_header {
|
|
unsigned biSize;
|
|
int biWidth;
|
|
int biHeight;
|
|
unsigned short biPlanes;
|
|
unsigned short biBitCount;
|
|
unsigned biCompression;
|
|
unsigned biSizeImage;
|
|
int biXPelsPerMeter;
|
|
int biYPelsPerMeter;
|
|
unsigned biClrUsed;
|
|
unsigned biClrImportant;
|
|
}__attribute__((packed));
|
|
|
|
struct rgb_quad
|
|
{
|
|
unsigned char rgbBlue;
|
|
unsigned char rgbGreen;
|
|
unsigned char rgbRed;
|
|
unsigned char rgbReserved;
|
|
};
|
|
|
|
struct bitmap_info
|
|
{
|
|
bitmap_info_header bmiHeader;
|
|
rgb_quad bmiColors[1];
|
|
}__attribute__((packed));
|
|
#else
|
|
typedef BITMAPFILEHEADER bitmap_file_header;
|
|
typedef BITMAPINFO bitmap_info;
|
|
typedef RGBQUAD rgb_quad;
|
|
#endif
|
|
|
|
class image_bmp
|
|
:public image::image_impl_interface
|
|
{
|
|
public:
|
|
image_bmp(){}
|
|
|
|
~image_bmp()
|
|
{
|
|
this->close();
|
|
}
|
|
|
|
bool open(const void* data, std::size_t bytes) override
|
|
{
|
|
// TODO: read a BMP file from memory
|
|
return false;
|
|
}
|
|
|
|
bool open(const nana::char_t* filename) override
|
|
{
|
|
if(nullptr == filename) return false;
|
|
std::ifstream ifs;
|
|
#if defined(NANA_UNICODE)
|
|
ifs.open(static_cast<std::string>(nana::charset(filename)).c_str(), std::ios::binary);
|
|
#else
|
|
ifs.open(filename, std::ios::binary);
|
|
#endif
|
|
if(ifs)
|
|
{
|
|
ifs.seekg(0, std::ios::end);
|
|
auto size = ifs.tellg();
|
|
ifs.seekg(0, std::ios::beg);
|
|
|
|
if(size <= static_cast<int>(sizeof(bitmap_file_header)))
|
|
return false;
|
|
|
|
std::unique_ptr<char[]> buffer(new char[static_cast<int>(size)]);
|
|
|
|
ifs.read(buffer.get(), size);
|
|
if(size == ifs.gcount())
|
|
{
|
|
bitmap_file_header * header = reinterpret_cast<bitmap_file_header*>(buffer.get());
|
|
if((header->bfType == 0x4D42) && (static_cast<std::streamsize>(header->bfSize) == size))
|
|
{
|
|
unsigned char* bits = reinterpret_cast<unsigned char*>(buffer.get() + header->bfOffBits);
|
|
bitmap_info * info = reinterpret_cast<bitmap_info *>(header + 1);
|
|
|
|
//Bitmap file is 4byte-aligned for each line.
|
|
std::size_t bytes_per_line;
|
|
const std::size_t height_pixels = std::abs(info->bmiHeader.biHeight);
|
|
if(0 == info->bmiHeader.biSizeImage)
|
|
bytes_per_line = (((info->bmiHeader.biWidth * info->bmiHeader.biBitCount + 31) & ~31) >> 3);
|
|
else
|
|
bytes_per_line = info->bmiHeader.biSizeImage / height_pixels;
|
|
|
|
pixbuf_.open(info->bmiHeader.biWidth, height_pixels);
|
|
|
|
auto d = pixbuf_.raw_ptr(0);
|
|
|
|
if(16 <= info->bmiHeader.biBitCount)
|
|
{
|
|
pixbuf_.put(bits, info->bmiHeader.biWidth, height_pixels, info->bmiHeader.biBitCount, bytes_per_line, (info->bmiHeader.biHeight < 0));
|
|
}
|
|
else if(8 == info->bmiHeader.biBitCount)
|
|
{
|
|
const auto lend = d + info->bmiHeader.biWidth * height_pixels;
|
|
|
|
if(info->bmiHeader.biHeight < 0)
|
|
{
|
|
auto s = bits;
|
|
while(d < lend)
|
|
{
|
|
auto d_p = d;
|
|
auto dpend = d_p + info->bmiHeader.biWidth;
|
|
auto s_p = s;
|
|
while(d_p != dpend)
|
|
{
|
|
rgb_quad & rgb = info->bmiColors[*s_p++];
|
|
d_p->element.red = rgb.rgbRed;
|
|
d_p->element.green = rgb.rgbGreen;
|
|
d_p->element.blue = rgb.rgbBlue;
|
|
d_p->element.alpha_channel = rgb.rgbReserved;
|
|
++d_p;
|
|
}
|
|
d = dpend;
|
|
s += bytes_per_line;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const auto* s = bits + bytes_per_line * (height_pixels - 1);
|
|
while(d < lend)
|
|
{
|
|
auto d_p = d;
|
|
auto* const dpend = d_p + info->bmiHeader.biWidth;
|
|
const auto * s_p = s;
|
|
while(d_p != dpend)
|
|
{
|
|
rgb_quad & rgb = info->bmiColors[*s_p++];
|
|
d_p->element.red = rgb.rgbRed;
|
|
d_p->element.green = rgb.rgbGreen;
|
|
d_p->element.blue = rgb.rgbBlue;
|
|
d_p->element.alpha_channel = rgb.rgbReserved;
|
|
++d_p;
|
|
}
|
|
d = dpend;
|
|
s -= bytes_per_line;
|
|
}
|
|
}
|
|
}
|
|
else if(4 == info->bmiHeader.biBitCount)
|
|
{
|
|
const auto * const lend = d + info->bmiHeader.biWidth * height_pixels;
|
|
if(info->bmiHeader.biHeight < 0)
|
|
{
|
|
const unsigned char* s = bits;
|
|
while(d < lend)
|
|
{
|
|
auto d_p = d;
|
|
auto * const dpend = d_p + info->bmiHeader.biWidth;
|
|
unsigned index = 0;
|
|
while(d_p != dpend)
|
|
{
|
|
rgb_quad & rgb = info->bmiColors[(index & 1) ? (s[index >> 1] & 0xF) : (s[index >> 1] & 0xF0) >> 4];
|
|
|
|
d_p->element.red = rgb.rgbRed;
|
|
d_p->element.green = rgb.rgbGreen;
|
|
d_p->element.blue = rgb.rgbBlue;
|
|
d_p->element.alpha_channel = rgb.rgbReserved;
|
|
++d_p;
|
|
++index;
|
|
}
|
|
d = dpend;
|
|
s += bytes_per_line;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const auto* s = bits + bytes_per_line * (height_pixels - 1);
|
|
while(d < lend)
|
|
{
|
|
auto d_p = d;
|
|
auto * const dpend = d_p + info->bmiHeader.biWidth;
|
|
|
|
unsigned index = 0;
|
|
while(d_p != dpend)
|
|
{
|
|
rgb_quad & rgb = info->bmiColors[(index & 1) ? (s[index >> 1] & 0xF) : (s[index >> 1] & 0xF0) >> 4];
|
|
|
|
d_p->element.red = rgb.rgbRed;
|
|
d_p->element.green = rgb.rgbGreen;
|
|
d_p->element.blue = rgb.rgbBlue;
|
|
d_p->element.alpha_channel = rgb.rgbReserved;
|
|
++d_p;
|
|
++index;
|
|
}
|
|
d = dpend;
|
|
s -= bytes_per_line;
|
|
}
|
|
}
|
|
}
|
|
else if(2 == info->bmiHeader.biBitCount)
|
|
{
|
|
const auto * const lend = d + info->bmiHeader.biWidth * height_pixels;
|
|
if(info->bmiHeader.biHeight < 0)
|
|
{
|
|
const unsigned char* s = bits;
|
|
while(d < lend)
|
|
{
|
|
auto d_p = d;
|
|
auto * const dpend = d_p + info->bmiHeader.biWidth;
|
|
unsigned index = 0;
|
|
while(d_p != dpend)
|
|
{
|
|
unsigned shift = (3 - (index & 0x3)) << 1; // (index % 4) * 2
|
|
rgb_quad& rgb = info->bmiColors[(s[index >> 2] & (0x3 << shift))>>shift];
|
|
|
|
d_p->element.red = rgb.rgbRed;
|
|
d_p->element.green = rgb.rgbGreen;
|
|
d_p->element.blue = rgb.rgbBlue;
|
|
d_p->element.alpha_channel = rgb.rgbReserved;
|
|
++d_p;
|
|
++index;
|
|
}
|
|
d = dpend;
|
|
s += bytes_per_line;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const auto* s = bits + bytes_per_line * (height_pixels - 1);
|
|
while(d < lend)
|
|
{
|
|
auto d_p = d;
|
|
auto * const dpend = d_p + info->bmiHeader.biWidth;
|
|
|
|
unsigned index = 0;
|
|
while(d_p != dpend)
|
|
{
|
|
unsigned shift = (3 - (index & 0x3)) << 1; // (index % 4) * 2
|
|
rgb_quad& rgb = info->bmiColors[(s[index >> 2] & (0x3 << shift))>>shift];
|
|
|
|
d_p->element.red = rgb.rgbRed;
|
|
d_p->element.green = rgb.rgbGreen;
|
|
d_p->element.blue = rgb.rgbBlue;
|
|
d_p->element.alpha_channel = rgb.rgbReserved;
|
|
++d_p;
|
|
++index;
|
|
}
|
|
d = dpend;
|
|
s -= bytes_per_line;
|
|
}
|
|
}
|
|
}
|
|
else if(1 == info->bmiHeader.biBitCount)
|
|
{
|
|
const auto * const lend = d + info->bmiHeader.biWidth * height_pixels;
|
|
if(info->bmiHeader.biHeight < 0)
|
|
{
|
|
const auto* s = bits;
|
|
while(d < lend)
|
|
{
|
|
auto d_p = d;
|
|
auto * const dpend = d_p + info->bmiHeader.biWidth;
|
|
unsigned index = 0;
|
|
while(d_p != dpend)
|
|
{
|
|
unsigned bi = (7 - (index & 7)); //(index % 8)
|
|
rgb_quad & rgb = info->bmiColors[(s[index >> 3] & (1 << bi)) >> bi];
|
|
|
|
d_p->element.red = rgb.rgbRed;
|
|
d_p->element.green = rgb.rgbGreen;
|
|
d_p->element.blue = rgb.rgbBlue;
|
|
d_p->element.alpha_channel = rgb.rgbReserved;
|
|
++d_p;
|
|
++index;
|
|
}
|
|
d = dpend;
|
|
s += bytes_per_line;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const auto* s = bits + bytes_per_line * (height_pixels - 1);
|
|
while(d < lend)
|
|
{
|
|
auto d_p = d;
|
|
auto * const dpend = d_p + info->bmiHeader.biWidth;
|
|
|
|
unsigned index = 0;
|
|
while(d_p != dpend)
|
|
{
|
|
unsigned bi = (7 - (index & 7));
|
|
rgb_quad & rgb = info->bmiColors[(s[index >> 3] & (1 << bi)) >> bi];
|
|
|
|
d_p->element.red = rgb.rgbRed;
|
|
d_p->element.green = rgb.rgbGreen;
|
|
d_p->element.blue = rgb.rgbBlue;
|
|
d_p->element.alpha_channel = rgb.rgbReserved;
|
|
++d_p;
|
|
++index;
|
|
}
|
|
d = dpend;
|
|
s -= bytes_per_line;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (false == pixbuf_.empty());
|
|
}
|
|
|
|
bool alpha_channel() const override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool empty() const override
|
|
{
|
|
return pixbuf_.empty();
|
|
}
|
|
|
|
void close() override
|
|
{
|
|
pixbuf_.close();
|
|
}
|
|
|
|
nana::size size() const override
|
|
{
|
|
return pixbuf_.size();
|
|
}
|
|
|
|
void paste(const nana::rectangle& src_r, graph_reference graph, const point& p_dst) const override
|
|
{
|
|
if(graph && pixbuf_)
|
|
pixbuf_.paste(src_r, graph.handle(), p_dst);
|
|
}
|
|
|
|
void stretch(const nana::rectangle& src_r, graph_reference graph, const nana::rectangle& r) const override
|
|
{
|
|
if(graph && pixbuf_)
|
|
pixbuf_.stretch(src_r, graph.handle(), r);
|
|
}
|
|
private:
|
|
nana::paint::pixel_buffer pixbuf_;
|
|
};//end class bmpfile
|
|
}//end namespace detail
|
|
}//end namespace paint
|
|
}//end namespace nana
|
|
|
|
#endif
|