skia 0.1.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.
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/skia'
5
+
6
+ bounds = Skia::Rect.from_xywh(0, 0, 200, 200)
7
+ picture = Skia::Picture.record(bounds) do |canvas|
8
+ paint = Skia::Paint.new
9
+ paint.antialias = true
10
+
11
+ paint.color = Skia::Color::RED
12
+ paint.style = :fill
13
+ canvas.draw_circle(100, 100, 80, paint)
14
+
15
+ paint.color = Skia::Color::BLUE
16
+ paint.style = :stroke
17
+ paint.stroke_width = 5
18
+ canvas.draw_circle(100, 100, 80, paint)
19
+ end
20
+
21
+ puts 'Recorded picture:'
22
+ puts " Unique ID: #{picture.unique_id}"
23
+ puts " Cull rect: #{picture.cull_rect.inspect}"
24
+ puts " Approximate op count: #{picture.approximate_op_count}"
25
+ puts " Approximate bytes used: #{picture.approximate_bytes_used}"
26
+
27
+ data = picture.serialize
28
+ puts " Serialized size: #{data.size} bytes"
29
+
30
+ loaded_picture = Skia::Picture.from_data(data)
31
+ puts " Loaded picture unique ID: #{loaded_picture.unique_id}"
32
+
33
+ surface = Skia::Surface.make_raster(640, 480)
34
+ surface.draw do |canvas|
35
+ canvas.clear(Skia::Color::WHITE)
36
+
37
+ canvas.with_save do
38
+ canvas.translate(50, 50)
39
+ picture.playback(canvas)
40
+ end
41
+
42
+ canvas.with_save do
43
+ canvas.translate(300, 50)
44
+ canvas.scale(0.5, 0.5)
45
+ picture.playback(canvas)
46
+ end
47
+
48
+ canvas.with_save do
49
+ canvas.translate(50, 280)
50
+ canvas.scale(1.5, 1.5)
51
+ picture.playback(canvas)
52
+ end
53
+
54
+ canvas.with_save do
55
+ canvas.translate(400, 280)
56
+ canvas.rotate(45, 100, 100)
57
+ picture.playback(canvas)
58
+ end
59
+ end
60
+
61
+ surface.save_png('picture_output.png')
62
+ puts 'Saved to picture_output.png'
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/skia'
5
+
6
+ def draw_circular_progress(canvas, cx, cy, radius, progress, label, color)
7
+ paint = Skia::Paint.new
8
+ paint.antialias = true
9
+ paint.style = :stroke
10
+ paint.stroke_width = 12
11
+ paint.stroke_cap = :round
12
+
13
+ paint.color = Skia::Color.rgb(230, 230, 230)
14
+ canvas.draw_circle(cx, cy, radius, paint)
15
+
16
+ paint.color = color
17
+ rect = Skia::Rect.from_xywh(cx - radius, cy - radius, radius * 2, radius * 2)
18
+
19
+ path = Skia::Path.new
20
+ start_angle = -90
21
+ sweep_angle = progress * 360 / 100.0
22
+ path.arc_to_with_oval(rect, start_angle, sweep_angle, true)
23
+ canvas.draw_path(path, paint)
24
+
25
+ paint.style = :fill
26
+ paint.color = Skia::Color.rgb(50, 50, 50)
27
+ font_value = Skia::Font.new(nil, radius * 0.5)
28
+ value_text = "#{progress.to_i}%"
29
+ text_width, = font_value.measure_text(value_text)
30
+ canvas.draw_text(value_text, cx - text_width / 2, cy + radius * 0.15, font_value, paint)
31
+
32
+ font_label = Skia::Font.new(nil, radius * 0.25)
33
+ paint.color = Skia::Color.rgb(120, 120, 120)
34
+ label_width, = font_label.measure_text(label)
35
+ canvas.draw_text(label, cx - label_width / 2, cy + radius + 25, font_label, paint)
36
+ end
37
+
38
+ def draw_linear_progress(canvas, x, y, width, height, progress, label, color)
39
+ paint = Skia::Paint.new
40
+ paint.antialias = true
41
+
42
+ font = Skia::Font.new(nil, 14.0)
43
+ paint.color = Skia::Color.rgb(80, 80, 80)
44
+ canvas.draw_text(label, x, y - 10, font, paint)
45
+
46
+ percent_text = "#{progress.to_i}%"
47
+ text_width, = font.measure_text(percent_text)
48
+ canvas.draw_text(percent_text, x + width - text_width, y - 10, font, paint)
49
+
50
+ paint.color = Skia::Color.rgb(230, 230, 230)
51
+ bg_rect = Skia::Rect.from_xywh(x, y, width, height)
52
+ canvas.draw_round_rect(bg_rect, height / 2, paint)
53
+
54
+ paint.color = color
55
+ progress_width = width * progress / 100.0
56
+ return unless progress_width > height
57
+
58
+ progress_rect = Skia::Rect.from_xywh(x, y, progress_width, height)
59
+ canvas.draw_round_rect(progress_rect, height / 2, paint)
60
+ end
61
+
62
+ WIDTH = 800
63
+ HEIGHT = 500
64
+
65
+ surface = Skia::Surface.make_raster(WIDTH, HEIGHT)
66
+
67
+ surface.draw do |canvas|
68
+ canvas.clear(Skia::Color.rgb(245, 247, 250))
69
+
70
+ paint = Skia::Paint.new
71
+ paint.antialias = true
72
+
73
+ font_title = Skia::Font.new(nil, 28.0)
74
+ paint.color = Skia::Color.rgb(50, 50, 50)
75
+ canvas.draw_text('System Dashboard', 30, 45, font_title, paint)
76
+
77
+ draw_circular_progress(canvas, 130, 180, 70, 78, 'CPU Usage', Skia::Color.rgb(59, 130, 246))
78
+ draw_circular_progress(canvas, 310, 180, 70, 45, 'Memory', Skia::Color.rgb(16, 185, 129))
79
+ draw_circular_progress(canvas, 490, 180, 70, 92, 'Disk', Skia::Color.rgb(245, 158, 11))
80
+ draw_circular_progress(canvas, 670, 180, 70, 23, 'Network', Skia::Color.rgb(139, 92, 246))
81
+
82
+ y_start = 320
83
+ bar_height = 16
84
+ bar_spacing = 55
85
+
86
+ tasks = [
87
+ { label: 'Project Alpha', progress: 85, color: Skia::Color.rgb(59, 130, 246) },
88
+ { label: 'Project Beta', progress: 60, color: Skia::Color.rgb(16, 185, 129) },
89
+ { label: 'Project Gamma', progress: 35, color: Skia::Color.rgb(245, 158, 11) }
90
+ ]
91
+
92
+ tasks.each_with_index do |task, i|
93
+ draw_linear_progress(
94
+ canvas,
95
+ 50, y_start + (i * bar_spacing),
96
+ WIDTH - 100, bar_height,
97
+ task[:progress], task[:label], task[:color]
98
+ )
99
+ end
100
+ end
101
+
102
+ surface.save_png('dashboard.png')
103
+ puts 'Saved to dashboard.png'
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/skia'
5
+
6
+ def generate_social_card(
7
+ title:,
8
+ author: nil,
9
+ site_name: nil,
10
+ tags: [],
11
+ output: 'social_card.png'
12
+ )
13
+ width = 1200
14
+ height = 630
15
+
16
+ surface = Skia::Surface.make_raster(width, height)
17
+
18
+ surface.draw do |canvas|
19
+ paint = Skia::Paint.new
20
+ paint.antialias = true
21
+
22
+ gradient = Skia::Shader.linear_gradient(
23
+ Skia::Point.new(0, 0),
24
+ Skia::Point.new(width, height),
25
+ [
26
+ Skia::Color.rgb(102, 126, 234),
27
+ Skia::Color.rgb(118, 75, 162)
28
+ ]
29
+ )
30
+ paint.shader = gradient
31
+ canvas.draw_rect(Skia::Rect.from_xywh(0, 0, width, height), paint)
32
+ paint.shader = nil
33
+
34
+ paint.color = Skia::Color.argb(30, 255, 255, 255)
35
+ canvas.draw_circle(100, 100, 200, paint)
36
+ canvas.draw_circle(1100, 500, 250, paint)
37
+ canvas.draw_circle(900, 50, 100, paint)
38
+
39
+ paint.color = Skia::Color::WHITE
40
+ content_rect = Skia::Rect.from_xywh(60, 60, width - 120, height - 120)
41
+ canvas.draw_round_rect(content_rect, 20, paint)
42
+
43
+ paint.color = Skia::Color.rgb(30, 30, 30)
44
+ font_title = Skia::Font.new(nil, 52.0)
45
+
46
+ max_chars_per_line = 28
47
+ title_lines = []
48
+ words = title.split
49
+ current_line = ''
50
+
51
+ words.each do |word|
52
+ test_line = current_line.empty? ? word : "#{current_line} #{word}"
53
+ if test_line.length > max_chars_per_line && !current_line.empty?
54
+ title_lines << current_line
55
+ current_line = word
56
+ else
57
+ current_line = test_line
58
+ end
59
+ end
60
+ title_lines << current_line unless current_line.empty?
61
+
62
+ title_y = 160
63
+ title_lines.each_with_index do |line, i|
64
+ canvas.draw_text(line, 100, title_y + (i * 70), font_title, paint)
65
+ end
66
+
67
+ if tags.any?
68
+ font_tag = Skia::Font.new(nil, 24.0)
69
+ tag_y = height - 180
70
+ tag_x = 100
71
+
72
+ tags.each do |tag|
73
+ tag_text = "##{tag}"
74
+ text_width, = font_tag.measure_text(tag_text)
75
+
76
+ paint.color = Skia::Color.rgb(102, 126, 234)
77
+ tag_rect = Skia::Rect.from_xywh(tag_x - 10, tag_y - 25, text_width + 20, 35)
78
+ canvas.draw_round_rect(tag_rect, 17, paint)
79
+
80
+ paint.color = Skia::Color::WHITE
81
+ canvas.draw_text(tag_text, tag_x, tag_y, font_tag, paint)
82
+
83
+ tag_x += text_width + 30
84
+ end
85
+ end
86
+
87
+ if author
88
+ font_author = Skia::Font.new(nil, 28.0)
89
+ paint.color = Skia::Color.rgb(100, 100, 100)
90
+ canvas.draw_text(author, 100, height - 100, font_author, paint)
91
+ end
92
+
93
+ if site_name
94
+ font_site = Skia::Font.new(nil, 24.0)
95
+ paint.color = Skia::Color.rgb(150, 150, 150)
96
+ text_width, = font_site.measure_text(site_name)
97
+ canvas.draw_text(site_name, width - 100 - text_width, height - 100, font_site, paint)
98
+ end
99
+ end
100
+
101
+ surface.save_png(output)
102
+ puts "Saved to #{output}"
103
+ end
104
+
105
+ generate_social_card(
106
+ title: 'Building Ruby Bindings for Skia Graphics Library with FFI',
107
+ author: '@ruby_dev',
108
+ site_name: 'tech.blog',
109
+ tags: %w[Ruby FFI Graphics],
110
+ output: 'social_card_blog.png'
111
+ )
112
+
113
+ generate_social_card(
114
+ title: 'Conference Session: High Performance 2D Graphics in Ruby',
115
+ author: 'Speaker Name',
116
+ site_name: 'RubyKaigi',
117
+ tags: %w[RubyKaigi Conference],
118
+ output: 'social_card_event.png'
119
+ )
120
+
121
+ puts "\nSocial cards generated!"
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/skia"
5
+
6
+ surface = Skia::Surface.make_raster(640, 480)
7
+
8
+ surface.draw do |canvas|
9
+ canvas.clear(Skia::Color::WHITE)
10
+
11
+ font = Skia::Font.new(nil, 48.0)
12
+
13
+ paint = Skia::Paint.new
14
+ paint.antialias = true
15
+ paint.color = Skia::Color::BLACK
16
+
17
+ canvas.draw_text("Hello, Skia!", 50, 100, font, paint)
18
+
19
+ paint.color = Skia::Color::RED
20
+ canvas.draw_text("Red Text", 50, 180, font, paint)
21
+
22
+ paint.color = Skia::Color::BLUE
23
+ canvas.draw_text("Blue Text", 50, 260, font, paint)
24
+
25
+ small_font = Skia::Font.new(nil, 24.0)
26
+ paint.color = Skia::Color::GREEN
27
+ canvas.draw_text("Smaller green text", 50, 320, small_font, paint)
28
+
29
+ width, bounds = font.measure_text("Measured Text")
30
+ paint.color = Skia::Color.argb(100, 255, 200, 0)
31
+ paint.style = :fill
32
+ measured_rect = Skia::Rect.from_xywh(50, 380 + bounds.top, width, bounds.height)
33
+ canvas.draw_rect(measured_rect, paint)
34
+
35
+ paint.color = Skia::Color::BLACK
36
+ canvas.draw_text("Measured Text", 50, 380, font, paint)
37
+ end
38
+
39
+ surface.save_png("text_output.png")
40
+ puts "Saved to text_output.png"
data/lib/skia/base.rb ADDED
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skia
4
+ class Base
5
+ attr_reader :ptr
6
+
7
+ def initialize(ptr, release_method = nil)
8
+ raise NullPointerError, 'Pointer cannot be nil' if ptr.nil? || ptr.null?
9
+
10
+ @ptr = ptr
11
+ @release_method = release_method
12
+ setup_release if @release_method
13
+ end
14
+
15
+ def to_ptr
16
+ @ptr
17
+ end
18
+
19
+ def null?
20
+ @ptr.nil? || @ptr.null?
21
+ end
22
+
23
+ def self.release_callback(release_method, ptr)
24
+ proc { Native.send(release_method, ptr) unless ptr.null? }
25
+ end
26
+
27
+ protected
28
+
29
+ def setup_release
30
+ ObjectSpace.define_finalizer(self, self.class.release_callback(@release_method, @ptr))
31
+ end
32
+
33
+ def release!
34
+ return unless @release_method && @ptr && !@ptr.null?
35
+
36
+ Native.send(@release_method, @ptr)
37
+ @ptr = nil
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skia
4
+ class Canvas < Base
5
+ def initialize(ptr)
6
+ super(ptr, nil)
7
+ end
8
+
9
+ def save
10
+ Native.sk_canvas_save(@ptr)
11
+ end
12
+
13
+ def save_layer(bounds = nil, paint = nil)
14
+ bounds_ptr = bounds&.to_struct
15
+ paint_ptr = paint&.ptr
16
+ Native.sk_canvas_save_layer(@ptr, bounds_ptr, paint_ptr)
17
+ end
18
+
19
+ def restore
20
+ Native.sk_canvas_restore(@ptr)
21
+ end
22
+
23
+ def restore_to_count(count)
24
+ Native.sk_canvas_restore_to_count(@ptr, count)
25
+ end
26
+
27
+ def save_count
28
+ Native.sk_canvas_get_save_count(@ptr)
29
+ end
30
+
31
+ def translate(dx, dy)
32
+ Native.sk_canvas_translate(@ptr, dx.to_f, dy.to_f)
33
+ self
34
+ end
35
+
36
+ def scale(sx, sy = sx)
37
+ Native.sk_canvas_scale(@ptr, sx.to_f, sy.to_f)
38
+ self
39
+ end
40
+
41
+ def rotate(degrees, px = nil, py = nil)
42
+ if px && py
43
+ translate(px, py)
44
+ Native.sk_canvas_rotate_degrees(@ptr, degrees.to_f)
45
+ translate(-px, -py)
46
+ else
47
+ Native.sk_canvas_rotate_degrees(@ptr, degrees.to_f)
48
+ end
49
+ self
50
+ end
51
+
52
+ def rotate_radians(radians)
53
+ Native.sk_canvas_rotate_radians(@ptr, radians.to_f)
54
+ self
55
+ end
56
+
57
+ def skew(sx, sy)
58
+ Native.sk_canvas_skew(@ptr, sx.to_f, sy.to_f)
59
+ self
60
+ end
61
+
62
+ def concat(matrix)
63
+ matrix_struct = matrix.to_struct44
64
+ Native.sk_canvas_concat(@ptr, matrix_struct)
65
+ self
66
+ end
67
+
68
+ def matrix=(matrix)
69
+ matrix_struct = matrix.to_struct44
70
+ Native.sk_canvas_set_matrix(@ptr, matrix_struct)
71
+ end
72
+
73
+ def matrix
74
+ m = Native::SKMatrix44.new
75
+ Native.sk_canvas_get_matrix(@ptr, m)
76
+ Matrix.from_struct44(m)
77
+ end
78
+
79
+ def clip_rect(rect, op = :intersect, antialias: false)
80
+ rect_struct = rect.to_struct
81
+ Native.sk_canvas_clip_rect_with_operation(@ptr, rect_struct, op, antialias)
82
+ self
83
+ end
84
+
85
+ def clip_path(path, op = :intersect, antialias: false)
86
+ Native.sk_canvas_clip_path_with_operation(@ptr, path.ptr, op, antialias)
87
+ self
88
+ end
89
+
90
+ def clear(color = Color::TRANSPARENT)
91
+ color_value = color.is_a?(Color) ? color.to_i : color
92
+ Native.sk_canvas_clear(@ptr, color_value)
93
+ self
94
+ end
95
+
96
+ def draw_color(color, blend_mode = :src_over)
97
+ color_value = color.is_a?(Color) ? color.to_i : color
98
+ Native.sk_canvas_draw_color(@ptr, color_value, blend_mode)
99
+ self
100
+ end
101
+
102
+ def draw_paint(paint)
103
+ Native.sk_canvas_draw_paint(@ptr, paint.ptr)
104
+ self
105
+ end
106
+
107
+ def draw_rect(rect, paint)
108
+ rect_struct = rect.to_struct
109
+ Native.sk_canvas_draw_rect(@ptr, rect_struct, paint.ptr)
110
+ self
111
+ end
112
+
113
+ def draw_round_rect(rect, radius, paint)
114
+ rect_struct = rect.to_struct
115
+ Native.sk_canvas_draw_round_rect(@ptr, rect_struct, radius.to_f, radius.to_f, paint.ptr)
116
+ self
117
+ end
118
+
119
+ def draw_circle(cx, cy, radius, paint)
120
+ Native.sk_canvas_draw_circle(@ptr, cx.to_f, cy.to_f, radius.to_f, paint.ptr)
121
+ self
122
+ end
123
+
124
+ def draw_oval(rect, paint)
125
+ rect_struct = rect.to_struct
126
+ Native.sk_canvas_draw_oval(@ptr, rect_struct, paint.ptr)
127
+ self
128
+ end
129
+
130
+ def draw_path(path, paint)
131
+ Native.sk_canvas_draw_path(@ptr, path.ptr, paint.ptr)
132
+ self
133
+ end
134
+
135
+ def draw_line(x1, y1, x2, y2, paint)
136
+ Native.sk_canvas_draw_line(@ptr, x1.to_f, y1.to_f, x2.to_f, y2.to_f, paint.ptr)
137
+ self
138
+ end
139
+
140
+ def draw_image(image, x, y, paint = nil)
141
+ Native.sk_canvas_draw_image(@ptr, image.ptr, x.to_f, y.to_f, paint&.ptr)
142
+ self
143
+ end
144
+
145
+ def draw_image_rect(image, src_rect, dst_rect, paint = nil)
146
+ src_struct = src_rect&.to_struct
147
+ dst_struct = dst_rect.to_struct
148
+ Native.sk_canvas_draw_image_rect(@ptr, image.ptr, src_struct, dst_struct, paint&.ptr)
149
+ self
150
+ end
151
+
152
+ def draw_text(text, x, y, font, paint)
153
+ text_bytes = text.encode('UTF-8')
154
+ Native.sk_canvas_draw_simple_text(@ptr, text_bytes, text_bytes.bytesize, :utf8, x.to_f, y.to_f, font.ptr,
155
+ paint.ptr)
156
+ self
157
+ end
158
+
159
+ def with_save
160
+ count = save
161
+ begin
162
+ yield self
163
+ ensure
164
+ restore_to_count(count)
165
+ end
166
+ end
167
+
168
+ def with_save_layer(bounds = nil, paint = nil)
169
+ count = save_layer(bounds, paint)
170
+ begin
171
+ yield self
172
+ ensure
173
+ restore_to_count(count)
174
+ end
175
+ end
176
+ end
177
+ end
data/lib/skia/color.rb ADDED
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skia
4
+ class Color
5
+ attr_reader :value
6
+
7
+ def initialize(r_or_value = 0, g = nil, b = nil, a = 255)
8
+ @value = if g.nil? && b.nil?
9
+ r_or_value.is_a?(Integer) ? r_or_value : 0xFF000000
10
+ else
11
+ ((a & 0xFF) << 24) | ((r_or_value & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF)
12
+ end
13
+ end
14
+
15
+ def self.argb(a, r, g, b)
16
+ new(r, g, b, a)
17
+ end
18
+
19
+ def self.rgb(r, g, b)
20
+ new(r, g, b, 255)
21
+ end
22
+
23
+ def self.from_hex(hex_string)
24
+ hex = hex_string.delete_prefix('#')
25
+ case hex.length
26
+ when 6
27
+ new(hex.to_i(16) | 0xFF000000)
28
+ when 8
29
+ new(hex.to_i(16))
30
+ else
31
+ raise ArgumentError, "Invalid hex color format: #{hex_string}"
32
+ end
33
+ end
34
+
35
+ def alpha
36
+ (@value >> 24) & 0xFF
37
+ end
38
+
39
+ def red
40
+ (@value >> 16) & 0xFF
41
+ end
42
+
43
+ def green
44
+ (@value >> 8) & 0xFF
45
+ end
46
+
47
+ def blue
48
+ @value & 0xFF
49
+ end
50
+
51
+ def to_i
52
+ @value
53
+ end
54
+
55
+ def to_hex
56
+ format('#%08X', @value)
57
+ end
58
+
59
+ def with_alpha(a)
60
+ self.class.new(red, green, blue, a)
61
+ end
62
+
63
+ def ==(other)
64
+ return false unless other.is_a?(Color)
65
+
66
+ @value == other.value
67
+ end
68
+
69
+ TRANSPARENT = new(0x00000000)
70
+ BLACK = new(0xFF000000)
71
+ WHITE = new(0xFFFFFFFF)
72
+ RED = new(0xFFFF0000)
73
+ GREEN = new(0xFF00FF00)
74
+ BLUE = new(0xFF0000FF)
75
+ YELLOW = new(0xFFFFFF00)
76
+ CYAN = new(0xFF00FFFF)
77
+ MAGENTA = new(0xFFFF00FF)
78
+ GRAY = new(0xFF808080)
79
+ LIGHT_GRAY = new(0xFFC0C0C0)
80
+ DARK_GRAY = new(0xFF404040)
81
+ end
82
+ end
data/lib/skia/data.rb ADDED
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skia
4
+ class Data < Base
5
+ def initialize(ptr_or_bytes = nil)
6
+ case ptr_or_bytes
7
+ when FFI::Pointer
8
+ super(ptr_or_bytes, :sk_data_unref)
9
+ when String
10
+ ptr = Native.sk_data_new_with_copy(ptr_or_bytes, ptr_or_bytes.bytesize)
11
+ raise Error, 'Failed to create data from bytes' if ptr.nil? || ptr.null?
12
+
13
+ super(ptr, :sk_data_unref)
14
+ when nil
15
+ raise ArgumentError, 'Data requires bytes or pointer'
16
+ else
17
+ raise ArgumentError, "Invalid argument type: #{ptr_or_bytes.class}"
18
+ end
19
+ end
20
+
21
+ def self.from_file(path)
22
+ ptr = Native.sk_data_new_from_file(path)
23
+ raise FileNotFoundError, "Failed to load file: #{path}" if ptr.nil? || ptr.null?
24
+
25
+ new(ptr)
26
+ end
27
+
28
+ def self.from_bytes(bytes)
29
+ new(bytes)
30
+ end
31
+
32
+ def size
33
+ Native.sk_data_get_size(@ptr)
34
+ end
35
+
36
+ def empty?
37
+ size == 0
38
+ end
39
+
40
+ def to_s
41
+ data_ptr = Native.sk_data_get_data(@ptr)
42
+ data_ptr.read_bytes(size)
43
+ end
44
+
45
+ alias to_bytes to_s
46
+ end
47
+ end