ext/wxImage.cpp

Summary

Maintainability
Test Coverage
/*
 * wxImage.cpp
 *
 *  Created on: 16.02.2012
 *      Author: hanmac
 */

#include "wxApp.hpp"
#include "wxSize.hpp"
#include "wxRect.hpp"
#include "wxPoint.hpp"

#include "wxImage.hpp"
#include "wxBitmap.hpp"
#include "wxDC.hpp"
#include "wxColor.hpp"

#include "wxStream.hpp"

#if wxUSE_PALETTE
#include "wxPalette.hpp"
#endif

#if wxUSE_IMAGE

#define _self unwrap<wxImage*>(self)

VALUE rb_cWXImage;

template <>
wxImage* unwrap< wxImage* >(const VALUE &vimage)
{
    if(rb_obj_is_kind_of(vimage,rb_cWXImage))
        return unwrapTypedPtr<wxImage>(vimage, rb_cWXImage);
    if(is_wrapable<wxSize>(vimage))
        return new wxImage(unwrap<wxSize>(vimage));
    return unwrapTypedPtr<wxImage>(rb_class_new_instance(1,const_cast<VALUE*>(&vimage),rb_cWXImage), rb_cWXImage);
}


template <>
wxImage unwrap< wxImage >(const VALUE &vimage)
{
    return *unwrap<wxImage*>(vimage);
}


