#include "rays/ruby/bitmap.h" #include "rays/ruby/color_space.h" #include "rays/ruby/color.h" #include "rays/ruby/font.h" #include "defs.h" RUCY_DEFINE_VALUE_FROM_TO(Rays::Bitmap) #define THIS to(self) #define CHECK RUCY_CHECK_OBJECT(Rays::Bitmap, self) static VALUE alloc(VALUE klass) { return new_type(klass); } static VALUE initialize(VALUE self) { RUCY_CHECK_OBJ(Rays::Bitmap, self); check_arg_count(__FILE__, __LINE__, "Bitmap#initialize", argc, 2, 3); *THIS = Rays::Bitmap( to(argv[0]), to(argv[1]), argc >= 3 ? to(argv[2]) : Rays::RGBA); return self; } static VALUE initialize_copy(VALUE self, VALUE obj) { RUCY_CHECK_OBJ(Rays::Bitmap, self); *THIS = to(obj).dup(); return self; } static VALUE width(VALUE self) { CHECK; return value(THIS->width()); } static VALUE height(VALUE self) { CHECK; return value(THIS->height()); } static VALUE color_space(VALUE self) { CHECK; return value(THIS->color_space()); } static void set_pixels (Rays::Bitmap* bmp, Value pixels) { int w = bmp->width(), h = bmp->height(); const auto& cs = bmp->color_space(); if (pixels.size() != (w * h * (cs.is_float() ? cs.Bpp() / cs.Bpc() : 1))) { argument_error( __FILE__, __LINE__, "The size of the pixel array does not match the size of the bitmap"); } const Value* array = pixels.as_array(); switch (cs.type()) { case Rays::GRAY_8: case Rays::ALPHA_8: for (int y = 0; y < h; ++y) { const Value* pa = &array[w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, ++pa, ++pb) *pb = to(*pa); } break; case Rays::GRAY_16: case Rays::ALPHA_16: for (int y = 0; y < h; ++y) { const Value* pa = &array[w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, ++pa, ++pb) *pb = to(*pa); } break; case Rays::GRAY_32: case Rays::ALPHA_32: for (int y = 0; y < h; ++y) { const Value* pa = &array[w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, ++pa, ++pb) *pb = to(*pa); } break; case Rays::GRAY_float: case Rays::ALPHA_float: for (int y = 0; y < h; ++y) { const Value* pa = &array[w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, ++pa, ++pb) *pb = to(*pa); } break; case Rays::RGB_888: for (int y = 0; y < h; ++y) { const Value* pa = &array[w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, ++pa, pb += 3) { uint32_t argb = to(*pa); pb[0] = (uint8_t) (argb >> 16 & 0xff); pb[1] = (uint8_t) (argb >> 8 & 0xff); pb[2] = (uint8_t) (argb >> 0 & 0xff); } } break; case Rays::RGBA_8888: case Rays::RGBX_8888: for (int y = 0; y < h; ++y) { const Value* pa = &array[w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, ++pa, pb += 4) { uint32_t argb = to(*pa); pb[0] = (uint8_t) (argb >> 16 & 0xff); pb[1] = (uint8_t) (argb >> 8 & 0xff); pb[2] = (uint8_t) (argb >> 0 & 0xff); pb[3] = (uint8_t) (argb >> 24 & 0xff); } } break; case Rays::ARGB_8888: case Rays::XRGB_8888: for (int y = 0; y < h; ++y) { const Value* pa = &array[w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, ++pa, pb += 4) { uint32_t argb = to(*pa); pb[0] = (uint8_t) (argb >> 24 & 0xff); pb[1] = (uint8_t) (argb >> 16 & 0xff); pb[2] = (uint8_t) (argb >> 8 & 0xff); pb[3] = (uint8_t) (argb >> 0 & 0xff); } } break; case Rays::BGR_888: for (int y = 0; y < h; ++y) { const Value* pa = &array[w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, ++pa, pb += 3) { uint32_t argb = to(*pa); pb[0] = (uint8_t) (argb >> 0 & 0xff); pb[1] = (uint8_t) (argb >> 8 & 0xff); pb[2] = (uint8_t) (argb >> 16 & 0xff); } } break; case Rays::BGRA_8888: case Rays::BGRX_8888: for (int y = 0; y < h; ++y) { const Value* pa = &array[w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, ++pa, pb += 4) { uint32_t argb = to(*pa); pb[0] = (uint8_t) (argb >> 0 & 0xff); pb[1] = (uint8_t) (argb >> 8 & 0xff); pb[2] = (uint8_t) (argb >> 16 & 0xff); pb[3] = (uint8_t) (argb >> 24 & 0xff); } } break; case Rays::ABGR_8888: case Rays::XBGR_8888: for (int y = 0; y < h; ++y) { const Value* pa = &array[w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, ++pa, pb += 4) { uint32_t argb = to(*pa); pb[0] = (uint8_t) (argb >> 24 & 0xff); pb[1] = (uint8_t) (argb >> 0 & 0xff); pb[2] = (uint8_t) (argb >> 8 & 0xff); pb[3] = (uint8_t) (argb >> 16 & 0xff); } } break; case Rays::RGB_float: for (int y = 0; y < h; ++y) { const Value* pa = &array[3 * w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, pa += 3, pb += 3) { pb[0] = to(pa[0]); pb[1] = to(pa[1]); pb[2] = to(pa[2]); } } break; case Rays::RGBA_float: for (int y = 0; y < h; ++y) { const Value* pa = &array[4 * w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, pa += 4, pb += 4) { pb[0] = to(pa[0]); pb[1] = to(pa[1]); pb[2] = to(pa[2]); pb[3] = to(pa[3]); } } break; case Rays::ARGB_float: for (int y = 0; y < h; ++y) { const Value* pa = &array[4 * w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, pa += 4, pb += 4) { pb[0] = to(pa[3]); pb[1] = to(pa[0]); pb[2] = to(pa[1]); pb[3] = to(pa[2]); } } break; case Rays::BGR_float: for (int y = 0; y < h; ++y) { const Value* pa = &array[3 * w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, pa += 3, pb += 3) { pb[0] = to(pa[2]); pb[1] = to(pa[1]); pb[2] = to(pa[0]); } } break; case Rays::BGRA_float: for (int y = 0; y < h; ++y) { const Value* pa = &array[4 * w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, pa += 4, pb += 4) { pb[0] = to(pa[2]); pb[1] = to(pa[1]); pb[2] = to(pa[0]); pb[3] = to(pa[3]); } } break; case Rays::ABGR_float: for (int y = 0; y < h; ++y) { const Value* pa = &array[4 * w * y]; auto* pb = bmp->at(0, y); for (int x = 0; x < w; ++x, pa += 4, pb += 4) { pb[0] = to(pa[3]); pb[1] = to(pa[2]); pb[2] = to(pa[1]); pb[3] = to(pa[0]); } } break; default: argument_error(__FILE__, __LINE__); } } static inline Value to_rgb_value (uint8_t r, uint8_t g, uint8_t b) { return value( ((uint) r) << 16 | ((uint) g) << 8 | ((uint) b)); } static inline Value to_argb_value (uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return value( ((uint) a) << 24 | ((uint) r) << 16 | ((uint) g) << 8 | ((uint) b)); } static void get_pixels (auto* pixels, const Rays::Bitmap& bmp) { int w = bmp.width(), h = bmp.height(); const auto& cs = bmp.color_space(); pixels->clear(); pixels->reserve(w * h * (cs.is_float() ? cs.Bpp() / cs.Bpc() : 1)); switch (cs.type()) { case Rays::GRAY_8: case Rays::ALPHA_8: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, ++p) pixels->push_back(value(*p)); } break; case Rays::GRAY_16: case Rays::ALPHA_16: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, ++p) pixels->push_back(value(*p)); } break; case Rays::GRAY_32: case Rays::ALPHA_32: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, ++p) pixels->push_back(value(*p)); } break; case Rays::GRAY_float: case Rays::ALPHA_float: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, ++p) pixels->push_back(value(*p)); } break; case Rays::RGB_888: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, p += 3) pixels->push_back(to_rgb_value(p[0], p[1], p[2])); } break; case Rays::RGBA_8888: case Rays::RGBX_8888: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, p += 4) pixels->push_back(to_argb_value(p[0], p[1], p[2], p[3])); } break; case Rays::ARGB_8888: case Rays::XRGB_8888: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, p += 4) pixels->push_back(to_argb_value(p[1], p[2], p[3], p[0])); } break; case Rays::BGR_888: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, p += 3) pixels->push_back(to_rgb_value(p[2], p[1], p[0])); } break; case Rays::BGRA_8888: case Rays::BGRX_8888: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, p += 4) pixels->push_back(to_argb_value(p[2], p[1], p[0], p[3])); } break; case Rays::ABGR_8888: case Rays::XBGR_8888: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, p += 4) pixels->push_back(to_argb_value(p[3], p[2], p[1], p[0])); } break; case Rays::RGB_float: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, p += 3) { pixels->push_back(value(p[0])); pixels->push_back(value(p[1])); pixels->push_back(value(p[2])); } } break; case Rays::RGBA_float: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, p += 4) { pixels->push_back(value(p[0])); pixels->push_back(value(p[1])); pixels->push_back(value(p[2])); pixels->push_back(value(p[3])); } } break; case Rays::ARGB_float: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, p += 4) { pixels->push_back(value(p[1])); pixels->push_back(value(p[2])); pixels->push_back(value(p[3])); pixels->push_back(value(p[0])); } } break; case Rays::BGR_float: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, p += 3) { pixels->push_back(value(p[2])); pixels->push_back(value(p[1])); pixels->push_back(value(p[0])); } } break; case Rays::BGRA_float: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, p += 4) { pixels->push_back(value(p[2])); pixels->push_back(value(p[1])); pixels->push_back(value(p[0])); pixels->push_back(value(p[3])); } } break; case Rays::ABGR_float: for (int y = 0; y < h; ++y) { const auto* p = bmp.at(0, y); for (int x = 0; x < w; ++x, p += 4) { pixels->push_back(value(p[3])); pixels->push_back(value(p[2])); pixels->push_back(value(p[1])); pixels->push_back(value(p[0])); } } break; default: argument_error(__FILE__, __LINE__); } } static VALUE set_pixels(VALUE self, VALUE pixels) { CHECK; if (sizeof(VALUE) <= 4) { not_implemented_error( __FILE__, __LINE__, "Bitmap#pixels=() does not support 32-bit platforms"); } set_pixels(THIS, pixels); return pixels; } static VALUE get_pixels(VALUE self) { CHECK; if (sizeof(VALUE) <= 4) { not_implemented_error( __FILE__, __LINE__, "Bitmap#pixels() does not support 32-bit platforms"); } std::vector pixels; get_pixels(&pixels, *THIS); return value(pixels.size(), (const Value*) &pixels[0]); } static VALUE set_at(VALUE self, VALUE x, VALUE y, VALUE color) { CHECK; bool is_array = color.is_array(); size_t argc = is_array ? color.size() : 1; const Value* argv = is_array ? color.as_array() : &color; to(argc, argv) .get(THIS->at(to(x), to(y)), THIS->color_space()); return color; } static VALUE get_at(VALUE self, VALUE x, VALUE y) { CHECK; int xx = to(x); int yy = to(y); return value(Rays::Color(THIS->at(xx, yy), THIS->color_space())); } static Class cBitmap; void Init_rays_bitmap () { Module mRays = rb_define_module("Rays"); cBitmap = rb_define_class_under(mRays, "Bitmap", rb_cObject); rb_define_alloc_func(cBitmap, alloc); rb_define_private_method(cBitmap, "initialize", RUBY_METHOD_FUNC(initialize), -1); rb_define_private_method(cBitmap, "initialize_copy", RUBY_METHOD_FUNC(initialize_copy), 1); rb_define_method(cBitmap, "width", RUBY_METHOD_FUNC(width), 0); rb_define_method(cBitmap, "height", RUBY_METHOD_FUNC(height), 0); rb_define_method(cBitmap, "color_space", RUBY_METHOD_FUNC(color_space), 0); rb_define_method(cBitmap, "pixels=", RUBY_METHOD_FUNC(set_pixels), 1); rb_define_method(cBitmap, "pixels", RUBY_METHOD_FUNC(get_pixels), 0); cBitmap.define_method("[]=", set_at); cBitmap.define_method("[]", get_at); } namespace Rays { Class bitmap_class () { return cBitmap; } }// Rays