CooCoo 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/CooCoo.gemspec +47 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +88 -0
- data/README.md +123 -0
- data/Rakefile +81 -0
- data/bin/cuda-dev-info +25 -0
- data/bin/cuda-free +28 -0
- data/bin/cuda-free-trend +7 -0
- data/bin/ffi-gen +267 -0
- data/bin/spec_runner_html.sh +42 -0
- data/bin/trainer +198 -0
- data/bin/trend-cost +13 -0
- data/examples/char-rnn.rb +405 -0
- data/examples/cifar/cifar.rb +94 -0
- data/examples/img-similarity.rb +201 -0
- data/examples/math_ops.rb +57 -0
- data/examples/mnist.rb +365 -0
- data/examples/mnist_classifier.rb +293 -0
- data/examples/mnist_dream.rb +214 -0
- data/examples/seeds.rb +268 -0
- data/examples/seeds_dataset.txt +210 -0
- data/examples/t10k-images-idx3-ubyte +0 -0
- data/examples/t10k-labels-idx1-ubyte +0 -0
- data/examples/train-images-idx3-ubyte +0 -0
- data/examples/train-labels-idx1-ubyte +0 -0
- data/ext/buffer/Rakefile +50 -0
- data/ext/buffer/buffer.pre.cu +727 -0
- data/ext/buffer/matrix.pre.cu +49 -0
- data/lib/CooCoo.rb +1 -0
- data/lib/coo-coo.rb +18 -0
- data/lib/coo-coo/activation_functions.rb +344 -0
- data/lib/coo-coo/consts.rb +5 -0
- data/lib/coo-coo/convolution.rb +298 -0
- data/lib/coo-coo/core_ext.rb +75 -0
- data/lib/coo-coo/cost_functions.rb +91 -0
- data/lib/coo-coo/cuda.rb +116 -0
- data/lib/coo-coo/cuda/device_buffer.rb +240 -0
- data/lib/coo-coo/cuda/device_buffer/ffi.rb +109 -0
- data/lib/coo-coo/cuda/error.rb +51 -0
- data/lib/coo-coo/cuda/host_buffer.rb +117 -0
- data/lib/coo-coo/cuda/runtime.rb +157 -0
- data/lib/coo-coo/cuda/vector.rb +315 -0
- data/lib/coo-coo/data_sources.rb +2 -0
- data/lib/coo-coo/data_sources/xournal.rb +25 -0
- data/lib/coo-coo/data_sources/xournal/bitmap_stream.rb +197 -0
- data/lib/coo-coo/data_sources/xournal/document.rb +377 -0
- data/lib/coo-coo/data_sources/xournal/loader.rb +144 -0
- data/lib/coo-coo/data_sources/xournal/renderer.rb +101 -0
- data/lib/coo-coo/data_sources/xournal/saver.rb +99 -0
- data/lib/coo-coo/data_sources/xournal/training_document.rb +78 -0
- data/lib/coo-coo/data_sources/xournal/training_document/constants.rb +15 -0
- data/lib/coo-coo/data_sources/xournal/training_document/document_maker.rb +89 -0
- data/lib/coo-coo/data_sources/xournal/training_document/document_reader.rb +105 -0
- data/lib/coo-coo/data_sources/xournal/training_document/example.rb +37 -0
- data/lib/coo-coo/data_sources/xournal/training_document/sets.rb +76 -0
- data/lib/coo-coo/debug.rb +8 -0
- data/lib/coo-coo/dot.rb +129 -0
- data/lib/coo-coo/drawing.rb +4 -0
- data/lib/coo-coo/drawing/cairo_canvas.rb +100 -0
- data/lib/coo-coo/drawing/canvas.rb +68 -0
- data/lib/coo-coo/drawing/chunky_canvas.rb +101 -0
- data/lib/coo-coo/drawing/sixel.rb +214 -0
- data/lib/coo-coo/enum.rb +17 -0
- data/lib/coo-coo/from_name.rb +58 -0
- data/lib/coo-coo/fully_connected_layer.rb +205 -0
- data/lib/coo-coo/generation_script.rb +38 -0
- data/lib/coo-coo/grapher.rb +140 -0
- data/lib/coo-coo/image.rb +286 -0
- data/lib/coo-coo/layer.rb +67 -0
- data/lib/coo-coo/layer_factory.rb +26 -0
- data/lib/coo-coo/linear_layer.rb +59 -0
- data/lib/coo-coo/math.rb +607 -0
- data/lib/coo-coo/math/abstract_vector.rb +121 -0
- data/lib/coo-coo/math/functions.rb +39 -0
- data/lib/coo-coo/math/interpolation.rb +7 -0
- data/lib/coo-coo/network.rb +264 -0
- data/lib/coo-coo/neuron.rb +112 -0
- data/lib/coo-coo/neuron_layer.rb +168 -0
- data/lib/coo-coo/option_parser.rb +18 -0
- data/lib/coo-coo/platform.rb +17 -0
- data/lib/coo-coo/progress_bar.rb +11 -0
- data/lib/coo-coo/recurrence/backend.rb +99 -0
- data/lib/coo-coo/recurrence/frontend.rb +101 -0
- data/lib/coo-coo/sequence.rb +187 -0
- data/lib/coo-coo/shell.rb +2 -0
- data/lib/coo-coo/temporal_network.rb +291 -0
- data/lib/coo-coo/trainer.rb +21 -0
- data/lib/coo-coo/trainer/base.rb +67 -0
- data/lib/coo-coo/trainer/batch.rb +82 -0
- data/lib/coo-coo/trainer/batch_stats.rb +27 -0
- data/lib/coo-coo/trainer/momentum_stochastic.rb +59 -0
- data/lib/coo-coo/trainer/stochastic.rb +47 -0
- data/lib/coo-coo/transformer.rb +272 -0
- data/lib/coo-coo/vector_layer.rb +194 -0
- data/lib/coo-coo/version.rb +3 -0
- data/lib/coo-coo/weight_deltas.rb +23 -0
- data/prototypes/convolution.rb +116 -0
- data/prototypes/linear_drop.rb +51 -0
- data/prototypes/recurrent_layers.rb +79 -0
- data/www/images/screamer.png +0 -0
- data/www/images/screamer.xcf +0 -0
- data/www/index.html +82 -0
- metadata +373 -0
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'coo-coo/data_sources/xournal/training_document/constants'
|
2
|
+
require 'coo-coo/data_sources/xournal/training_document/example'
|
3
|
+
|
4
|
+
module CooCoo
|
5
|
+
module DataSources
|
6
|
+
module Xournal
|
7
|
+
class TrainingDocument
|
8
|
+
class DocumentReader
|
9
|
+
def initialize
|
10
|
+
end
|
11
|
+
|
12
|
+
def load(xournal)
|
13
|
+
version, columns, rows, cells_per_example = read_meta_label(xournal)
|
14
|
+
|
15
|
+
if columns == nil || rows == nil
|
16
|
+
raise ArgumentError.new("Xournal lacks a Text element with '#{META_LABEL} VERSION: COLS ROWS CELLS_PER_EXAMPLE'")
|
17
|
+
end
|
18
|
+
|
19
|
+
doc = TrainingDocument.new
|
20
|
+
|
21
|
+
xournal.each_page do |page|
|
22
|
+
page.each_layer do |layer|
|
23
|
+
process_layer(doc, page, layer, columns, rows)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
doc
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_meta_label(xournal)
|
31
|
+
version = nil
|
32
|
+
columns = nil
|
33
|
+
rows = nil
|
34
|
+
meta = nil
|
35
|
+
|
36
|
+
xournal.each_page do |page|
|
37
|
+
page.each_layer do |layer|
|
38
|
+
layer.each_text do |txt|
|
39
|
+
if txt.text =~ /^#{META_LABEL}/
|
40
|
+
meta = txt.text
|
41
|
+
break
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
if meta
|
48
|
+
m = meta.match(META_LABEL_REGEX)
|
49
|
+
version = m[1].to_f
|
50
|
+
columns = m[2].to_i
|
51
|
+
rows = m[3].to_i
|
52
|
+
cells_per_example = (m[4] || 1).to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
return version, columns, rows, cells_per_example
|
56
|
+
end
|
57
|
+
|
58
|
+
def process_layer(doc, page, layer, columns, rows)
|
59
|
+
grid_w = page.width / columns.to_f
|
60
|
+
grid_h = page.height / rows.to_f
|
61
|
+
|
62
|
+
labels = Hash.new { |h, k| h[k] = Hash.new { |a, b| a[b] = Array.new } }
|
63
|
+
strokes = Hash.new { |h, k| h[k] = Hash.new { |a, b| a[b] = Array.new } }
|
64
|
+
|
65
|
+
layer.each_text do |txt|
|
66
|
+
next if txt.text =~ /^#{META_LABEL}/
|
67
|
+
row = (txt.y / grid_h).round
|
68
|
+
column = (txt.x / grid_w).round
|
69
|
+
labels[row.to_i][column.to_i] << txt
|
70
|
+
end
|
71
|
+
|
72
|
+
layer.each_stroke do |stroke|
|
73
|
+
color = ChunkyPNG::Color.parse(stroke.color)
|
74
|
+
next if ChunkyPNG::Color.euclidean_distance_rgba(color, PARSED_GRID_COLOR) == 0.0
|
75
|
+
min, max = stroke.minmax
|
76
|
+
row = (min[1] / grid_h)
|
77
|
+
column = (min[0] / grid_w)
|
78
|
+
|
79
|
+
strokes[row.to_i][column.to_i] << stroke
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
rows.times do |row|
|
84
|
+
grid_min_y = (row * grid_h).floor
|
85
|
+
|
86
|
+
columns.times do |column|
|
87
|
+
grid_min_x = (column * grid_w).floor
|
88
|
+
ex_label = labels[row][column].first
|
89
|
+
ex_strokes = strokes[row][column]
|
90
|
+
unless ex_strokes.empty? && ex_label == nil
|
91
|
+
doc.add_example(ex_label && ex_label.text,
|
92
|
+
ex_strokes.collect { |s|
|
93
|
+
s.
|
94
|
+
translate(-grid_min_x, -grid_min_y).
|
95
|
+
scale(1.0 / grid_w, 1.0 / grid_h, 1.0 / grid_w)
|
96
|
+
})
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module CooCoo
|
2
|
+
module DataSources
|
3
|
+
module Xournal
|
4
|
+
class TrainingDocument
|
5
|
+
class Example
|
6
|
+
attr_accessor :label
|
7
|
+
attr_reader :stroke_sets
|
8
|
+
|
9
|
+
def initialize(label, *sets)
|
10
|
+
@label = label
|
11
|
+
@stroke_sets = Array.new
|
12
|
+
sets.each do |points|
|
13
|
+
@stroke_sets << points
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_set(strokes)
|
18
|
+
@stroke_sets << strokes
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def each_set(&block)
|
23
|
+
@stroke_sets.each(&block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def empty?
|
27
|
+
@stroke_sets.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
def size
|
31
|
+
@stroke_sets.size
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module CooCoo
|
2
|
+
module DataSources
|
3
|
+
module Xournal
|
4
|
+
class TrainingDocument
|
5
|
+
# Create a {TrainingDocument} for ASCII characters.
|
6
|
+
# @param doc [Document] Optional Document used to extract examples.
|
7
|
+
# @return [TrainingDocument]
|
8
|
+
def self.ascii_trainer(doc = self.new)
|
9
|
+
(32...127).each do |c|
|
10
|
+
c = c.chr[0]
|
11
|
+
doc.add_example(c, [])
|
12
|
+
end
|
13
|
+
|
14
|
+
doc
|
15
|
+
end
|
16
|
+
|
17
|
+
# Create a {TrainingDocument} for an arbitrary Unicode block.
|
18
|
+
# @param starting_offset [Integer] Which Unicode character to start the examples
|
19
|
+
# @param number [Integer] The number of characters to place in the document.
|
20
|
+
# @param doc [Document] Optional Document used to extract examples.
|
21
|
+
# @return [TrainingDocument]
|
22
|
+
def self.unicode_trainer(starting_offset, number, doc = self.new)
|
23
|
+
number.times do |i|
|
24
|
+
doc.add_example("" << (starting_offset + i), [])
|
25
|
+
end
|
26
|
+
|
27
|
+
doc
|
28
|
+
end
|
29
|
+
|
30
|
+
# Create a {TrainingDocument} for Japanese Hiragana, Katakana, and punctuation.
|
31
|
+
# @param doc [Document] Optional Document used to extract examples.
|
32
|
+
# @return [TrainingDocument]
|
33
|
+
def self.jp_trainer(doc = self.new)
|
34
|
+
unicode_trainer(0x3000, 64, doc)
|
35
|
+
unicode_trainer(0x3040, 96, doc)
|
36
|
+
unicode_trainer(0x30A0, 96, doc)
|
37
|
+
unicode_trainer(0xff00, 16 * 15, doc)
|
38
|
+
doc
|
39
|
+
end
|
40
|
+
|
41
|
+
# Create a {TrainingDocument} for the CJK block.
|
42
|
+
# @param limit [Integer] Optional limit on how many characters to include.
|
43
|
+
# @param doc [Document] Optional Document used to extract examples.
|
44
|
+
# @return [TrainingDocument]
|
45
|
+
def self.cjk_trainer(limit = 2000, doc = self.new)
|
46
|
+
unicode_trainer(0x4e00, limit)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Create a {TrainingDocument} for emoji.
|
50
|
+
# @param doc [Document] Optional Document used to extract examples.
|
51
|
+
# @return [TrainingDocument]
|
52
|
+
def self.emoji_trainer(doc = self.new)
|
53
|
+
unicode_trainer(0x1F600, 16 * 5, doc)
|
54
|
+
unicode_trainer(0x2700, 16 * 12, doc)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Create a {TrainingDocument} for math symbols.
|
58
|
+
# @param doc [Document] Optional Document used to extract examples.
|
59
|
+
# @return [TrainingDocument]
|
60
|
+
def self.math_trainer(doc = self.new)
|
61
|
+
unicode_trainer(0x2200, 16 * 16, doc)
|
62
|
+
unicode_trainer(0x2A00, 16 * 16, doc)
|
63
|
+
unicode_trainer(0x2100, 16 * 5, doc)
|
64
|
+
unicode_trainer(0x27C0, 16 * 3, doc)
|
65
|
+
unicode_trainer(0x2980, 16 * 8, doc)
|
66
|
+
unicode_trainer(0x2300, 16 * 16, doc)
|
67
|
+
unicode_trainer(0x25A0, 16 * 6, doc)
|
68
|
+
unicode_trainer(0x2B00, 16 * 16, doc)
|
69
|
+
unicode_trainer(0x2190, 16 * 7, doc)
|
70
|
+
unicode_trainer(0x2900, 16 * 8, doc)
|
71
|
+
unicode_trainer(0x1D400, 16 * 16 * 4, doc)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/coo-coo/dot.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
module CooCoo
|
2
|
+
module Dot
|
3
|
+
class Block
|
4
|
+
attr_reader :options
|
5
|
+
attr_reader :kind
|
6
|
+
|
7
|
+
def initialize(kind, options)
|
8
|
+
@kind = kind
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Graph < Block
|
14
|
+
class Node
|
15
|
+
attr_reader :options
|
16
|
+
attr_reader :name
|
17
|
+
|
18
|
+
def initialize(name, options)
|
19
|
+
@name = name
|
20
|
+
@options = options
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Edge
|
25
|
+
attr_reader :options
|
26
|
+
attr_reader :nodes
|
27
|
+
|
28
|
+
def initialize(nodes, options = {})
|
29
|
+
@options = options
|
30
|
+
@nodes = nodes.dup
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_node(node)
|
34
|
+
@nodes << node
|
35
|
+
self
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :nodes, :edges, :blocks
|
40
|
+
|
41
|
+
def initialize(kind, options)
|
42
|
+
super(kind, options)
|
43
|
+
@nodes = []
|
44
|
+
@edges = []
|
45
|
+
@blocks = []
|
46
|
+
yield(self) if block_given?
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_node(name, options = {})
|
50
|
+
@nodes << Node.new(name, options)
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_edge(nodes, options = {})
|
55
|
+
@edges << Edge.new(nodes, options)
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_subgraph(name, options = {}, &block)
|
60
|
+
@blocks << Graph.new("subgraph #{name}", options, &block)
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_block(type, options = {}, &block)
|
65
|
+
@blocks << Graph.new(type, options, &block)
|
66
|
+
self
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Writer
|
71
|
+
def initialize
|
72
|
+
end
|
73
|
+
|
74
|
+
def write(graph, io)
|
75
|
+
io.write(write_graph(graph).join("\n"))
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
def write_graph(g, depth = 0)
|
80
|
+
start_block("#{g.kind}", depth) do |d|
|
81
|
+
lines = []
|
82
|
+
lines += write_graph_options(g, d)
|
83
|
+
g.blocks.each do |kid|
|
84
|
+
lines += write_graph(kid, d)
|
85
|
+
end
|
86
|
+
lines += write_nodes(g.nodes, d)
|
87
|
+
lines += write_edges(g.edges, d)
|
88
|
+
lines
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def indent(size)
|
93
|
+
" " * size
|
94
|
+
end
|
95
|
+
|
96
|
+
def write_graph_options(graph, depth)
|
97
|
+
graph.options.collect do |key, value|
|
98
|
+
indent(depth) + "#{key}=\"#{value}\";"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def start_block(kind, depth)
|
103
|
+
[ indent(depth) + "#{kind} {",
|
104
|
+
*yield(depth + 1),
|
105
|
+
indent(depth) + "}"
|
106
|
+
]
|
107
|
+
end
|
108
|
+
|
109
|
+
def write_nodes(nodes, depth)
|
110
|
+
nodes.collect do |node|
|
111
|
+
indent(depth) + "#{node.name}[#{write_options(node.options)}];"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def write_edges(edges, depth)
|
116
|
+
edges.collect do |edge|
|
117
|
+
nodes = edge.nodes.join(" -> ")
|
118
|
+
indent(depth) + "#{nodes}[#{write_options(edge.options)}];"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def write_options(options)
|
123
|
+
options.collect do |key, value|
|
124
|
+
"#{key}=\"#{value}\""
|
125
|
+
end.join(",")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'chunky_png'
|
2
|
+
require 'cairo'
|
3
|
+
|
4
|
+
module CooCoo
|
5
|
+
module Drawing
|
6
|
+
class CairoCanvas < Canvas
|
7
|
+
attr_reader :surface, :context
|
8
|
+
|
9
|
+
def initialize(surface_or_width, height = nil)
|
10
|
+
if height
|
11
|
+
surface = Cairo::ImageSurface.new(surface_or_width, height)
|
12
|
+
else
|
13
|
+
surface = surface_or_width
|
14
|
+
end
|
15
|
+
|
16
|
+
@surface = surface
|
17
|
+
@context = Cairo::Context.new(@surface)
|
18
|
+
end
|
19
|
+
|
20
|
+
def flush
|
21
|
+
@surface.flush
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def line(x1, y1, x2, y2)
|
26
|
+
@context.set_source_rgba(ChunkyPNG::Color.to_truecolor_alpha_bytes(stroke_color))
|
27
|
+
@context.move_to(x1, y1)
|
28
|
+
@context.line_to(x2, y2)
|
29
|
+
@context.stroke
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def stroke(points)
|
34
|
+
@context.set_source_rgba(*ChunkyPNG::Color.to_truecolor_alpha_bytes(stroke_color))
|
35
|
+
@context.set_line_width(points[0][2])
|
36
|
+
@context.move_to(points[0][0], points[0][1])
|
37
|
+
@context.line_cap = Cairo::LINE_CAP_ROUND
|
38
|
+
@context.line_join = Cairo::LINE_JOIN_ROUND
|
39
|
+
|
40
|
+
points.each.drop(1).each do |(x, y, w, color)|
|
41
|
+
@context.set_line_width(w)
|
42
|
+
if color
|
43
|
+
@context.set_source_rgba(*ChunkyPNG::Color.to_truecolor_alpha_bytes(color))
|
44
|
+
end
|
45
|
+
@context.line_to(x, y)
|
46
|
+
end
|
47
|
+
|
48
|
+
@context.stroke
|
49
|
+
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def rect(x, y, w, h)
|
54
|
+
@context.rectangle(x, y, w, h)
|
55
|
+
@context.set_source_rgba(*ChunkyPNG::Color.to_truecolor_alpha_bytes(fill_color))
|
56
|
+
@context.fill
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def circle(x, y, r)
|
61
|
+
@context.circle(x, y, r)
|
62
|
+
@context.set_source_rgba(*ChunkyPNG::Color.to_truecolor_alpha_bytes(fill_color))
|
63
|
+
@context.fill
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
def blit(img, x, y, w, h)
|
68
|
+
surface = Cairo::ImageSurface.from_png(StringIO.new(img))
|
69
|
+
zx = w / surface.width.to_f
|
70
|
+
zy = h / surface.height.to_f
|
71
|
+
@context.set_source(surface, x / zx, y / zy)
|
72
|
+
@context.scale(zx, zy)
|
73
|
+
@context.paint
|
74
|
+
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
def text(txt, x, y, font, font_size, style = Cairo::FONT_SLANT_NORMAL)
|
79
|
+
@context.move_to(x, y + font_size)
|
80
|
+
@context.set_source_rgba(*ChunkyPNG::Color.to_truecolor_alpha_bytes(fill_color))
|
81
|
+
@context.select_font_face(font, style)
|
82
|
+
@context.font_size = font_size
|
83
|
+
@context.show_text(txt)
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_blob
|
88
|
+
data = StringIO.new
|
89
|
+
@surface.write_to_png(data)
|
90
|
+
data.rewind
|
91
|
+
data.read
|
92
|
+
end
|
93
|
+
|
94
|
+
def to_vector(grayscale = false)
|
95
|
+
@surface.flush
|
96
|
+
chunky_to_vector(ChunkyPNG::Image.from_blob(to_blob), grayscale)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|