diff --git a/CHANGELOG.md b/CHANGELOG.md index 686cbe9..4a26601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## master * improve NULL pointer handling [dloebl] +* improve GFlags argument handling [jcupitt] ## Version 2.2.4 (2025-06-05) diff --git a/lib/vips/gvalue.rb b/lib/vips/gvalue.rb index c6a1cb8..9b5340f 100644 --- a/lib/vips/gvalue.rb +++ b/lib/vips/gvalue.rb @@ -25,7 +25,7 @@ class GValue < FFI::ManagedStruct :data, [:ulong_long, 2] # convert an enum value (str/symb/int) into an int ready for libvips - def self.from_nick(gtype, value) + def self.enum_from_nick(gtype, value) value = value.to_s if value.is_a? Symbol if value.is_a? String @@ -40,6 +40,43 @@ def self.from_nick(gtype, value) value end + # compatibility ... we used to call it this, perhaps someone has used this + # internal method + def self.from_nick(gtype, value) + GValue.enum_from_nick(gtype, value) + end + + # convert an flags value (array[str/symb/int] | str/symb/int) into an + # int ready for libvips + def self.flags_from_nick(gtype, value) + if value.is_a? String + # libvips will parse strings like "sub|up" etc. + name = value.tr("_", "-") + value = Vips.vips_flags_from_nick "ruby-vips", gtype, name + else + value = [value] if !value.is_a? Array + + # convert each item to a set of bits, OR them together + result = 0 + value.map do |item| + item = item.to_s if item.is_a? Symbol + if item.is_a? String + name = item.tr("_", "-") + item = Vips.vips_flags_from_nick "ruby-vips", gtype, name + if item == -1 + raise Vips::Error + end + end + + result |= item + end + + value = result + end + + value + end + # convert an int enum back into a symbol def self.to_nick(gtype, enum_value) enum_name = Vips.vips_enum_nick gtype, enum_value @@ -148,10 +185,11 @@ def set value else case fundamental when GFLAGS_TYPE - ::GObject.g_value_set_flags self, value + flags_value = GValue.flags_from_nick(self[:gtype], value) + ::GObject.g_value_set_flags self, flags_value when GENUM_TYPE - enum_value = GValue.from_nick(self[:gtype], value) + enum_value = GValue.enum_from_nick(self[:gtype], value) ::GObject.g_value_set_enum self, enum_value when GOBJECT_TYPE diff --git a/lib/vips/image.rb b/lib/vips/image.rb index 9ee303a..9a0df71 100644 --- a/lib/vips/image.rb +++ b/lib/vips/image.rb @@ -114,12 +114,12 @@ def self.smap x, &block end def self.complex? format - format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format + format_number = GObject::GValue.enum_from_nick BAND_FORMAT_TYPE, format Vips.vips_band_format_iscomplex(format_number) != 0 end def self.float? format - format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format + format_number = GObject::GValue.enum_from_nick BAND_FORMAT_TYPE, format Vips.vips_band_format_isfloat(format_number) != 0 end @@ -380,7 +380,7 @@ def self.new_from_memory data, width, height, bands, format size = data.bytesize end - format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format + format_number = GObject::GValue.enum_from_nick BAND_FORMAT_TYPE, format vi = Vips.vips_image_new_from_memory data, size, width, height, bands, format_number raise Vips::Error if vi.null? @@ -405,7 +405,7 @@ def self.new_from_memory data, width, height, bands, format # @param format [Symbol] band format # @return [Image] the loaded image def self.new_from_memory_copy data, width, height, bands, format - format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format + format_number = GObject::GValue.enum_from_nick BAND_FORMAT_TYPE, format if data.is_a?(FFI::Pointer) if data.size == UNKNOWN_POINTER_SIZE @@ -1319,7 +1319,7 @@ def composite overlay, mode, **opts end mode = mode.map do |x| - GObject::GValue.from_nick Vips::BLEND_MODE_TYPE, x + GObject::GValue.enum_from_nick Vips::BLEND_MODE_TYPE, x end Vips::Image.composite([self] + overlay, mode, **opts) diff --git a/lib/vips/object.rb b/lib/vips/object.rb index 9541c6f..4ac23ab 100644 --- a/lib/vips/object.rb +++ b/lib/vips/object.rb @@ -125,6 +125,7 @@ class Progress < FFI::Struct attach_function :vips_enum_from_nick, [:string, :GType, :string], :int attach_function :vips_enum_nick, [:GType, :int], :string + attach_function :vips_flags_from_nick, [:string, :GType, :string], :int attach_function :vips_value_set_ref_string, [GObject::GValue.ptr, :string], :void diff --git a/spec/vips_spec.rb b/spec/vips_spec.rb index 3b8c407..c22f924 100644 --- a/spec/vips_spec.rb +++ b/spec/vips_spec.rb @@ -166,6 +166,35 @@ expect(rot.bands).to eq(1) end + it "can handle symbol flag arguments" do + black = Vips::Operation.call "black", [200, 200] + bytes = Vips::Operation.call "pngsave_buffer", [black], keep: :icc + + expect(bytes.length).to be > 100 + end + + it "can handle int flag arguments" do + black = Vips::Operation.call "black", [200, 200] + bytes = Vips::Operation.call "pngsave_buffer", [black], keep: 1 << 3 + + expect(bytes.length).to be > 100 + end + + it "can handle string flag arguments" do + black = Vips::Operation.call "black", [200, 200] + bytes = Vips::Operation.call "pngsave_buffer", [black], keep: "icc" + + expect(bytes.length).to be > 100 + end + + it "can handle array flag arguments" do + black = Vips::Operation.call "black", [200, 200] + bytes = Vips::Operation.call "pngsave_buffer", [black], + keep: [:icc, :xmp] + + expect(bytes.length).to be > 100 + end + it "can return optional output args" do point = Vips::Operation.call "black", [1, 1] test = Vips::Operation.call "embed", [point, 20, 10, 100, 100],