#ifndef SURFACE_H
#define SURFACE_H
#include <cassert>
#include <cstddef>
#include <gdkmm/pixbuf.h>
#include <cairomm/surface.h>
#include "common.h"
class Surface: public Shared {
protected:
int m_width;
int m_height;
int m_pitch;
Color *m_origin;
Surface();
Surface(const Surface &);
Surface& operator= (const Surface &) { return *this; }
void reset();
public:
virtual ~Surface() { }
inline int width() const { return m_width; }
inline int height() const { return m_height; }
inline int pitch() const { return m_pitch; }
inline int count() const { return m_width*m_height; }
inline bool empty() const { return !count(); }
inline Color* origin() { return m_origin; }
inline const Color* origin() const { return m_origin; }
inline int data_count() const { return data_count (width(), height(), pitch()); }
inline size_t data_size() const { return data_size (width(), height(), pitch()); }
inline int begin_offset() const { return begin_offset(width(), height(), pitch()); }
inline int end_offset() const { return end_offset (width(), height(), pitch()); }
inline Color* data_begin() { return m_origin ? m_origin + begin_offset() : NULL; }
inline const Color* data_begin() const { return m_origin ? m_origin + begin_offset() : NULL; }
inline Color* data_end() { return m_origin ? m_origin + end_offset() : NULL; }
inline const Color* data_end() const { return m_origin ? m_origin + end_offset() : NULL; }
inline Color* row(int index)
{ assert(index >= 0 && index < m_height); return m_origin + index*m_pitch; }
inline const Color* row(int index) const
{ assert(index >= 0 && index < m_height); return m_origin + index*m_pitch; }
inline Color* operator[] (int index) { return row(index); }
inline const Color* operator[] (int index) const { return row(index); }
inline void put_pixel(int x, int y, const Color &color) {
if (x >= 0 && y >= 0 && x < width() && y < height())
row(y)[x] = color;
}
void mult_alpha();
void demult_alpha();
Color get_pixel(Real x, Real y) const;
Color get_pixel_premulted(Real x, Real y) const;
static inline int data_count(int width, int height, int pitch)
{ return width + abs(pitch)*(height - 1); }
static inline size_t data_size(int width, int height, int pitch)
{ return data_count(width, height, pitch)*sizeof(Color); }
static inline int begin_offset(int width, int height, int pitch)
{ return pitch > 0 ? 0 : width - data_count(width, height, pitch); }
static inline int end_offset(int width, int height, int pitch)
{ return pitch > 0 ? data_count(width, height, pitch) : width; }
static void copy(int width, int height, const Color *src_origin, Color *dest_origin, int src_pitch = 0, int dest_pitch = 0);
Glib::RefPtr<Gdk::Pixbuf> to_pixbuf() const;
Cairo::RefPtr<Cairo::ImageSurface> to_cairo_surface(bool premulted = false) const;
};
class DataSurface: public Surface {
public:
explicit DataSurface(int width = 0, int height = 0, int pitch = 0, const Color *src_origin = NULL, int src_pitch = 0)
{ init(width, height, pitch, src_origin, src_pitch); }
DataSurface(const Surface &other)
{ *this = other; }
~DataSurface()
{ clear(); }
DataSurface& operator= (const Surface &other)
{ init(other); return *this; }
void clear();
void init(int width, int height, int pitch = 0, const Color *src_origin = NULL, int src_pitch = 0);
inline void init(const Surface &other)
{ init(other.width(), other.height(), other.pitch(), other.origin(), other.pitch()); }
};
class AliasSurface: public Surface {
public:
AliasSurface() { }
AliasSurface(Color *origin, int width, int height, int pitch = 0)
{ init(origin, width, height, pitch); }
AliasSurface(const AliasSurface &other)
{ *this = other; }
AliasSurface(Surface &other)
{ init(other); }
AliasSurface& operator= (const AliasSurface &other)
{ init(other); return *this; }
inline Color* origin() const { return m_origin; }
void init(Color *origin, int width, int height, int pitch = 0);
inline void clear() { reset(); }
inline void init(Surface &other)
{ init(other.origin(), other.width(), other.height(), other.pitch()); }
inline void init(const AliasSurface &other)
{ init(other.origin(), other.width(), other.height(), other.pitch()); }
};
#endif