CooCoo 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.
- 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
|