-
Notifications
You must be signed in to change notification settings - Fork 60
/
Copy pathmutableimage.rb
180 lines (159 loc) · 5.65 KB
/
mutableimage.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# This module provides an interface to the vips image processing library
# via ruby-ffi.
#
# Author:: John Cupitt (mailto:jcupitt@gmail.com)
# License:: MIT
require "ffi"
require "forwardable"
module Vips
# This class represents a libvips image which can be modified. See
# {Vips::Image#mutate}.
class MutableImage < Vips::Object
extend Forwardable
alias_method :parent_get_typeof, :get_typeof
def_instance_delegators :@image, :width, :height, :bands, :format,
:interpretation, :filename, :xoffset, :yoffset, :xres, :yres, :size,
:get, :get_typeof, :get_fields
# layout is exactly as {Image} (since we are also wrapping a VipsImage
# object)
module MutableImageLayout
def self.included base
base.class_eval do
layout :parent, Vips::Object::Struct
# rest opaque
end
end
end
class Struct < Vips::Object::Struct
include MutableImageLayout
end
class ManagedStruct < Vips::Object::ManagedStruct
include MutableImageLayout
end
# Get the {Image} this {MutableImage} is modifying. Only use this once you
# have finished all modifications.
#
# This is for internal use only. See {Vips::Image#mutate} for the
# user-facing interface.
attr_reader :image
# Make a {MutableImage} from a regular {Image}.
#
# This is for internal use only. See {Vips::Image#mutate} for the
# user-facing interface.
def initialize(image)
# We take a copy of the regular Image to ensure we have an unshared
# (unique) object. We forward things like #width and #height to this, and
# it's the thing we return at the end of the mutate block.
copy_image = image.copy
# Use ptr since we need the raw unwrapped pointer inside the image ...
# and make the ref that gobject will unref when it finishes.
# See also the comment on set_type! before changing this.
pointer = copy_image.ptr
::GObject.g_object_ref pointer
super(pointer)
# and save the copy ready for when we finish mutating
@image = copy_image
end
def inspect
"#<MutableImage #{width}x#{height} #{format}, #{bands} bands, #{interpretation}>"
end
def respond_to? name, include_all = false
# To support keyword args, we need to tell Ruby that final image
# arguments cannot be hashes of keywords.
#
# https://makandracards.com/makandra/
# 36013-heads-up-ruby-implicitly-converts-a-hash-to-keyword-arguments
return false if name == :to_hash
super
end
def respond_to_missing? name, include_all = false
# Respond to all vips operations by nickname.
return true if Vips.type_find("VipsOperation", name.to_s) != 0
super
end
# Invoke a vips operation with {Vips::Operation#call}, using self as
# the first input argument. {Vips::Operation#call} will only allow
# operations that modify self when passed a {MutableImage}.
#
# @param name [String] vips operation to call
# @return result of vips operation
def method_missing name, *args, **options
Vips::Operation.call name.to_s, [self, *args], options
end
# Draw a point on an image.
#
# See {Image#draw_rect}.
def draw_point! ink, left, top, **opts
draw_rect! ink, left, top, 1, 1, **opts
end
# Create a metadata item on an image of the specifed type. Ruby types
# are automatically transformed into the matching glib type (eg.
# {GObject::GINT_TYPE}), if possible.
#
# For example, you can use this to set an image's ICC profile:
#
# ```ruby
# x.set_type! Vips::BLOB_TYPE, "icc-profile-data", profile
# ```
#
# where `profile` is an ICC profile held as a binary string object.
#
# @see set!
# @param gtype [Integer] GType of item
# @param name [String] Metadata field to set
# @param value [Object] Value to set
def set_type! gtype, name, value
gvalue = GObject::GValue.alloc
gvalue.init gtype
gvalue.set value
# libvips 8.9.1 had a terrible misfeature which would block metadata
# modification unless the object had a ref_count of 1. MutableImage
# will always have a ref_count of at least 2 (the parent gobject keeps a
# ref, and we keep a ref to the copy ready to return to our caller),
# so we must temporarily drop the refs to 1 around metadata changes.
#
# See https://github.com/libvips/ruby-vips/issues/291
begin
::GObject.g_object_unref ptr
Vips.vips_image_set self, name, gvalue
ensure
::GObject.g_object_ref ptr
end
gvalue.unset
end
# Set the value of a metadata item on an image. The metadata item must
# already exist. Ruby types are automatically transformed into the
# matching {GObject::GValue}, if possible.
#
# For example, you can use this to set an image's ICC profile:
#
# ```
# x.set! "icc-profile-data", profile
# ```
#
# where `profile` is an ICC profile held as a binary string object.
#
# @see set_type!
# @param name [String] Metadata field to set
# @param value [Object] Value to set
def set! name, value
set_type! get_typeof(name), name, value
end
# Remove a metadata item from an image.
#
# For example:
#
# ```
# x.remove! "icc-profile-data"
# ```
#
# @param name [String] Metadata field to remove
def remove! name
# See set_type! for an explanation. Image#remove can't throw an
# exception, so there's no need to ensure we unref.
::GObject.g_object_unref ptr
Vips.vips_image_remove self, name
::GObject.g_object_ref ptr
end
end
end