skia 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +12 -0
  3. data/LICENSE +21 -0
  4. data/README.md +205 -0
  5. data/Rakefile +8 -0
  6. data/examples/advanced_features.rb +50 -0
  7. data/examples/avatar_generator.rb +74 -0
  8. data/examples/bar_chart.rb +89 -0
  9. data/examples/basic_drawing.rb +43 -0
  10. data/examples/gradient.rb +32 -0
  11. data/examples/pdf_output.rb +48 -0
  12. data/examples/pdf_stream.rb +23 -0
  13. data/examples/picture_recording.rb +62 -0
  14. data/examples/progress_gauge.rb +103 -0
  15. data/examples/runtime_effect.rb +26 -0
  16. data/examples/social_card.rb +121 -0
  17. data/examples/text_drawing.rb +40 -0
  18. data/lib/skia/base.rb +40 -0
  19. data/lib/skia/bitmap.rb +140 -0
  20. data/lib/skia/canvas.rb +239 -0
  21. data/lib/skia/color.rb +82 -0
  22. data/lib/skia/color_filter.rb +23 -0
  23. data/lib/skia/color_space.rb +44 -0
  24. data/lib/skia/data.rb +47 -0
  25. data/lib/skia/document.rb +222 -0
  26. data/lib/skia/font.rb +118 -0
  27. data/lib/skia/image.rb +216 -0
  28. data/lib/skia/image_filter.rb +29 -0
  29. data/lib/skia/image_info.rb +59 -0
  30. data/lib/skia/mask_filter.rb +26 -0
  31. data/lib/skia/matrix.rb +163 -0
  32. data/lib/skia/native/callbacks.rb +6 -0
  33. data/lib/skia/native/functions.rb +384 -0
  34. data/lib/skia/native/types.rb +400 -0
  35. data/lib/skia/native.rb +67 -0
  36. data/lib/skia/paint.rb +144 -0
  37. data/lib/skia/path.rb +166 -0
  38. data/lib/skia/path_effect.rb +30 -0
  39. data/lib/skia/picture.rb +120 -0
  40. data/lib/skia/pixmap.rb +109 -0
  41. data/lib/skia/point.rb +94 -0
  42. data/lib/skia/rect.rb +179 -0
  43. data/lib/skia/rrect.rb +139 -0
  44. data/lib/skia/runtime_effect.rb +88 -0
  45. data/lib/skia/shader.rb +145 -0
  46. data/lib/skia/surface.rb +272 -0
  47. data/lib/skia/text_blob.rb +47 -0
  48. data/lib/skia/typeface.rb +175 -0
  49. data/lib/skia/version.rb +5 -0
  50. data/lib/skia.rb +42 -0
  51. data/skia-ruby.gemspec +30 -0
  52. metadata +107 -0
