/**************************************************************************//** * Contains Pixel class methods. * * Copyright © 2002 - 2009 by Timothy P. Hunter * * Changes since Nov. 2009 copyright © by Benjamin Thomas and Omer Bar-or * * @file rmpixel.cpp * @version $Id: rmpixel.cpp,v 1.7 2009/12/21 10:34:58 baror Exp $ * @author Tim Hunter ******************************************************************************/ #include "rmagick.h" #if defined(IMAGEMAGICK_6) #define QueryColorname QueryMagickColorname #endif static VALUE color_arg_rescue(VALUE, VALUE ATTRIBUTE_UNUSED) ATTRIBUTE_NORETURN; static void Color_Name_to_PixelColor(PixelColor *, VALUE); static void Pixel_destroy(void *); static size_t Pixel_memsize(const void *); const rb_data_type_t rm_pixel_data_type = { "Magick::Pixel", { NULL, Pixel_destroy, Pixel_memsize, }, 0, 0, RUBY_TYPED_FROZEN_SHAREABLE, }; /** * Free the storage associated with a Pixel object. * * No Ruby usage (internal function) * * @param ptr the Pixel object to destroy */ static void Pixel_destroy(void *ptr) { Pixel *pixel = (Pixel *)ptr; xfree(pixel); } /** * Get Pixel object size. * * No Ruby usage (internal function) * * @param ptr pointer to the Pixel object */ static size_t Pixel_memsize(const void *ptr) { return sizeof(Pixel); } /** * Get Pixel red value. * * @return [Numeric] the red value */ VALUE Pixel_red(VALUE self) { IMPLEMENT_TYPED_ATTR_READER(Pixel, red, int, &rm_pixel_data_type); } /** * Get Pixel green value. * * @return [Numeric] the green value */ VALUE Pixel_green(VALUE self) { IMPLEMENT_TYPED_ATTR_READER(Pixel, green, int, &rm_pixel_data_type); } /** * Get Pixel blue value. * * @return [Numeric] the blue value */ VALUE Pixel_blue(VALUE self) { IMPLEMENT_TYPED_ATTR_READER(Pixel, blue, int, &rm_pixel_data_type); } /** * Get Pixel alpha value. * * @return [Numeric] the alpha value */ VALUE Pixel_alpha(VALUE self) { Pixel *pixel; TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); #if defined(IMAGEMAGICK_7) return C_int_to_R_int(pixel->alpha); #else return C_int_to_R_int(QuantumRange - pixel->opacity); #endif } /** * Set Pixel red value. * * - Pixel is Observable. Setters call {Magick::Pixel#changed}, * {Magick::Pixel#notify_observers} * - Setters return their argument values for backward compatibility to when * Pixel was a Struct class. * * @param v [Numeric] the red value * @return [Numeric] the given red value */ VALUE Pixel_red_eq(VALUE self, VALUE v) { Pixel *pixel; rb_check_frozen(self); TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); pixel->red = APP2QUANTUM(v); rb_funcall(self, rm_ID_changed, 0); rb_funcall(self, rm_ID_notify_observers, 1, self); return QUANTUM2NUM((pixel->red)); } /** * Set Pixel green value. * * - Pixel is Observable. Setters call {Magick::Pixel#changed}, * {Magick::Pixel#notify_observers} * - Setters return their argument values for backward compatibility to when * Pixel was a Struct class. * * @param v [Numeric] the green value * @return [Numeric] the given green value */ VALUE Pixel_green_eq(VALUE self, VALUE v) { Pixel *pixel; rb_check_frozen(self); TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); pixel->green = APP2QUANTUM(v); rb_funcall(self, rm_ID_changed, 0); rb_funcall(self, rm_ID_notify_observers, 1, self); return QUANTUM2NUM((pixel->green)); } /** * Set Pixel blue value. * * - Pixel is Observable. Setters call {Magick::Pixel#changed}, * {Magick::Pixel#notify_observers} * - Setters return their argument values for backward compatibility to when * Pixel was a Struct class. * * @param v [Numeric] the blue value * @return [Numeric] the given blue value */ VALUE Pixel_blue_eq(VALUE self, VALUE v) { Pixel *pixel; rb_check_frozen(self); TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); pixel->blue = APP2QUANTUM(v); rb_funcall(self, rm_ID_changed, 0); rb_funcall(self, rm_ID_notify_observers, 1, self); return QUANTUM2NUM((pixel->blue)); } /** * Set Pixel alpha value. * * - Pixel is Observable. Setters call {Magick::Pixel#changed}, * {Magick::Pixel#notify_observers} * - Setters return their argument values for backward compatibility to when * Pixel was a Struct class. * * @param v [Numeric] the alpha value * @return [Numeric] the given alpha value */ VALUE Pixel_alpha_eq(VALUE self, VALUE v) { Pixel *pixel; rb_check_frozen(self); TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); #if defined(IMAGEMAGICK_7) pixel->alpha = APP2QUANTUM(v); rb_funcall(self, rm_ID_changed, 0); rb_funcall(self, rm_ID_notify_observers, 1, self); return QUANTUM2NUM(pixel->alpha); #else pixel->opacity = QuantumRange - APP2QUANTUM(v); rb_funcall(self, rm_ID_changed, 0); rb_funcall(self, rm_ID_notify_observers, 1, self); return QUANTUM2NUM(QuantumRange - pixel->opacity); #endif } /** * Get Pixel cyan value. * * @return [Numeric] the cyan value */ VALUE Pixel_cyan(VALUE self) { Pixel *pixel; TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); return INT2NUM(pixel->red); } /** * Set Pixel cyan value. * * - Pixel is Observable. Setters call {Magick::Pixel#changed}, * {Magick::Pixel#notify_observers} * - Setters return their argument values for backward compatibility to when * Pixel was a Struct class. * * @param v [Numeric] the cyan value * @return [Numeric] the given cyan value */ VALUE Pixel_cyan_eq(VALUE self, VALUE v) { Pixel *pixel; rb_check_frozen(self); TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); pixel->red = APP2QUANTUM(v); rb_funcall(self, rm_ID_changed, 0); rb_funcall(self, rm_ID_notify_observers, 1, self); return QUANTUM2NUM(pixel->red); } /** * Get Pixel magenta value. * * @return [Numeric] the magenta value */ VALUE Pixel_magenta(VALUE self) { Pixel *pixel; TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); return INT2NUM(pixel->green); } /** * Set Pixel magenta value. * * - Pixel is Observable. Setters call {Magick::Pixel#changed}, * {Magick::Pixel#notify_observers} * - Setters return their argument values for backward compatibility to when * Pixel was a Struct class. * * @param v [Numeric] the magenta value * @return [Numeric] the given magenta value */ VALUE Pixel_magenta_eq(VALUE self, VALUE v) { Pixel *pixel; rb_check_frozen(self); TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); pixel->green = APP2QUANTUM(v); rb_funcall(self, rm_ID_changed, 0); rb_funcall(self, rm_ID_notify_observers, 1, self); return QUANTUM2NUM(pixel->green); } /** * Get Pixel yellow value. * * @return [Numeric] the yellow value */ VALUE Pixel_yellow(VALUE self) { Pixel *pixel; TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); return INT2NUM(pixel->blue); } /** * Set Pixel yellow value. * * - Pixel is Observable. Setters call {Magick::Pixel#changed}, * {Magick::Pixel#notify_observers} * - Setters return their argument values for backward compatibility to when * Pixel was a Struct class. * * @param v [Numeric] the yellow value * @return [Numeric] the given yellow value */ VALUE Pixel_yellow_eq(VALUE self, VALUE v) { Pixel *pixel; rb_check_frozen(self); TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); pixel->blue = APP2QUANTUM(v); rb_funcall(self, rm_ID_changed, 0); rb_funcall(self, rm_ID_notify_observers, 1, self); return QUANTUM2NUM(pixel->blue); } /** * Get Pixel black value. * * @return [Numeric] the black value */ VALUE Pixel_black(VALUE self) { Pixel *pixel; TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); return INT2NUM(pixel->black); } /** * Set Pixel black value. * * - Pixel is Observable. Setters call {Magick::Pixel#changed}, * {Magick::Pixel#notify_observers} * - Setters return their argument values for backward compatibility to when * Pixel was a Struct class. * * @param v [Numeric] the black value * @return [Numeric] the given black value */ VALUE Pixel_black_eq(VALUE self, VALUE v) { Pixel *pixel; rb_check_frozen(self); TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); pixel->black = APP2QUANTUM(v); rb_funcall(self, rm_ID_changed, 0); rb_funcall(self, rm_ID_notify_observers, 1, self); return QUANTUM2NUM(pixel->black); } /** * Raise ArgumentError if the color name cannot be converted to a string via * rb_str_to_str. * * No Ruby usage (internal function) * * @param arg the argument to convert * @return 0 * @throw ArgumentError */ static VALUE color_arg_rescue(VALUE arg, VALUE raised_exc ATTRIBUTE_UNUSED) { rb_raise(rb_eTypeError, "argument must be color name or pixel (%s given)", rb_class2name(CLASS_OF(arg))); } /** * Convert either a String color name or a Magick::Pixel to a PixelColor. * * No Ruby usage (internal function) * * @param pp the PixelColor to modify * @param color the color name or Magick::Pixel */ void Color_to_PixelColor(PixelColor *pp, VALUE color) { Pixel *pixel; // Allow color name or Pixel if (CLASS_OF(color) == Class_Pixel) { memset(pp, 0, sizeof(*pp)); TypedData_Get_Struct(color, Pixel, &rm_pixel_data_type, pixel); pp->red = pixel->red; pp->green = pixel->green; pp->blue = pixel->blue; #if defined(IMAGEMAGICK_7) pp->black = pixel->black; rm_set_pixelinfo_alpha(pp, pixel->alpha); #else pp->opacity = pixel->opacity; #endif } else { // require 'to_str' here instead of just 'to_s'. color = rb_rescue(RESCUE_FUNC(rb_str_to_str), color, RESCUE_EXCEPTION_HANDLER_FUNC(color_arg_rescue), color); Color_Name_to_PixelColor(pp, color); } } /** * Convert either a String color name or a {Magick::Pixel} to a Pixel. * * No Ruby usage (internal function) * * @param pp the Pixel to modify * @param color the color name or Magick::Pixel */ void Color_to_Pixel(Pixel *pp, VALUE color) { PixelColor pixel_color; memset(pp, 0, sizeof(*pp)); // Allow color name or Pixel if (CLASS_OF(color) == Class_Pixel) { Pixel *pixel; TypedData_Get_Struct(color, Pixel, &rm_pixel_data_type, pixel); memcpy(pp, pixel, sizeof(Pixel)); } else { Color_to_PixelColor(&pixel_color, color); pp->red = pixel_color.red; pp->green = pixel_color.green; pp->blue = pixel_color.blue; #if defined(IMAGEMAGICK_7) pp->alpha = pixel_color.alpha; pp->black = pixel_color.black; #else pp->opacity = pixel_color.opacity; #endif } } /** * Convert a color name to a PixelColor * * No Ruby usage (internal function) * * @param color the PixelColor to modify * @param name_arg the coor name * @throw ArgumentError */ static void Color_Name_to_PixelColor(PixelColor *color, VALUE name_arg) { MagickBooleanType okay; char *name; ExceptionInfo *exception; exception = AcquireExceptionInfo(); name = StringValueCStr(name_arg); okay = QueryColorCompliance(name, AllCompliance, color, exception); DestroyExceptionInfo(exception); if (!okay) { rb_raise(rb_eArgError, "invalid color name %s", name); } } /** * Allocate a Pixel object. * * @return [Magick::Pixel] a new Magick::Pixel object */ VALUE Pixel_alloc(VALUE klass) { Pixel *pixel; pixel = ALLOC(Pixel); memset(pixel, '\0', sizeof(Pixel)); return TypedData_Wrap_Struct(klass, &rm_pixel_data_type, pixel); } /** * "Case equal" operator for Pixel. * * @param other [Object] the other object * @return [Boolean] true or false */ VALUE Pixel_case_eq(VALUE self, VALUE other) { if (CLASS_OF(self) == CLASS_OF(other)) { Pixel *self_pixel, *other_pixel; TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, self_pixel); TypedData_Get_Struct(other, Pixel, &rm_pixel_data_type, other_pixel); return (self_pixel->red == other_pixel->red && self_pixel->blue == other_pixel->blue && self_pixel->green == other_pixel->green #if defined(IMAGEMAGICK_7) && self_pixel->alpha == other_pixel->alpha) ? Qtrue : Qfalse; #else && self_pixel->opacity == other_pixel->opacity) ? Qtrue : Qfalse; #endif } return Qfalse; } /** * Clone a Pixel. * * @return [Magick::Pixel] a clone object * @see #dup * @see #initialize_copy */ VALUE Pixel_clone(VALUE self) { VALUE clone; clone = Pixel_dup(self); if (OBJ_FROZEN(self)) { OBJ_FREEZE(clone); } RB_GC_GUARD(clone); return clone; } /** * Duplicate a Pixel. * * @return [Magick::Pixel] a duplicated object * @see #clone * @see #initialize_copy */ VALUE Pixel_dup(VALUE self) { Pixel *pixel; VALUE dup; pixel = ALLOC(Pixel); memset(pixel, '\0', sizeof(Pixel)); dup = TypedData_Wrap_Struct(CLASS_OF(self), &rm_pixel_data_type, pixel); RB_GC_GUARD(dup); return rb_funcall(dup, rm_ID_initialize_copy, 1, self); } /** * Equality. Returns true only if receiver and other are the same object. * * @param other [Object] the other object * @return [Boolean] true if other is the same value, otherwise false */ VALUE Pixel_eql_q(VALUE self, VALUE other) { return NUM2INT(Pixel_spaceship(self, other)) == 0 ? Qtrue : Qfalse; } /** * Compare pixel values for equality. * * @overload fcmp(other, fuzz = 0.0, colorspace = Magick::RGBColorspace) * @param other [Magick::Pixel, String] The pixel to which the receiver is compared * @param fuzz [Numeric] The amount of fuzz to allow before the colors are considered to be different * @param colorspace [Magick::ColorspaceType] The colorspace * @return [Boolean] true if equal, otherwise false */ VALUE Pixel_fcmp(int argc, VALUE *argv, VALUE self) { double fuzz = 0.0; unsigned int equal; ColorspaceType colorspace = RGBColorspace; PixelColor self_pixel_color, other_pixel_color; #if defined(IMAGEMAGICK_6) Image *image; Info *info; #endif switch (argc) { case 3: VALUE_TO_ENUM(argv[2], colorspace, ColorspaceType); case 2: fuzz = NUM2DBL(argv[1]); case 1: // Allow 1 argument break; default: rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 to 3)", argc); break; } Color_to_PixelColor(&self_pixel_color, self); Color_to_PixelColor(&other_pixel_color, argv[0]); #if defined(IMAGEMAGICK_7) self_pixel_color.fuzz = fuzz; self_pixel_color.colorspace = colorspace; other_pixel_color.fuzz = fuzz; other_pixel_color.colorspace = colorspace; equal = IsFuzzyEquivalencePixelInfo(&self_pixel_color, &other_pixel_color); #else // The IsColorSimilar function expects to get the // colorspace and fuzz parameters from an Image structure. info = CloneImageInfo(NULL); if (!info) { rb_raise(rb_eNoMemError, "not enough memory to continue"); } image = rm_acquire_image(info); // Delete Info now in case we have to raise an exception DestroyImageInfo(info); if (!image) { rb_raise(rb_eNoMemError, "not enough memory to continue"); } image->colorspace = colorspace; image->fuzz = fuzz; equal = IsColorSimilar(image, &self_pixel_color, &other_pixel_color); DestroyImage(image); #endif return equal ? Qtrue : Qfalse; } /** * Construct an {Magick::Pixel} corresponding to the given color name. * * - The "inverse" is {Image#to_color}, b/c the conversion of a pixel to a * color name requires both a color depth and if the opacity value has * meaning. * * @param name [String] the color name * @return [Magick::Pixel] a new Magic::Pixel object * @see Magick::Image#to_color * @see Magick::Pixel#to_color */ VALUE Pixel_from_color(VALUE klass ATTRIBUTE_UNUSED, VALUE name) { PixelColor pp; ExceptionInfo *exception; MagickBooleanType okay; exception = AcquireExceptionInfo(); okay = QueryColorCompliance(StringValueCStr(name), AllCompliance, &pp, exception); CHECK_EXCEPTION(); DestroyExceptionInfo(exception); if (!okay) { rb_raise(rb_eArgError, "invalid color name: %s", StringValueCStr(name)); } return Pixel_from_PixelColor(&pp); } /** * Construct an RGB pixel. * * - 0 <= +hue+ < 360 OR "0%" <= +hue+ < "100%" * - 0 <= +saturation+ <= 255 OR "0%" <= +saturation+ <= "100%" * - 0 <= +lightness+ <= 255 OR "0%" <= +lightness+ <= "100%" * - 0 <= +alpha+ <= 1 (0 is transparent, 1 is opaque) OR "0%" <= +alpha+ <= "100%" * * @overload from_hsla(hue, saturation, lightness, alpha = 1.0) * @param hue [Numeric, String] A value in the range. * @param saturation [Numeric, String] A value in the range. * @param lightness [Numeric, String] A value in the range. * @param alpha [Numeric, String] The alpha value. * @return [Magick::Pixel] a new Magick::Pixel object */ VALUE Pixel_from_hsla(int argc, VALUE *argv, VALUE klass ATTRIBUTE_UNUSED) { double h, s, l, a = 1.0; MagickPixel pp; ExceptionInfo *exception; char name[50]; MagickBooleanType alpha = MagickFalse; switch (argc) { case 4: a = rm_percentage(argv[3], 1.0); alpha = MagickTrue; case 3: // saturation and lightness are out of 255 in new ImageMagicks and // out of 100 in old ImageMagicks. Compromise: always use %. l = rm_percentage(argv[2], 255.0); s = rm_percentage(argv[1], 255.0); h = rm_percentage(argv[0], 360.0); break; default: rb_raise(rb_eArgError, "wrong number of arguments (%d for 3 or 4)", argc); break; } if (alpha && (a < 0.0 || a > 1.0)) { rb_raise(rb_eRangeError, "alpha %g out of range [0.0, 1.0]", a); } if (l < 0.0 || l > 255.0) { rb_raise(rb_eRangeError, "lightness %g out of range [0.0, 255.0]", l); } if (s < 0.0 || s > 255.0) { rb_raise(rb_eRangeError, "saturation %g out of range [0.0, 255.0]", s); } if (h < 0.0 || h >= 360.0) { rb_raise(rb_eRangeError, "hue %g out of range [0.0, 360.0)", h); } memset(name, 0, sizeof(name)); if (alpha) { snprintf(name, sizeof(name), "hsla(%-2.1f,%-2.1f,%-2.1f,%-2.1f)", h, s, l, a); } else { snprintf(name, sizeof(name), "hsl(%-2.1f,%-2.1f,%-2.1f)", h, s, l); } exception = AcquireExceptionInfo(); #if defined(IMAGEMAGICK_7) QueryColorCompliance(name, AllCompliance, &pp, exception); #else QueryMagickColor(name, &pp, exception); #endif CHECK_EXCEPTION(); DestroyExceptionInfo(exception); return Pixel_from_MagickPixel(&pp); } /** * Create a Magick::Pixel object from a MagickPixel structure. * * No Ruby usage (internal function) * * Notes: * - Bypasses normal Pixel.new, Pixel#initialize methods * * @param pp the MagickPixel * @return a new Magick::Pixel object */ VALUE Pixel_from_MagickPixel(const MagickPixel *pp) { Pixel *pixel; pixel = ALLOC(Pixel); pixel->red = pp->red; pixel->green = pp->green; pixel->blue = pp->blue; #if defined(IMAGEMAGICK_7) pixel->alpha = pp->alpha; #else pixel->opacity = pp->opacity; #endif pixel->black = pp->index; return TypedData_Wrap_Struct(Class_Pixel, &rm_pixel_data_type, pixel); } /** * Create a Magick::Pixel object from a PixelPacket structure. * * No Ruby usage (internal function) * * Notes: * - Bypasses normal Pixel.new, Pixel#initialize methods * * @param pp the PixelPacket * @return a new Magick::Pixel object */ VALUE Pixel_from_PixelPacket(const PixelPacket *pp) { Pixel *pixel; pixel = ALLOC(Pixel); pixel->red = pp->red; pixel->green = pp->green; pixel->blue = pp->blue; #if defined(IMAGEMAGICK_7) pixel->alpha = pp->alpha; pixel->black = pp->black; #else pixel->opacity = pp->opacity; pixel->black = 0; #endif return TypedData_Wrap_Struct(Class_Pixel, &rm_pixel_data_type, pixel); } /** * Create a Magick::Pixel object from a PixelColor structure. * * No Ruby usage (internal function) * * Notes: * - Bypasses normal Pixel.new, Pixel#initialize methods * * @param pp the PixelColor * @return a new Magick::Pixel object */ VALUE Pixel_from_PixelColor(const PixelColor *pp) { Pixel *pixel; pixel = ALLOC(Pixel); pixel->red = pp->red; pixel->green = pp->green; pixel->blue = pp->blue; #if defined(IMAGEMAGICK_7) pixel->alpha = pp->alpha; pixel->black = pp->black; #else pixel->opacity = pp->opacity; pixel->black = 0; #endif return TypedData_Wrap_Struct(Class_Pixel, &rm_pixel_data_type, pixel); } /** * Compute a hash-code. * * @return [Numeric] the hash of self */ VALUE Pixel_hash(VALUE self) { Pixel *pixel; unsigned int hash; TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); hash = ScaleQuantumToChar(pixel->red) << 24; hash += ScaleQuantumToChar(pixel->green) << 16; hash += ScaleQuantumToChar(pixel->blue) << 8; #if defined(IMAGEMAGICK_7) hash += ScaleQuantumToChar(pixel->alpha); #else hash += ScaleQuantumToChar(QuantumRange - pixel->opacity); #endif return UINT2NUM(hash >> 1); } /** * Initialize clone, dup methods. * * @param orig [Magick::Pixel] the original Pixel * @return [Magick::Pixel] self * @see #clone * @see #dup */ VALUE Pixel_init_copy(VALUE self, VALUE orig) { Pixel *copy, *original; TypedData_Get_Struct(orig, Pixel, &rm_pixel_data_type, original); TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, copy); *copy = *original; return self; } /** * Initialize Pixel object. * * @overload initialize(red = 0, green = 0, blue = 0, opacity = 0) * @param red [Numeric] The red value * @param green [Numeric] The green value * @param blue [Numeric] The blue value * @param opacity [Numeric] The opacity value * @return [Magick::Pixel] self */ VALUE Pixel_initialize(int argc, VALUE *argv, VALUE self) { Pixel *pixel; TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); #if defined(IMAGEMAGICK_7) pixel->alpha = OpaqueAlpha; #endif switch(argc) { case 4: #if defined(IMAGEMAGICK_7) if (argv[3] != Qnil) { pixel->alpha = APP2QUANTUM(argv[3]); } #else if (argv[3] != Qnil) { pixel->opacity = APP2QUANTUM(argv[3]); } #endif case 3: if (argv[2] != Qnil) { pixel->blue = APP2QUANTUM(argv[2]); } case 2: if (argv[1] != Qnil) { pixel->green = APP2QUANTUM(argv[1]); } case 1: if (argv[0] != Qnil) { pixel->red = APP2QUANTUM(argv[0]); } case 0: break; default: rb_raise(rb_eArgError, "wrong number of arguments (%d for 0 to 4)", argc); } return self; } /** * Return the "intensity" of a pixel. * * @return [Numeric] the intensity */ VALUE Pixel_intensity(VALUE self) { Pixel *pixel; Quantum intensity; TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); intensity = ROUND_TO_QUANTUM((0.299*pixel->red) + (0.587*pixel->green) + (0.114*pixel->blue)); return QUANTUM2NUM((unsigned long) intensity); } /** * Support Marshal.dump. * * @return [Hash] a representing the dumped pixel */ VALUE Pixel_marshal_dump(VALUE self) { Pixel *pixel; VALUE dpixel; TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); dpixel = rb_hash_new(); rb_hash_aset(dpixel, CSTR2SYM("red"), QUANTUM2NUM(pixel->red)); rb_hash_aset(dpixel, CSTR2SYM("green"), QUANTUM2NUM(pixel->green)); rb_hash_aset(dpixel, CSTR2SYM("blue"), QUANTUM2NUM(pixel->blue)); #if defined(IMAGEMAGICK_7) rb_hash_aset(dpixel, CSTR2SYM("alpha"), QUANTUM2NUM(pixel->alpha)); #else rb_hash_aset(dpixel, CSTR2SYM("opacity"), QUANTUM2NUM(pixel->opacity)); #endif RB_GC_GUARD(dpixel); return dpixel; } /** * Support Marshal.load. * * @param dpixel [Hash] the dumped pixel * @return self */ VALUE Pixel_marshal_load(VALUE self, VALUE dpixel) { Pixel *pixel; TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); pixel->red = NUM2QUANTUM(rb_hash_aref(dpixel, CSTR2SYM("red"))); pixel->green = NUM2QUANTUM(rb_hash_aref(dpixel, CSTR2SYM("green"))); pixel->blue = NUM2QUANTUM(rb_hash_aref(dpixel, CSTR2SYM("blue"))); #if defined(IMAGEMAGICK_7) pixel->alpha = NUM2QUANTUM(rb_hash_aref(dpixel, CSTR2SYM("alpha"))); #else pixel->opacity = NUM2QUANTUM(rb_hash_aref(dpixel, CSTR2SYM("opacity"))); #endif return self; } /** * Support Comparable mixin. * * @param other [Object] the other Pixel * @return [-1, 0, 1, nil] the result of compare */ VALUE Pixel_spaceship(VALUE self, VALUE other) { Pixel *self_pixel, *other_pixel; if (CLASS_OF(self) != CLASS_OF(other)) { return Qnil; } TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, self_pixel); TypedData_Get_Struct(other, Pixel, &rm_pixel_data_type, other_pixel); if (self_pixel->red != other_pixel->red) { return INT2NUM((self_pixel->red - other_pixel->red)/abs((int)(self_pixel->red - other_pixel->red))); } else if(self_pixel->green != other_pixel->green) { return INT2NUM((self_pixel->green - other_pixel->green)/abs((int)(self_pixel->green - other_pixel->green))); } else if(self_pixel->blue != other_pixel->blue) { return INT2NUM((self_pixel->blue - other_pixel->blue)/abs((int)(self_pixel->blue - other_pixel->blue))); } #if defined(IMAGEMAGICK_7) else if(self_pixel->alpha != other_pixel->alpha) { return INT2NUM((self_pixel->alpha - other_pixel->alpha)/abs((int)(self_pixel->alpha - other_pixel->alpha))); } #else else if(self_pixel->opacity != other_pixel->opacity) { return INT2NUM(((QuantumRange - self_pixel->opacity) - (QuantumRange - other_pixel->opacity))/abs((int)((QuantumRange - self_pixel->opacity) - (QuantumRange - other_pixel->opacity)))); } #endif // Values are equal. return INT2NUM(0); } /** * Return [+hue+, +saturation+, +lightness+, +alpha+] in the same ranges as * {Magick::Pixel.from_hsla}. * * @return [Array] an array with hsla data * @see Pixel.from_hsla */ VALUE Pixel_to_hsla(VALUE self) { double hue, sat, lum, alpha; Pixel *pixel; VALUE hsla; TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); ConvertRGBToHSL(pixel->red, pixel->green, pixel->blue, &hue, &sat, &lum); hue *= 360.0; sat *= 255.0; lum *= 255.0; #if defined(IMAGEMAGICK_7) if (pixel->alpha == OpaqueAlpha) { alpha = 1.0; } else if (pixel->alpha == TransparentAlpha) { alpha = 0.0; } else { alpha = (double)(pixel->alpha) / (double)QuantumRange; } #else if (pixel->opacity == OpaqueOpacity) { alpha = 1.0; } else if (pixel->opacity == TransparentOpacity) { alpha = 0.0; } else { alpha = (double)(QuantumRange - pixel->opacity) / (double)QuantumRange; } #endif hsla = rb_ary_new3(4, rb_float_new(hue), rb_float_new(sat), rb_float_new(lum), rb_float_new(alpha)); RB_GC_GUARD(hsla); return hsla; } /** * Convert a Pixel to a MagickPixel. * * No Ruby usage (internal function) * * Notes: * - Same code as the private function SetMagickPixelPacket in ImageMagick. * * @param pixel the pixel * @param pp the MagickPixel to be modified */ static void rm_set_magick_pixel_packet(Pixel *pixel, MagickPixel *pp) { pp->red = (MagickRealType) pixel->red; pp->green = (MagickRealType) pixel->green; pp->blue = (MagickRealType) pixel->blue; #if defined(IMAGEMAGICK_7) pp->alpha = (MagickRealType) pixel->alpha; #else pp->opacity = (MagickRealType) pixel->opacity; #endif pp->index = (MagickRealType) 0.0; } /** * Return the color name corresponding to the pixel values. * * @overload to_color(compliance = Magick::AllCompliance, alpha = false, depth = Magick::MAGICKCORE_QUANTUM_DEPTH, hex = false) * @param compliance [Magick::ComplianceType] A ComplianceType constant * @param alpha [Boolean] If false, the pixel's alpha attribute is ignored * @param depth [Numeric] An image depth * @param hex [Boolean] If true, represent the color name in hex format * @return [String] the color name as a String */ VALUE Pixel_to_color(int argc, VALUE *argv, VALUE self) { Info *info; Image *image; Pixel *pixel; MagickPixel mpp; MagickBooleanType hex = MagickFalse; char name[MaxTextExtent]; ExceptionInfo *exception; ComplianceType compliance = AllCompliance; MagickBooleanType alpha = MagickFalse; unsigned int depth = MAGICKCORE_QUANTUM_DEPTH; switch (argc) { case 4: hex = (MagickBooleanType)RTEST(argv[3]); case 3: depth = NUM2UINT(argv[2]); // Ensure depth is appropriate for the way xMagick was compiled. switch (depth) { case 8: #if MAGICKCORE_QUANTUM_DEPTH == 16 || MAGICKCORE_QUANTUM_DEPTH == 32 case 16: #endif #if MAGICKCORE_QUANTUM_DEPTH == 32 case 32: #endif break; default: rb_raise(rb_eArgError, "invalid depth (%d)", depth); break; } case 2: alpha = (MagickBooleanType)RTEST(argv[1]); case 1: VALUE_TO_ENUM(argv[0], compliance, ComplianceType); case 0: break; default: rb_raise(rb_eArgError, "wrong number of arguments (%d for 0 to 2)", argc); } TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); info = CloneImageInfo(NULL); image = rm_acquire_image(info); DestroyImageInfo(info); if (!image) { rb_raise(rb_eNoMemError, "not enough memory to continue."); } exception = AcquireExceptionInfo(); image->depth = depth; #if defined(IMAGEMAGICK_7) if (alpha) { image->alpha_trait = BlendPixelTrait; } #else image->matte = alpha; #endif rm_init_magickpixel(image, &mpp); rm_set_magick_pixel_packet(pixel, &mpp); // Support for hex-format color names moved out of QueryMagickColorname // in 6.4.1-9. The 'hex' argument was removed as well. if (hex) { if (compliance == XPMCompliance) { #if defined(IMAGEMAGICK_7) mpp.alpha_trait = UndefinedPixelTrait; #else mpp.matte = MagickFalse; #endif mpp.depth = (unsigned long) min(1.0 * image->depth, 16.0); } GetColorTuple(&mpp, MagickTrue, name); } else { QueryColorname(image, &mpp, compliance, name, exception); } DestroyImage(image); CHECK_EXCEPTION(); DestroyExceptionInfo(exception); // Always return a string, even if it's "" return rb_str_new2(name); } /** * Return a string representation of a {Magick::Pixel} object. * * @return [String] the string */ VALUE Pixel_to_s(VALUE self) { Pixel *pixel; char buff[100]; TypedData_Get_Struct(self, Pixel, &rm_pixel_data_type, pixel); snprintf(buff, sizeof(buff), "red=" QuantumFormat ", green=" QuantumFormat ", blue=" QuantumFormat ", alpha=" QuantumFormat, pixel->red, pixel->green, pixel->blue, #if defined(IMAGEMAGICK_7) pixel->alpha); #else (QuantumRange - pixel->opacity)); #endif return rb_str_new2(buff); }