namespace RubyWX {
namespace Image {

#if wxUSE_PALETTE
macro_attr(Palette,wxPalette)
#endif

DLL_LOCAL VALUE _load(int argc,VALUE *argv,VALUE self)
{
    VALUE name,mime,nr;
    rb_scan_args(argc, argv, "12",&name,&mime,&nr);

    bool result;

    if(NIL_P(nr))
        nr = RB_INT2NUM(-1);

#if wxUSE_STREAMS
    if(!rb_respond_to(name,rb_intern("read")))
    {
#endif
        if(!check_file_loadable(unwrap<wxString>(name)))
            return Qfalse;

        wxFileName file(unwrap<wxString>(name));
        file.MakeAbsolute(wxGetCwd());

        if(NIL_P(mime)){
            result = _self->LoadFile(file.GetFullPath(), wxBITMAP_TYPE_ANY, RB_NUM2INT(nr));
        }else if(RB_SYMBOL_P(mime) || RB_FIXNUM_P(mime)){
//            result = _self->LoadFile(file.GetFullPath(),unwrap<wxBitmapType>(mime),RB_NUM2INT(nr));
        }else
            result = _self->LoadFile(file.GetFullPath(),unwrap<wxString>(mime),RB_NUM2INT(nr));
#if wxUSE_STREAMS
    }else{
        RubyInputStream st(name);

        if(NIL_P(mime)){
            result = _self->LoadFile(st, wxBITMAP_TYPE_ANY, RB_NUM2INT(nr));
        }else if(RB_SYMBOL_P(mime) || RB_FIXNUM_P(mime)){
//            result = _self->LoadFile(st,unwrap<wxBitmapType>(mime),RB_NUM2INT(nr));
        }else
            result = _self->LoadFile(st,unwrap<wxString>(mime),RB_NUM2INT(nr));
    }
#endif
    return wrap(result);
}

DLL_LOCAL VALUE _alloc(VALUE self) {
    return wrapTypedPtr(new wxImage, self);
}


/*
 * call-seq:
 *   Image.new(path,[type,[id]])
 *   Image.new(width,height)
 *
 * creates a new Image Object.
 * === Exceptions
 * [ArgumentError]
 * * if height or width are negative or zero
*/
DLL_LOCAL VALUE _initialize(int argc,VALUE *argv,VALUE self)
{
    VALUE width,height,arg1;
    rb_scan_args(argc, argv, "12",&width,&height,&arg1);
    if(rb_obj_is_kind_of(width, rb_cWXSize))
    {
        _self->Create(unwrap<wxSize>(width));
    } else if(! rb_obj_is_kind_of(width, rb_cNumeric) || NIL_P(height) || !NIL_P(arg1))
        _load(argc,argv,self);
    else {
        int cwidth = RB_NUM2INT(width);
        int cheight = RB_NUM2INT(height);

        if(check_negative_size(cwidth, cheight)) {
            _self->Create(cwidth, cheight);
        }
    }
    //_self->InitAlpha();
    return self;
}

singlereturn_if(GetHeight, _self->IsOk())
singlereturn_if(GetWidth, _self->IsOk())
singlereturn_if(GetSize, _self->IsOk())

DLL_LOCAL VALUE _getData(VALUE self)
{
    if(_self->IsOk())
    {
        //if(_self->GetType() == wxBITMAP_TYPE_INVALID)
        //    return Qnil;
        char* data = (char*)_self->GetData();
        if(data)
            return rb_str_new(data,_self->GetHeight() * _self->GetWidth() * 3);
    }
    return Qnil;
}

DLL_LOCAL VALUE _getAlpha(VALUE self)
{
    if(_self->IsOk() && _self->HasAlpha())
    {
        return rb_str_new((char*) _self->GetAlpha(),_self->GetHeight() * _self->GetWidth());
    }
    return Qnil;
}


bool check_inside(int x, int y, const wxSize& size)
{
    return x >= 0 && y >= 0 && x < size.GetWidth() && y < size.GetHeight();
}

/*
 * call-seq:
 *   image.sub_image(rect) -> WX::Image or nil
 *
 * return a sub image of the given place
 * ===Arguments
 * * rect is a WX::Rect
 * ===Return value
 * WX::Image or nil
 * === Exceptions
 * [ArgumentError]
 * * rect does have negative size
 * * rect does not fit into the Size of the Image
*/
DLL_LOCAL VALUE _getSubImage(VALUE self, VALUE vrect)
{
    if(_self->IsOk())
    {
        wxRect rect;
        wxSize size;

        if(!check_negative_size(vrect, size))
            return Qnil;

        if(!check_contain_rect(_GetSize(self), _self->GetSize(), vrect, rect))
            return Qnil;

        return wrap(_self->GetSubImage(rect));
    }
    return Qnil;
}

/*
 * call-seq:
 *   image[rect] -> WX::Image or nil
 *   image[x,y] -> WX::Color or nil
 *
 * if giving a WX::Rect, return a sub image of the given place
 * if giving x and y, return the color of the given position or nil when out of range
 * ===Arguments
 * * x and y are Integer
 * * rect is a WX::Rect
 * ===Return value
 * WX::Image, WX::Color or nil
 * === Exceptions
 * [ArgumentError]
 * * rect does have negative size
 * * rect does not fit into the Size of the Image
*/
DLL_LOCAL VALUE _get(int argc,VALUE *argv,VALUE self)
{
    VALUE vx,vy;
    rb_scan_args(argc, argv, "11",&vx,&vy);
    if(_self->IsOk())
    {
        if(NIL_P(vy))
            return _getSubImage(self, vy);

        int x,y;
        x = RB_NUM2UINT(vx);
        y = RB_NUM2UINT(vy);

        if(!check_inside(x,y, _self->GetSize()))
            return Qnil;

        unsigned char red,green,blue,alpha;
        red = _self->GetRed(x,y);
        green = _self->GetGreen(x,y);
        blue = _self->GetBlue(x,y);
        if(_self->HasAlpha())
            alpha = _self->GetAlpha(x,y);
        else
            alpha = wxALPHA_OPAQUE;
        return wrap(new wxColor(red,green,blue,alpha));
    }else
        return Qnil;
}

void set_at_pos(int x, int y, wxImage* self, VALUE val)
{
    if(!check_inside(x, y, self->GetSize()))
        return;

    if(rb_obj_is_kind_of(val,rb_cWXImage) || rb_obj_is_kind_of(val,rb_cWXBitmap))
    {
        self->Paste(unwrap<wxImage>(val),x,y);
    } else {
        wxColor c(unwrap<wxColor>(val));
        self->SetRGB(x,y,c.Red(),c.Green(),c.Blue());
        if(self->HasAlpha())
            self->SetAlpha(x,y,c.Alpha());
    }
}

/*
 * call-seq:
 *   image[x,y]= WX::Color or WX::Image or WX::Bitmap
 *   image[pos]= WX::Color or WX::Image or WX::Bitmap
 *   image[rect]= WX::Color
 *
 * if giving x and y or pos, and as value a color, sets the color at the given position
 * if giving x and y or pos, and an image or bitmap does paste it at the given position
 * if giving a WX::Rect, fill the color at the place with the given one
 * ===Arguments
 * * x and y are Integer
 * * pos is a WX::Point
 * * rect is a WX::Rect
 *
 * === Exceptions
 * [ArgumentError]
 * * rect does not fit into the Size of the Image
 */
DLL_LOCAL VALUE _set(int argc,VALUE *argv,VALUE self)
{
    VALUE vx,vy,value;
    rb_scan_args(argc, argv, "21",&vx,&vy,&value);

    rb_check_frozen(self);

    if(_self->IsOk())
    {
        if(NIL_P(value)) {
            if(is_wrapable<wxRect>(vx)) {
                wxColor c(unwrap<wxColor>(vy));
                wxSize size(_self->GetSize());
                wxRect vrect;

                if(check_contain_rect(_GetSize(self), size, vx, vrect))
                {
                    _self->SetRGB(vrect,c.Red(),c.Green(),c.Blue());
                    if(_self->HasAlpha()) {
                        for(int i = vrect.x; i <= vrect.GetRight(); ++i)
                            for(int j = vrect.y; j <= vrect.GetBottom(); ++j)
                                if(check_inside(i, j, size))
                                    _self->SetAlpha(i,j,c.Alpha());
                    }
                }
            } else {
                wxPoint vpoint(unwrap<wxPoint>(vx));
                set_at_pos(vpoint.x, vpoint.y, _self, vy);
            }
        } else {
            set_at_pos(RB_NUM2UINT(vx), RB_NUM2UINT(vy), _self, value);
        }
    }

    return NIL_P(value) ? value : vy;
}

/*
 * call-seq:
 *   image.resize(size, pos [, color]) -> WX::Image
 *   image.resize(rect [, color]) -> WX::Image
 *
 * returns a new resized image, if color is given use this one to fill new space
 * ===Arguments
 * * size WX::Size
 * * pos WX::Point
 * * rect WX::Rect
 * * color WX::Color
 * ===Return value
 * WX::Image
 *
 * === Exceptions
 * [ArgumentError]
 * * size is invalid
*/
DLL_LOCAL VALUE _resize(int argc,VALUE *argv,VALUE self)
{
    VALUE size, pos, color;
    rb_scan_args(argc, argv, "12",&size,&pos, &color);

    wxSize csize;
    if(!check_negative_size(size,csize))
        return Qnil;

    wxPoint cpos;

    if(is_wrapable<wxColor>(pos))
    {
        std::swap(color,pos);
    }

    if(NIL_P(pos) && is_wrapable<wxPoint>(size))
        cpos = unwrap<wxPoint>(size);
    else
        cpos = unwrap<wxPoint>(pos);

    if(NIL_P(color))
        return wrap(_self->Size(csize, cpos));
    else {
        wxColor c(unwrap<wxColor>(size));

        return wrap(_self->Size(csize, cpos, c.Red(), c.Green(), c.Blue()));
    }
}

/*
 * call-seq:
 *   image.resize!(size, pos [, color]) -> self
 *   image.resize!(rect [, color]) -> self
 *
 * returns this image resized, if color is given use this one to fill new space
 * ===Arguments
 * * size WX::Size
 * * pos WX::Point
 * * rect WX::Rect
 * * color WX::Color
 * ===Return value
 * self
 * === Exceptions
 * [ArgumentError]
 * * size is invalid
*/
DLL_LOCAL VALUE _resize_self(int argc,VALUE *argv,VALUE self)
{
    VALUE size, pos, color;
    rb_scan_args(argc, argv, "12",&size,&pos, &color);

    rb_check_frozen(self);

    wxSize csize;
    if(!check_negative_size(size,csize))
        return self;

    wxPoint cpos;

    if(is_wrapable<wxColor>(pos))
    {
        std::swap(color,pos);
    }

    if(NIL_P(pos) && is_wrapable<wxPoint>(size))
        cpos = unwrap<wxPoint>(size);
    else
        cpos = unwrap<wxPoint>(pos);

    if(NIL_P(color))
        _self->Resize(csize, cpos);
    else {
        wxColor c(unwrap<wxColor>(size));

        _self->Resize(csize, cpos, c.Red(), c.Green(), c.Blue());
    }
    return self;
}


/*
 * call-seq:
 *   image.scale(size) -> WX::Image
 *   image.scale(x_ratio, y_ratio) -> WX::Image
 *
 * returns a new scaled image
 * ===Arguments
 * * size WX::Size
 * * x_ratio and y_ratio are Float
 * ===Return value
 * WX::Image
 * === Exceptions
 * [ArgumentError]
 * * size is invalid or x_ratio or y_ratio is zero
*/
DLL_LOCAL VALUE _scale(int argc,VALUE *argv,VALUE self)
{
    VALUE x_scale, y_scale;
    rb_scan_args(argc, argv, "11",&x_scale,&y_scale);

    if(NIL_P(y_scale))
    {
        wxSize size;
        if(!check_negative_size(x_scale,size))
            return Qnil;

        return wrap(_self->Scale(size.GetWidth(),size.GetHeight()));
    }else {

        double x,y;
        x = NUM2DBL(x_scale);
        y = NUM2DBL(y_scale);

        if(x == 0 || y == 0)
        {
            if(!check_negative_size(x, y))
                return Qnil;
        }

        wxImage result(*_self);

        if(x < 0)
        {
            x *= -1;
            result = result.Mirror(false);
        }
        if(y < 0)
        {
            y *= -1;
            result = result.Mirror();
        }

        result.Rescale(result.GetWidth() * x,result.GetHeight() * y);
        return wrap(result);
    }
}


/*
 * call-seq:
 *   image.scale!(size) -> self
 *   image.scale!(x_ratio, y_ratio) -> self
 *
 * scaled this image, return self
 * ===Arguments
 * * size WX::Size
 * * x_ratio and y_ratio are Float
 * ===Return value
 * self
 * === Exceptions
 * [ArgumentError]
 * * size is invalid or x_ratio or y_ratio is zero
*/
DLL_LOCAL VALUE _scale_self(int argc,VALUE *argv,VALUE self)
{
    VALUE x_scale, y_scale;
    rb_scan_args(argc, argv, "11",&x_scale,&y_scale);

    rb_check_frozen(self);

    if(NIL_P(y_scale))
    {
        wxSize size;
        if(check_negative_size(x_scale,size))
            _self->Rescale(size.GetWidth(),size.GetHeight());
    }else {
        double x,y;
        x = NUM2DBL(x_scale);
        y = NUM2DBL(y_scale);

        if(x == 0 || y == 0)
        {
            if(!check_negative_size(x, y))
                return Qnil;
        }

        if(x < 0)
        {
            x *= -1;
            *_self = _self->Mirror(false);
        }
        if(y < 0)
        {
            y *= -1;
            *_self = _self->Mirror();
        }

        _self->Rescale(_self->GetWidth() * x,_self->GetHeight() * y);
    }
    return self;
}

/*
 * call-seq:
 *   image.replace_color(color1, color2) -> WX::Image
 *
 * replaces color1 with color2, return new image
 * ===Arguments
 * * color1 and color2 are WX::Color
 * ===Return value
 * WX::Image
*/
DLL_LOCAL VALUE _replace_color(VALUE self, VALUE color1, VALUE color2)
{
    wxColor c1(unwrap<wxColor>(color1));
    wxColor c2(unwrap<wxColor>(color2));
    wxImage result(*_self);
    result.Replace(c1.Red(),c1.Green(),c1.Blue(),c2.Red(),c2.Green(),c2.Blue());
    return wrap(result);
}


/*
 * call-seq:
 *   image.replace_color!(color1, color2) -> self
 *
 * replaces color1 with color2, return self
 * ===Arguments
 * * color1 and color2 are WX::Color
 * ===Return value
 * self
*/
DLL_LOCAL VALUE _replace_color_self(VALUE self, VALUE color1, VALUE color2)
{
    rb_check_frozen(self);
    wxColor c1(unwrap<wxColor>(color1));
    wxColor c2(unwrap<wxColor>(color2));
    _self->Replace(c1.Red(),c1.Green(),c1.Blue(),c2.Red(),c2.Green(),c2.Blue());
    return self;
}

bool range_check(double val, double min, double max)
{
    bool result = val >= min && val <= max;
    if(!result)
        rb_raise(rb_eRangeError,"%f is out of range (%f..%f)", val, min, max);
    return result;
}


/*
 * call-seq:
 *   image.rotate_hue(angle) -> WX::Image
 *
 * rotates the hue, where -1.0 is -360 degrees and 1.0 is 360 degrees, return new image
 * ===Arguments
 * * angle is Float
 * ===Return value
 * WX::Image
 * === Exceptions
 * [RangeError]
 * * angle is outside of -1..1
*/
DLL_LOCAL VALUE _rotate_hue(VALUE self, VALUE angle)
{
    double val = NUM2DBL(angle);
    if(range_check(val, -1.0, 1.0)){
        wxImage result(*_self);
        result.RotateHue(val);
        wrap(result);
    }
    return Qnil;
}

/*
 * call-seq:
 *   image.rotate_hue!(angle) -> self
 *
 * rotates the hue, where -1.0 is -360 degrees and 1.0 is 360 degrees, return self
 * ===Arguments
 * * angle is Float
 * ===Return value
 * self
 * === Exceptions
 * [RangeError]
 * * angle is outside of -1..1
*/
DLL_LOCAL VALUE _rotate_hue_self(VALUE self, VALUE angle)
{
    rb_check_frozen(self);
    double val = NUM2DBL(angle);
    if(range_check(val, -1.0, 1.0))
        _self->RotateHue(val);

    return self;
}

DLL_LOCAL VALUE _mal(VALUE self,VALUE obj)
{


    wxImage *result = new wxImage(_self->GetWidth(),_self->GetHeight());
    //result->SetData(_self->GetData(),true);
    //result->SetAlpha(_self->GetAlpha());
    result->Paste(*_self,0,0);
    if(rb_obj_is_kind_of(obj,rb_cNumeric))
    {
        double d = NUM2DBL(obj);
        for(int x = 0; x < result->GetWidth();++x)
            for(int y = 0; y < result->GetHeight();++y)
            {
                unsigned char alpha;
                if(result->HasAlpha())
                    alpha = result->GetAlpha(x,y);
                else
                {
                    result->InitAlpha();
                    alpha = wxALPHA_OPAQUE;
                }
                result->SetAlpha(x,y,d * alpha);
            }

    }else
    {
        wxColor c(unwrap<wxColor>(obj));
        for(int x = 0; x < result->GetWidth();++x)
            for(int y = 0; y < result->GetHeight();++y)
            {
                unsigned char red,green,blue,alpha;
                red = result->GetRed(x,y);
                green = result->GetGreen(x,y);
                blue = result->GetBlue(x,y);

                if(result->HasAlpha())
                    alpha = result->GetAlpha(x,y);
                else
                    alpha = wxALPHA_OPAQUE;
                result->SetRGB(x,y,
                        red * c.Red() / 256,
                        green * c.Green() / 256,
                        blue * c.Blue() / 256);
                result->SetAlpha(x,y,alpha * c.Alpha() / wxALPHA_OPAQUE);
            }

    }
    return wrap(result);
}

DLL_LOCAL VALUE _getMask(VALUE self)
{
    unsigned char r,g,b;
    if(_self->GetOrFindMaskColour(&r,&g,&b))
        return wrap(new wxColor(r,g,b));
    else
        return Qnil;
}

DLL_LOCAL VALUE _setMask(VALUE self,VALUE val)
{
    rb_check_frozen(self);
    if(NIL_P(val))
        _self->SetMask(false);
    else
    {
        wxColor c(wrap<wxColor>(val));
        _self->SetMaskColour(c.Red(),c.Green(),c.Blue());
    }
    return val;
}


DLL_LOCAL VALUE _save(int argc,VALUE *argv,VALUE self)
{
    VALUE name,mime;
    rb_scan_args(argc, argv, "11",&name,&mime);
    if(!_self->IsOk())
        return Qfalse;

    if(!check_file_saveable(unwrap<wxString>(name)))
        return Qfalse;

    wxFileName file(unwrap<wxString>(name));
    file.MakeAbsolute(wxGetCwd());

    bool result = false;

    if(NIL_P(mime)){
        result = _self->SaveFile(file.GetFullPath());
    }else if(RB_SYMBOL_P(mime) || RB_FIXNUM_P(mime)){
//        if(wxImage::FindHandler(unwrap<wxBitmapType>(mime)))
//            result = _self->SaveFile(file.GetFullPath(),unwrap<wxBitmapType>(mime));
//        else
//            rb_raise(rb_eArgError,"%s type not known",rb_id2name(SYM2ID(mime)));
    }else{
//    if(wxImage::FindHandlerMime(unwrap<wxString>(mime)))
        result = _self->SaveFile(file.GetFullPath(),unwrap<wxString>(mime));
//        else
//            rb_raise(rb_eArgError,"%s mime not known",unwrap<wxString>(mime).c_str().AsChar());
    }
    return wrap(result);
}

/*
 * call-seq:
 *   image.blur(radius) -> WX::Image
 *
 * blur the image according to the specified pixel radius
 * ===Arguments
 * * radius Integer
 * ===Return value
 * WX::Image
*/
DLL_LOCAL VALUE _Blur(VALUE self, VALUE radius)
{
    return wrap(_self->Blur(RB_NUM2INT(radius)));
}

/*
 * call-seq:
 *   image.blur!(radius) -> self
 *
 * blur the image according to the specified pixel radius
 * ===Arguments
 * * radius Integer
 * ===Return value
 * self
*/
DLL_LOCAL VALUE _Blur_self(VALUE self, VALUE radius)
{
    rb_check_frozen(self);
    _self->Paste(_self->Blur(RB_NUM2INT(radius)), 0, 0);
    return self;
}

/*
 * call-seq:
 *   image.blur_horizontal(radius) -> WX::Image
 *
 * blur the image according to the specified pixel radius
 * ===Arguments
 * * radius Integer
 * ===Return value
 * WX::Image
*/
DLL_LOCAL VALUE _BlurHorizontal(VALUE self, VALUE radius)
{
    return wrap(_self->BlurHorizontal(RB_NUM2INT(radius)));
}

/*
 * call-seq:
 *   image.blur_horizontal!(radius) -> self
 *
 * blur the image according to the specified pixel radius
 * ===Arguments
 * * radius Integer
 * ===Return value
 * self
*/
DLL_LOCAL VALUE _BlurHorizontal_self(VALUE self, VALUE radius)
{
    rb_check_frozen(self);
    _self->Paste(_self->BlurHorizontal(RB_NUM2INT(radius)), 0, 0);
    return self;
}

/*
 * call-seq:
 *   image.blur_vertical(radius) -> WX::Image
 *
 * blur the image according to the specified pixel radius
 * ===Arguments
 * * radius Integer
 * ===Return value
 * WX::Image
*/
DLL_LOCAL VALUE _BlurVertical(VALUE self, VALUE radius)
{
    return wrap(_self->BlurVertical(RB_NUM2INT(radius)));
}

/*
 * call-seq:
 *   image.blur_vertical(radius) -> WX::Image
 *
 * blur the image according to the specified pixel radius
 * ===Arguments
 * * radius Integer
 * ===Return value
 * WX::Image
*/
DLL_LOCAL VALUE _BlurVertical_self(VALUE self, VALUE radius)
{
    rb_check_frozen(self);
    _self->Paste(_self->BlurVertical(RB_NUM2INT(radius)), 0, 0);
    return self;
}

/*
 * call-seq:
 *   image.convert_to_disabled([brightness = 255]) -> WX::Image
 *
 * Convert to disabled (dimmed) image.
 * ===Arguments
 * * brightness Fixnum
 * ===Return value
 * WX::Image
*/
DLL_LOCAL VALUE _ConvertToDisabled(int argc,VALUE *argv,VALUE self)
{
    VALUE brightness;
    rb_scan_args(argc, argv, "01",&brightness);
    return wrap(_self->ConvertToDisabled(RB_NUM2CHR(brightness)));
}

/*
 * call-seq:
 *   image.convert_to_disabled([brightness = 255]) -> WX::Image
 *
 * Convert to disabled (dimmed) image.
 * ===Arguments
 * * brightness Fixnum
 * ===Return value
 * WX::Image
*/
DLL_LOCAL VALUE _ConvertToDisabled_self(int argc,VALUE *argv,VALUE self)
{
    VALUE brightness;
    rb_scan_args(argc, argv, "01",&brightness);
    rb_check_frozen(self);
    _self->Paste(_self->ConvertToDisabled(RB_NUM2CHR(brightness)), 0, 0);
    return self;
}

/*
 * call-seq:
 *   image.convert_to_mono(color) -> WX::Image
 *
 * returns a new image where color is turned white
 * and all other colors are turned black.
 * ===Arguments
 * * color WX::Color
 * ===Return value
 * WX::Image
*/
DLL_LOCAL VALUE _ConvertToMono(VALUE self, VALUE color)
{
    wxColor c(unwrap<wxColor>(color));
    return wrap(_self->ConvertToMono(c.Red(), c.Green(), c.Blue()));
}

/*
 * call-seq:
 *   image.convert_to_mono!(color) -> self
 *
 * returns a new image where color is turned white
 * and all other colors are turned black.
 * ===Arguments
 * * color WX::Color
 * ===Return value
 * self
*/
DLL_LOCAL VALUE _ConvertToMono_self(VALUE self, VALUE color)
{
    rb_check_frozen(self);
    wxColor c(unwrap<wxColor>(color));
    _self->Paste(_self->ConvertToMono(c.Red(), c.Green(), c.Blue()), 0, 0);
    return self;
}
/*
 * call-seq:
 *   draw { | dc | } -> self
 *
 * create a DC for drawing in the image.
 *
 */
DLL_LOCAL VALUE _draw(VALUE self)
{
    app_protected();
    rb_check_frozen(self);

    wxDC *dc;

#if wxUSE_GRAPHICS_CONTEXT
    dc = new wxGCDC(wxGraphicsContext::Create(*_self));
#else
    wxBitmap bit(*_self);
    wxMemoryDC *mdc = new wxMemoryDC(bit);
    dc = mdc;
#endif
    rb_yield(wrap(dc));

#if !wxUSE_GRAPHICS_CONTEXT
    (*_self) = bit.ConvertToImage();
    mdc->SelectObject(wxNullBitmap);
#endif
    //TODO add a way to delete the DCs again
    return self;
}


/*
 * call-seq:
 *   to_image -> WX::Image
 *
 * return self
 *
 */
DLL_LOCAL VALUE _to_image(VALUE self)
{
    return self;
}

/*
 * call-seq:
 *   to_bitmap -> WX::Bitmap
 *
 * convert to WX::Bitmap
 *
 * ===Arguments
 * * opts: Hash with possible options to set:
 *   * depth Integer
 *   * scale Float
 * === Exceptions
 * [ArgumentError]
 * * if scale is negative or zero
 */
DLL_LOCAL VALUE _to_bitmap(int argc,VALUE *argv,VALUE self)
{
    VALUE opts;
    rb_scan_args(argc, argv, "0:",&opts);

    int cdepth(wxBITMAP_SCREEN_DEPTH);
    double cscale(1.0);

    if(rb_obj_is_kind_of(opts, rb_cHash)) {
        set_hash_option(opts, "depth", cdepth);
        if(set_hash_option(opts, "scale", cscale)) {
            if(cscale <= 0.0) {
                rb_raise(rb_eArgError, "scale cant be negative or zero");
                return Qnil;
            }
        }
    }
    return wrap(new wxBitmap(
        *_self, cdepth
#if HAVE_WXBITMAP_WXIMAGE
        , cscale
#endif
        ));
}

/* Document-method: initialize_copy
 * call-seq:
 *   initialize_copy(orig)
 *
 * Duplicate an object
*/
DLL_LOCAL VALUE _initialize_copy(VALUE self, VALUE other)
{
    VALUE result = rb_call_super(1,&other);
    wxImage* cother = unwrap<wxImage*>(other);

    _self->SetData(cother->GetData(),cother->GetWidth(),cother->GetHeight());
    if(cother->HasAlpha())
        _self->SetAlpha(cother->GetAlpha());

    _setMask(self,_getMask(other));

#if wxUSE_PALETTE
    _setPalette(self,_getPalette(other));
#endif
    return result;
}

/*
 * call-seq:
 *   hash -> Fixnum
 *
 * Generates a Fixnum hash value for this object.
 *
 *
 */
DLL_LOCAL VALUE _getHash(VALUE self)
{
    st_index_t h = rb_hash_start(0);

    h = rb_hash_uint(h, _self->GetWidth());
    h = rb_hash_uint(h, _self->GetHeight());

    if(_self->IsOk())
    {
        //if(_self->GetType() == wxBITMAP_TYPE_INVALID)
        //    return Qnil;
        char* data = (char*)_self->GetData();
        if(data)
            h = rb_hash_uint(h, rb_str_hash(rb_str_new(data,_self->GetHeight() * _self->GetWidth() * 3)));

        if(_self->HasAlpha())
        {
            h = rb_hash_uint(h, rb_str_hash(rb_str_new((char*) _self->GetAlpha(),_self->GetHeight() * _self->GetWidth())));
        }

        unsigned char r,g,b;
        if(_self->GetOrFindMaskColour(&r,&g,&b))
        {
            h = rb_hash_uint(h, r);
            h = rb_hash_uint(h, g);
            h = rb_hash_uint(h, b);
        }
    }

    h = rb_hash_end(h);
    return LONG2FIX(h);
}

bool check_equal(const wxImage &self, const wxImage &cother)
{
    if(self.GetHeight() != cother.GetHeight()){
        return false;
    }
    if(self.GetWidth() != cother.GetWidth()) {
        return false;
    }
    if(strcmp((char*)self.GetData(), (char*)cother.GetData()) != 0){
        return false;
    }
    if(self.HasAlpha()) {
        if(!cother.HasAlpha())
            return false;
        if(strcmp((char*)self.GetAlpha(), (char*)cother.GetAlpha()) != 0){
            return false;
        }
    } else {
        return !cother.HasAlpha();
    }

    return true;
}

struct equal_obj {
    wxImage* self;
    VALUE other;
};

VALUE _equal_block(equal_obj *obj)
{
    return wrap(check_equal(*obj->self, unwrap<wxImage>(obj->other)));
}

/*
 * call-seq:
 *   == image -> bool
 *
 * compares two image.
 *
 *
 */
DLL_LOCAL VALUE _equal(VALUE self, VALUE other)
{
    equal_obj obj;
    obj.self = _self;
    obj.other = other;

    return rb_rescue(
        RUBY_METHOD_FUNC(_equal_block),(VALUE)&obj,
        RUBY_METHOD_FUNC(_equal_rescue),Qnil
    );
}

/*
 * call-seq:
 *   marshal_dump -> Array
 *
 * Provides marshalling support for use by the Marshal library.
 * ===Return value
 * Array
 */
DLL_LOCAL VALUE _marshal_dump(VALUE self)
{
    VALUE result = rb_ary_new();

    rb_ary_push(result,_GetWidth(self));
    rb_ary_push(result,_GetHeight(self));
    rb_ary_push(result,_getData(self));
    rb_ary_push(result,_getAlpha(self));
    rb_ary_push(result,_getMask(self));
#if wxUSE_PALETTE
    rb_ary_push(result,_getPalette(self));
#else
    rb_ary_push(result,Qnil);
#endif
    return result;
}

/*
 * call-seq:
 *   marshal_load(array) -> self
 *
 * Provides marshalling support for use by the Marshal library.
 *
 *
 */
DLL_LOCAL VALUE _marshal_load(VALUE self,VALUE data)
{
    data = rb_Array(data);

    VALUE tmp = RARRAY_AREF(data,3);
    unsigned char* alpha = NULL;
    if(!NIL_P(tmp))
        alpha = (unsigned char*)StringValuePtr(tmp);

    VALUE val = RARRAY_AREF(data,2);
    _self->Create(
        RB_NUM2UINT(RARRAY_AREF(data,0)),RB_NUM2UINT(RARRAY_AREF(data,1)),
        (unsigned char*)StringValuePtr(val),alpha);

    _setMask(self,RARRAY_AREF(data,4));

#if wxUSE_PALETTE
    _setPalette(self,RARRAY_AREF(data,5));
#endif

    return self;
}

}
}