data/lib/skia/path.rb ADDED
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skia
4
+ class Path < Base
5
+ def initialize(ptr = nil)
6
+ super(ptr || Native.sk_path_new, :sk_path_delete)
7
+ end
8
+
9
+ def self.build(&block)
10
+ path = new
11
+ path.instance_eval(&block) if block
12
+ path
13
+ end
14
+
15
+ def move_to(x, y)
16
+ Native.sk_path_move_to(@ptr, x.to_f, y.to_f)
17
+ self
18
+ end
19
+
20
+ def line_to(x, y)
21
+ Native.sk_path_line_to(@ptr, x.to_f, y.to_f)
22
+ self
23
+ end
24
+
25
+ def quad_to(x1, y1, x2, y2)
26
+ Native.sk_path_quad_to(@ptr, x1.to_f, y1.to_f, x2.to_f, y2.to_f)
27
+ self
28
+ end
29
+
30
+ def conic_to(x1, y1, x2, y2, weight)
31
+ Native.sk_path_conic_to(@ptr, x1.to_f, y1.to_f, x2.to_f, y2.to_f, weight.to_f)
32
+ self
33
+ end
34
+
35
+ def cubic_to(x1, y1, x2, y2, x3, y3)
36
+ Native.sk_path_cubic_to(@ptr, x1.to_f, y1.to_f, x2.to_f, y2.to_f, x3.to_f, y3.to_f)
37
+ self
38
+ end
39
+
40
+ def arc_to(rx, ry, x_axis_rotate, x, y)
41
+ Native.sk_path_arc_to(@ptr, rx.to_f, ry.to_f, x_axis_rotate.to_f, x.to_f, y.to_f)
42
+ self
43
+ end
44
+
45
+ def arc_to_with_oval(oval, start_angle, sweep_angle, force_move_to = false)
46
+ rect_struct = oval.to_struct
47
+ Native.sk_path_arc_to_with_oval(@ptr, rect_struct, start_angle.to_f, sweep_angle.to_f, force_move_to)
48
+ self
49
+ end
50
+
51
+ def close
52
+ Native.sk_path_close(@ptr)
53
+ self
54
+ end
55
+
56
+ def add_rect(rect, direction = :cw)
57
+ rect_struct = rect.to_struct
58
+ Native.sk_path_add_rect(@ptr, rect_struct, direction)
59
+ self
60
+ end
61
+
62
+ def add_rrect(rrect, direction = :cw)
63
+ Native.sk_path_add_rrect(@ptr, rrect.ptr, direction)
64
+ self
65
+ end
66
+
67
+ def add_oval(rect, direction = :cw)
68
+ rect_struct = rect.to_struct
69
+ Native.sk_path_add_oval(@ptr, rect_struct, direction)
70
+ self
71
+ end
72
+
73
+ def add_circle(cx, cy, radius, direction = :cw)
74
+ Native.sk_path_add_circle(@ptr, cx.to_f, cy.to_f, radius.to_f, direction)
75
+ self
76
+ end
77
+
78
+ def add_arc(rect, start_angle, sweep_angle)
79
+ rect_struct = rect.to_struct
80
+ Native.sk_path_add_arc(@ptr, rect_struct, start_angle.to_f, sweep_angle.to_f)
81
+ self
82
+ end
83
+
84
+ def add_path(other, extend_path: false)
85
+ mode = extend_path ? 1 : 0
86
+ Native.sk_path_add_path(@ptr, other.ptr, mode, 0)
87
+ self
88
+ end
89
+
90
+ def add_path_offset(other, dx, dy, extend_path: false)
91
+ mode = extend_path ? 1 : 0
92
+ Native.sk_path_add_path_offset(@ptr, other.ptr, dx.to_f, dy.to_f, mode)
93
+ self
94
+ end
95
+
96
+ def add_path_reverse(other)
97
+ Native.sk_path_add_path_reverse(@ptr, other.ptr)
98
+ self
99
+ end
100
+
101
+ def add_path_matrix(other, matrix, extend_path: false)
102
+ mode = extend_path ? 1 : 0
103
+ matrix_struct = matrix.to_struct
104
+ Native.sk_path_add_path_matrix(@ptr, other.ptr, matrix_struct, mode)
105
+ self
106
+ end
107
+
108
+ def fill_type
109
+ Native.sk_path_get_filltype(@ptr)
110
+ end
111
+
112
+ def fill_type=(value)
113
+ Native.sk_path_set_filltype(@ptr, value)
114
+ end
115
+
116
+ def bounds
117
+ rect_struct = Native::SKRect.new
118
+ Native.sk_path_get_bounds(@ptr, rect_struct)
119
+ Rect.from_struct(rect_struct)
120
+ end
121
+
122
+ def empty?
123
+ Native.sk_path_count_verbs(@ptr) == 0
124
+ end
125
+
126
+ def count_points
127
+ Native.sk_path_count_points(@ptr)
128
+ end
129
+
130
+ def approximate_length(force_closed: false, res_scale: 1.0)
131
+ measure = Native.sk_pathmeasure_new_with_path(@ptr, force_closed, res_scale.to_f)
132
+ raise Error, 'Failed to create path measure' if measure.nil? || measure.null?
133
+
134
+ begin
135
+ Native.sk_pathmeasure_get_length(measure)
136
+ ensure
137
+ Native.sk_pathmeasure_destroy(measure)
138
+ end
139
+ end
140
+
141
+ def contains?(x, y)
142
+ Native.sk_path_contains(@ptr, x.to_f, y.to_f)
143
+ end
144
+
145
+ def transform(matrix)
146
+ matrix_struct = matrix.to_struct
147
+ Native.sk_path_transform(@ptr, matrix_struct)
148
+ self
149
+ end
150
+
151
+ def clone
152
+ self.class.new(Native.sk_path_clone(@ptr))
153
+ end
154
+
155
+ def reverse
156
+ reversed = self.class.new
157
+ Native.sk_path_add_path_reverse(reversed.ptr, @ptr)
158
+ reversed
159
+ end
160
+
161
+ def reset
162
+ Native.sk_path_reset(@ptr)
163
+ self
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skia
4
+ class PathEffect < Base
5
+ def initialize(ptr)
6
+ super(ptr, :sk_path_effect_unref)
7
+ end
8
+
9
+ def self.wrap(ptr)
10
+ return nil if ptr.nil? || ptr.null?
11
+
12
+ new(ptr)
13
+ end
14
+
15
+ def self.dash(intervals, phase: 0.0)
16
+ unless intervals.is_a?(Array) && intervals.length >= 2
17
+ raise ArgumentError, 'intervals must be an array with at least 2 elements'
18
+ end
19
+
20
+ interval_values = intervals.map(&:to_f)
21
+ intervals_ptr = FFI::MemoryPointer.new(:float, interval_values.length)
22
+ intervals_ptr.write_array_of_float(interval_values)
23
+
24
+ ptr = Native.sk_path_effect_create_dash(intervals_ptr, interval_values.length, phase.to_f)
25
+ raise Error, 'Failed to create dash path effect' if ptr.nil? || ptr.null?
26
+
27
+ new(ptr)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skia
4
+ class Picture < Base
5
+ def initialize(ptr)
6
+ super(ptr, :sk_picture_unref)
7
+ end
8
+
9
+ def self.from_data(data)
10
+ data_obj = data.is_a?(Data) ? data : Data.new(data)
11
+ ptr = Native.sk_picture_deserialize_from_data(data_obj.ptr)
12
+ raise Error, "Failed to deserialize picture" if ptr.nil? || ptr.null?
13
+
14
+ new(ptr)
15
+ end
16
+
17
+ def self.record(bounds, &block)
18
+ recorder = PictureRecorder.new
19
+ recorder.begin_recording(bounds, &block)
20
+ end
21
+
22
+ def unique_id
23
+ Native.sk_picture_get_unique_id(@ptr)
24
+ end
25
+
26
+ def cull_rect
27
+ rect_struct = Native::SKRect.new
28
+ Native.sk_picture_get_cull_rect(@ptr, rect_struct)
29
+ Rect.from_struct(rect_struct)
30
+ end
31
+
32
+ def playback(canvas)
33
+ Native.sk_picture_playback(@ptr, canvas.ptr)
34
+ self
35
+ end
36
+
37
+ def serialize
38
+ data_ptr = Native.sk_picture_serialize_to_data(@ptr)
39
+ raise Error, "Failed to serialize picture" if data_ptr.nil? || data_ptr.null?
40
+
41
+ Data.new(data_ptr)
42
+ end
43
+
44
+ def approximate_op_count(nested: true)
45
+ Native.sk_picture_approximate_op_count(@ptr, nested)
46
+ end
47
+
48
+ def approximate_bytes_used
49
+ Native.sk_picture_approximate_bytes_used(@ptr)
50
+ end
51
+
52
+ def save(path)
53
+ data = serialize
54
+ File.binwrite(path, data.to_s)
55
+ self
56
+ end
57
+
58
+ def self.load(path)
59
+ data = Data.from_file(path)
60
+ from_data(data)
61
+ end
62
+ end
63
+
64
+ class PictureRecorder
65
+ def initialize
66
+ @ptr = Native.sk_picture_recorder_new
67
+ raise Error, "Failed to create picture recorder" if @ptr.nil? || @ptr.null?
68
+
69
+ @recording = false
70
+ end
71
+
72
+ def ptr
73
+ @ptr
74
+ end
75
+
76
+ def begin_recording(bounds)
77
+ bounds_struct = bounds.to_struct
78
+ canvas_ptr = Native.sk_picture_recorder_begin_recording(@ptr, bounds_struct)
79
+ raise Error, "Failed to begin recording" if canvas_ptr.nil? || canvas_ptr.null?
80
+
81
+ @recording = true
82
+ canvas = Canvas.new(canvas_ptr)
83
+
84
+ if block_given?
85
+ yield canvas
86
+ end_recording
87
+ else
88
+ canvas
89
+ end
90
+ end
91
+
92
+ def recording_canvas
93
+ return nil unless @recording
94
+
95
+ canvas_ptr = Native.sk_picture_get_recording_canvas(@ptr)
96
+ return nil if canvas_ptr.nil? || canvas_ptr.null?
97
+
98
+ Canvas.new(canvas_ptr)
99
+ end
100
+
101
+ def end_recording
102
+ return nil unless @recording
103
+
104
+ ptr = Native.sk_picture_recorder_end_recording(@ptr)
105
+ @recording = false
106
+ raise Error, "Failed to end recording" if ptr.nil? || ptr.null?
107
+
108
+ Picture.new(ptr)
109
+ end
110
+
111
+ def recording?
112
+ @recording
113
+ end
114
+
115
+ def delete
116
+ Native.sk_picture_recorder_delete(@ptr) if @ptr
117
+ @ptr = nil
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skia
4
+ class Pixmap < Base
5
+ def initialize(ptr = nil)
6
+ super(ptr || Native.sk_pixmap_new, :sk_pixmap_destructor)
7
+ @pixel_storage = nil
8
+ end
9
+
10
+ def self.from_pixels(image_info, pixels, row_bytes: nil)
11
+ info = coerce_image_info(image_info)
12
+ bytes = pixels.is_a?(String) ? pixels.dup : pixels.pack('C*')
13
+ row_bytes ||= info.min_row_bytes
14
+
15
+ if bytes.bytesize < row_bytes * info.height
16
+ raise ArgumentError, "pixel buffer is too small: #{bytes.bytesize} bytes"
17
+ end
18
+
19
+ storage = FFI::MemoryPointer.new(:uint8, bytes.bytesize)
20
+ storage.write_bytes(bytes)
21
+
22
+ ptr = Native.sk_pixmap_new_with_params(info.to_struct, storage, row_bytes)
23
+ raise Error, 'Failed to create pixmap from pixels' if ptr.nil? || ptr.null?
24
+
25
+ pixmap = new(ptr)
26
+ pixmap.instance_variable_set(:@pixel_storage, storage)
27
+ pixmap
28
+ end
29
+
30
+ def info
31
+ info_struct = Native::SKImageInfo.new
32
+ Native.sk_pixmap_get_info(@ptr, info_struct)
33
+ ImageInfo.from_struct(info_struct)
34
+ end
35
+
36
+ def row_bytes
37
+ Native.sk_pixmap_get_row_bytes(@ptr)
38
+ end
39
+
40
+ def color_space
41
+ ColorSpace.wrap(Native.sk_pixmap_get_colorspace(@ptr), retain: true)
42
+ end
43
+
44
+ def color_space=(value)
45
+ unless value.nil? || value.is_a?(ColorSpace)
46
+ raise ArgumentError, 'color_space must be a Skia::ColorSpace or nil'
47
+ end
48
+
49
+ Native.sk_pixmap_set_colorspace(@ptr, value&.ptr)
50
+ end
51
+
52
+ def pixel_color(x, y)
53
+ Color.new(Native.sk_pixmap_get_pixel_color(@ptr, x.to_i, y.to_i))
54
+ end
55
+
56
+ def opaque?
57
+ Native.sk_pixmap_compute_is_opaque(@ptr)
58
+ end
59
+
60
+ def writable_addr(x = nil, y = nil)
61
+ if x.nil? || y.nil?
62
+ Native.sk_pixmap_get_writable_addr(@ptr)
63
+ else
64
+ Native.sk_pixmap_get_writeable_addr_with_xy(@ptr, x.to_i, y.to_i)
65
+ end
66
+ end
67
+
68
+ def read_pixels(dst_info, src_x: 0, src_y: 0, row_bytes: nil)
69
+ info = self.class.coerce_image_info(dst_info)
70
+ row_bytes ||= info.min_row_bytes
71
+ byte_size = row_bytes * info.height
72
+ pixels = FFI::MemoryPointer.new(:uint8, byte_size)
73
+
74
+ ok = Native.sk_pixmap_read_pixels(@ptr, info.to_struct, pixels, row_bytes, src_x.to_i, src_y.to_i)
75
+ raise Error, 'Failed to read pixels from pixmap' unless ok
76
+
77
+ pixels.read_bytes(byte_size)
78
+ end
79
+
80
+ def extract_subset(rect)
81
+ irect = coerce_irect(rect)
82
+ subset = self.class.new
83
+ success = Native.sk_pixmap_extract_subset(@ptr, subset.ptr, irect.to_struct)
84
+ return nil unless success
85
+
86
+ subset
87
+ end
88
+
89
+ def reset
90
+ Native.sk_pixmap_reset(@ptr)
91
+ @pixel_storage = nil
92
+ self
93
+ end
94
+
95
+ private
96
+
97
+ def self.coerce_image_info(image_info)
98
+ return image_info if image_info.is_a?(ImageInfo)
99
+
100
+ raise ArgumentError, 'image_info must be a Skia::ImageInfo'
101
+ end
102
+
103
+ def coerce_irect(rect)
104
+ return rect if rect.is_a?(IRect)
105
+
106
+ raise ArgumentError, 'rect must be a Skia::IRect'
107
+ end
108
+ end
109
+ end
data/lib/skia/point.rb ADDED
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skia
4
+ class Point
5
+ attr_accessor :x, :y
6
+
7
+ def initialize(x = 0.0, y = 0.0)
8
+ @x = x.to_f
9
+ @y = y.to_f
10
+ end
11
+
12
+ def self.from_struct(struct)
13
+ new(struct[:x], struct[:y])
14
+ end
15
+
16
+ def to_struct
17
+ struct = Native::SKPoint.new
18
+ struct[:x] = @x
19
+ struct[:y] = @y
20
+ struct
21
+ end
22
+
23
+ def +(other)
24
+ self.class.new(@x + other.x, @y + other.y)
25
+ end
26
+
27
+ def -(other)
28
+ self.class.new(@x - other.x, @y - other.y)
29
+ end
30
+
31
+ def *(other)
32
+ self.class.new(@x * other, @y * other)
33
+ end
34
+
35
+ def /(other)
36
+ self.class.new(@x / other, @y / other)
37
+ end
38
+
39
+ def -@
40
+ self.class.new(-@x, -@y)
41
+ end
42
+
43
+ def length
44
+ Math.sqrt(@x * @x + @y * @y)
45
+ end
46
+
47
+ def normalize
48
+ len = length
49
+ return self.class.new(0, 0) if len.zero?
50
+
51
+ self / len
52
+ end
53
+
54
+ def ==(other)
55
+ return false unless other.is_a?(Point)
56
+
57
+ @x == other.x && @y == other.y
58
+ end
59
+
60
+ def to_a
61
+ [@x, @y]
62
+ end
63
+ end
64
+
65
+ class IPoint
66
+ attr_accessor :x, :y
67
+
68
+ def initialize(x = 0, y = 0)
69
+ @x = x.to_i
70
+ @y = y.to_i
71
+ end
72
+
73
+ def self.from_struct(struct)
74
+ new(struct[:x], struct[:y])
75
+ end
76
+
77
+ def to_struct
78
+ struct = Native::SKIPoint.new
79
+ struct[:x] = @x
80
+ struct[:y] = @y
81
+ struct
82
+ end
83
+
84
+ def ==(other)
85
+ return false unless other.is_a?(IPoint)
86
+
87
+ @x == other.x && @y == other.y
88
+ end
89
+
90
+ def to_a
91
+ [@x, @y]
92
+ end
93
+ end
94
+ end
data/lib/skia/rect.rb ADDED
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skia
4
+ class Rect
5
+ attr_accessor :left, :top, :right, :bottom
6
+
7
+ def initialize(left = 0.0, top = 0.0, right = 0.0, bottom = 0.0)
8
+ @left = left.to_f
9
+ @top = top.to_f
10
+ @right = right.to_f
11
+ @bottom = bottom.to_f
12
+ end
13
+
14
+ def self.from_xywh(x, y, width, height)
15
+ new(x, y, x + width, y + height)
16
+ end
17
+
18
+ def self.from_wh(width, height)
19
+ new(0, 0, width, height)
20
+ end
21
+
22
+ def self.from_struct(struct)
23
+ new(struct[:left], struct[:top], struct[:right], struct[:bottom])
24
+ end
25
+
26
+ def to_struct
27
+ struct = Native::SKRect.new
28
+ struct[:left] = @left
29
+ struct[:top] = @top
30
+ struct[:right] = @right
31
+ struct[:bottom] = @bottom
32
+ struct
33
+ end
34
+
35
+ def width
36
+ @right - @left
37
+ end
38
+
39
+ def height
40
+ @bottom - @top
41
+ end
42
+
43
+ def center_x
44
+ (@left + @right) / 2.0
45
+ end
46
+
47
+ def center_y
48
+ (@top + @bottom) / 2.0
49
+ end
50
+
51
+ def center
52
+ Point.new(center_x, center_y)
53
+ end
54
+
55
+ def empty?
56
+ @left >= @right || @top >= @bottom
57
+ end
58
+
59
+ def contains?(x, y)
60
+ x >= @left && x < @right && y >= @top && y < @bottom
61
+ end
62
+
63
+ def contains_rect?(other)
64
+ @left <= other.left && @top <= other.top &&
65
+ @right >= other.right && @bottom >= other.bottom
66
+ end
67
+
68
+ def intersects?(other)
69
+ @left < other.right && other.left < @right &&
70
+ @top < other.bottom && other.top < @bottom
71
+ end
72
+
73
+ def intersect(other)
74
+ return nil unless intersects?(other)
75
+
76
+ self.class.new(
77
+ [@left, other.left].max,
78
+ [@top, other.top].min,
79
+ [@right, other.right].min,
80
+ [@bottom, other.bottom].max
81
+ )
82
+ end
83
+
84
+ def union(other)
85
+ self.class.new(
86
+ [@left, other.left].min,
87
+ [@top, other.top].min,
88
+ [@right, other.right].max,
89
+ [@bottom, other.bottom].max
90
+ )
91
+ end
92
+
93
+ def offset(dx, dy)
94
+ self.class.new(@left + dx, @top + dy, @right + dx, @bottom + dy)
95
+ end
96
+
97
+ def offset!(dx, dy)
98
+ @left += dx
99
+ @top += dy
100
+ @right += dx
101
+ @bottom += dy
102
+ self
103
+ end
104
+
105
+ def inset(dx, dy)
106
+ self.class.new(@left + dx, @top + dy, @right - dx, @bottom - dy)
107
+ end
108
+
109
+ def ==(other)
110
+ return false unless other.is_a?(Rect)
111
+
112
+ @left == other.left && @top == other.top &&
113
+ @right == other.right && @bottom == other.bottom
114
+ end
115
+
116
+ def to_a
117
+ [@left, @top, @right, @bottom]
118
+ end
119
+ end
120
+
121
+ class IRect
122
+ attr_accessor :left, :top, :right, :bottom
123
+
124
+ def initialize(left = 0, top = 0, right = 0, bottom = 0)
125
+ @left = left.to_i
126
+ @top = top.to_i
127
+ @right = right.to_i
128
+ @bottom = bottom.to_i
129
+ end
130
+
131
+ def self.from_xywh(x, y, width, height)
132
+ new(x, y, x + width, y + height)
133
+ end
134
+
135
+ def self.from_wh(width, height)
136
+ new(0, 0, width, height)
137
+ end
138
+
139
+ def self.from_struct(struct)
140
+ new(struct[:left], struct[:top], struct[:right], struct[:bottom])
141
+ end
142
+
143
+ def to_struct
144
+ struct = Native::SKIRect.new
145
+ struct[:left] = @left
146
+ struct[:top] = @top
147
+ struct[:right] = @right
148
+ struct[:bottom] = @bottom
149
+ struct
150
+ end
151
+
152
+ def width
153
+ @right - @left
154
+ end
155
+
156
+ def height
157
+ @bottom - @top
158
+ end
159
+
160
+ def empty?
161
+ @left >= @right || @top >= @bottom
162
+ end
163
+
164
+ def ==(other)
165
+ return false unless other.is_a?(IRect)
166
+
167
+ @left == other.left && @top == other.top &&
168
+ @right == other.right && @bottom == other.bottom
169
+ end
170
+
171
+ def to_a
172
+ [@left, @top, @right, @bottom]
173
+ end
174
+
175
+ def to_rect
176
+ Rect.new(@left.to_f, @top.to_f, @right.to_f, @bottom.to_f)
177
+ end
178
+ end
179
+ end