#include "alfe/main.h" #ifndef INCLUDED_BITMAP_H #define INCLUDED_BITMAP_H #include "alfe/colour_space.h" #include "alfe/vectors.h" template class Bitmap; template class BitmapFileFormat : public Handle { public: Bitmap load(const File& file) const { return body()->load(file); } void save(Bitmap& bitmap, const File& file) const { return body()->save(bitmap, file); } protected: class Body : public Handle::Body { public: virtual void save(Bitmap& bitmap, const File& file) const = 0; virtual Bitmap load(const File& file) const = 0; }; BitmapFileFormat(const Handle& handle) : Handle(handle) { } Body* body() { return as(); } const Body* body() const { return as(); } private: friend class Bitmap; }; template class RawFileFormatTemplate; typedef RawFileFormatTemplate RawFileFormat; template class RawFileFormatTemplate : public BitmapFileFormat { public: RawFileFormatTemplate(Vector size) : BitmapFileFormat(this->create(size)) { } private: class Body : public BitmapFileFormat::Body { public: Body(Vector size) : _size(size) { } // The bitmap needs to be 8-bit sRGB data for this to work. virtual void save(Bitmap& bitmap, const File& file) const { FileStream stream = file.openWrite(); Byte* data = bitmap.data(); int stride = bitmap.stride(); Vector size = bitmap.size(); for (int y = 0; y < size.y; ++y) { stream.write(static_cast(data), size.x*sizeof(T)); data += stride; } } // This will put 8-bit sRGB data in the bitmap. virtual Bitmap load(const File& file) const { FileStream stream = file.openRead(); Bitmap bitmap(_size); Byte* data = bitmap.data(); int stride = bitmap.stride(); for (int y = 0; y < _size.y; ++y) { stream.read(static_cast(data), _size.x*sizeof(T)); data += stride; } return bitmap; } private: Vector _size; }; }; // A Bitmap is a value class encapsulating a 2D image. Its width, height, // stride and pixel format are immutable but the pixels themselves are not. template class Bitmap : private Array { public: Bitmap() : _size(0, 0) { } Bitmap(Vector size) { _stride = size.x*sizeof(Pixel); _size = size; this->allocate(size.x*size.y); _topLeft = reinterpret_cast(&Array::operator[](0)); } void ensure(Vector s) { if (size().x < s.x || size().y < s.y) *this = Bitmap(Vector(max(size().x, s.x), max(size().y, s.y))); } // Convert from one pixel format to another. template void convert( Bitmap& target, Converter converter) { Byte* row = data(); Byte* targetRow = target.data(); for (int y = 0; y < _size.y; ++y) { Pixel* p = reinterpret_cast(row); TargetPixel* tp = reinterpret_cast(targetRow); for (int x = 0; x < _size.x; ++x) { *tp = converter.convert(*p); ++p; ++tp; } row += _stride; targetRow += target.stride(); } } void load(const BitmapFileFormat& format, const File& file) { *this = format.load(file); } void save(const BitmapFileFormat& format, const File& file) { format.save(*this, file); } Byte* data() { return _topLeft; } const Byte* data() const { return _topLeft; } int stride() const { return _stride; } Vector size() const { return _size; } bool valid() const { return _size.x != 0; } Pixel* row(int y) { return reinterpret_cast(_topLeft + y*_stride); } Pixel& operator[](Vector position) { return row(position.y)[position.x]; } // A sub-bitmap of a bitmap is a pointer into the same set of data, so // drawing on a subBitmap will also draw on the parent bitmap, and any // other overlapping sub-bitmaps. This can be used in conjunction with // fill() to draw rectangles. To avoid this behavior, use // subBitmap().clone(). Bitmap subBitmap(Vector topLeft, Vector size) { return Bitmap(*this, _topLeft + topLeft.x*sizeof(Pixel) + topLeft.y*_stride, size, _stride); } Bitmap clone() const { Bitmap c(_size); copyTo(c); return c; } void fill(const Pixel& pixel) { Byte* row = data(); for (int y = 0; y < _size.y; ++y) { Pixel* p = reinterpret_cast(row); for (int x = 0; x < _size.x; ++x) { *p = pixel; ++p; } row += _stride; } } // Copy with pixel format conversion but no resizing. Bitmaps must be the // same dimensions. template void copyFrom(const Bitmap& other) { Byte* row = data(); const Byte* otherRow = other.data(); for (int y = 0; y < _size.y; ++y) { Pixel* p = reinterpret_cast(row); const OtherPixel* op = reinterpret_cast(otherRow); for (int x = 0; x < _size.x; ++x) { *p = *op; ++p; ++op; } row += _stride; otherRow += other._stride; } } template void copyTo(Bitmap& other) const { other.copyFrom(*this); } private: Bitmap(Array array, Byte* topLeft, Vector size, int stride) : Array(array), _topLeft(topLeft), _size(size), _stride(stride) { } Vector _size; Byte* _topLeft; int _stride; }; #endif // INCLUDED_BITMAP_H