#endif


/* Document-attr: width
 * returns the width of the Image. Integer
 */
/* Document-attr: height
 * returns the height of the Image. Integer
 */
/* Document-attr: size
 * returns the size of the Image. WX::Size
 */

/* Document-attr: data
 * returns the data of the Image. String
 */
/* Document-attr: alpha
 * returns the alpha of the Image. String
 */

/* Document-attr: mask
 * returns the mask color of the Image. WX::Color
 */
/* Document-attr: palette
 * returns the color palette of the Image. WX::Palette
 */

DLL_LOCAL void Init_WXImage(VALUE rb_mWX)
{
#if wxUSE_IMAGE

    wxInitAllImageHandlers();

    using namespace RubyWX::Image;
    rb_cWXImage = rb_define_class_under(rb_mWX,"Image",rb_cObject);
    rb_define_alloc_func(rb_cWXImage,_alloc);

#if 0
    rb_define_attr(rb_cWXImage,"width",1,0);
    rb_define_attr(rb_cWXImage,"height",1,0);
    rb_define_attr(rb_cWXImage,"size",1,0);
    rb_define_attr(rb_cWXImage,"data",1,0);
    rb_define_attr(rb_cWXImage,"alpha",1,0);

    rb_define_attr(rb_cWXImage,"mask",1,1);
    rb_define_attr(rb_cWXImage,"palette",1,1);
#endif

    rb_define_method(rb_cWXImage,"initialize",RUBY_METHOD_FUNC(_initialize),-1);
    rb_define_private_method(rb_cWXImage,"initialize_copy",RUBY_METHOD_FUNC(_initialize_copy),1);

    rb_define_attr_method(rb_cWXImage,"height",_GetHeight,NULL);
    rb_define_attr_method(rb_cWXImage,"width",_GetWidth,NULL);
    rb_define_attr_method(rb_cWXImage,"size",_GetSize,NULL);
    rb_define_attr_method(rb_cWXImage,"data",_getData,NULL);
    rb_define_attr_method(rb_cWXImage,"alpha",_getAlpha,NULL);

    rb_define_method(rb_cWXImage,"draw",RUBY_METHOD_FUNC(_draw),0);

    rb_define_method(rb_cWXImage,"to_image",RUBY_METHOD_FUNC(_to_image),0);
    rb_define_method(rb_cWXImage,"sub_image",RUBY_METHOD_FUNC(_getSubImage),1);
    rb_define_method(rb_cWXImage,"to_bitmap",RUBY_METHOD_FUNC(_to_bitmap),-1);

    rb_define_method(rb_cWXImage,"marshal_dump",RUBY_METHOD_FUNC(_marshal_dump),0);
    rb_define_method(rb_cWXImage,"marshal_load",RUBY_METHOD_FUNC(_marshal_load),1);

    rb_define_method(rb_cWXImage,"hash",RUBY_METHOD_FUNC(_getHash),0);

    rb_define_method(rb_cWXImage,"==",RUBY_METHOD_FUNC(_equal),1);
    rb_define_alias(rb_cWXImage,"eql?","==");

    rb_define_attr_method(rb_cWXImage,"mask",_getMask,_setMask);
#if wxUSE_PALETTE
    rb_define_attr_method(rb_cWXImage,"palette",_getPalette,_setPalette);
#endif

    rb_define_method(rb_cWXImage,"*",RUBY_METHOD_FUNC(_mal),1);

    rb_define_method(rb_cWXImage,"scale",RUBY_METHOD_FUNC(_scale),-1);
    rb_define_method(rb_cWXImage,"scale!",RUBY_METHOD_FUNC(_scale_self),-1);

    rb_define_method(rb_cWXImage,"resize",RUBY_METHOD_FUNC(_resize),-1);
    rb_define_method(rb_cWXImage,"resize!",RUBY_METHOD_FUNC(_resize_self),-1);

    rb_define_method(rb_cWXImage,"replace_color",RUBY_METHOD_FUNC(_replace_color),2);
    rb_define_method(rb_cWXImage,"replace_color!",RUBY_METHOD_FUNC(_replace_color_self),2);

    rb_define_method(rb_cWXImage,"rotate_hue",RUBY_METHOD_FUNC(_rotate_hue),1);
    rb_define_method(rb_cWXImage,"rotate_hue!",RUBY_METHOD_FUNC(_rotate_hue_self),1);

    rb_define_method(rb_cWXImage,"blur",RUBY_METHOD_FUNC(_Blur),1);
    rb_define_method(rb_cWXImage,"blur_horizontal",RUBY_METHOD_FUNC(_BlurHorizontal),1);
    rb_define_method(rb_cWXImage,"blur_vertical",RUBY_METHOD_FUNC(_BlurVertical),1);

    rb_define_method(rb_cWXImage,"blur!",RUBY_METHOD_FUNC(_Blur_self),1);
    rb_define_method(rb_cWXImage,"blur_horizontal!",RUBY_METHOD_FUNC(_BlurHorizontal_self),1);
    rb_define_method(rb_cWXImage,"blur_vertical!",RUBY_METHOD_FUNC(_BlurVertical_self),1);

    rb_define_method(rb_cWXImage,"convert_to_disabled",RUBY_METHOD_FUNC(_ConvertToDisabled),-1);
    rb_define_method(rb_cWXImage,"convert_to_mono",RUBY_METHOD_FUNC(_ConvertToMono),1);

    rb_define_method(rb_cWXImage,"convert_to_disabled!",RUBY_METHOD_FUNC(_ConvertToDisabled_self),-1);
    rb_define_method(rb_cWXImage,"convert_to_mono!",RUBY_METHOD_FUNC(_ConvertToMono_self),1);

    rb_define_method(rb_cWXImage,"[]",RUBY_METHOD_FUNC(_get),-1);
    rb_define_method(rb_cWXImage,"[]=",RUBY_METHOD_FUNC(_set),-1);

    rb_define_method(rb_cWXImage,"load",RUBY_METHOD_FUNC(_load),-1);
    rb_define_method(rb_cWXImage,"save",RUBY_METHOD_FUNC(_save),-1);

    registerInfo<wxImage>(rb_cWXImage, true);
#endif

}