/* * 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 #include 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(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(sizeof(bitmap_file_header))) return false; std::unique_ptr buffer(new char[static_cast(size)]); ifs.read(buffer.get(), size); if(size == ifs.gcount()) { bitmap_file_header * header = reinterpret_cast(buffer.get()); if((header->bfType == 0x4D42) && (static_cast(header->bfSize) == size)) { unsigned char* bits = reinterpret_cast(buffer.get() + header->bfOffBits); bitmap_info * info = reinterpret_cast(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