prawn-shadings 0.1.1
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.
- data/.rspec +1 -0
- data/BSDL +24 -0
- data/Gemfile +13 -0
- data/LICENSE +56 -0
- data/README.md +68 -0
- data/Rakefile +18 -0
- data/lib/prawn-shadings.rb +1 -0
- data/lib/prawn/shadings.rb +649 -0
- data/spec/lib/prawn/shadings_spec.rb +862 -0
- data/spec/spec_helper.rb +22 -0
- metadata +134 -0
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/BSDL
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Copyright (C) 2013 Alexander Mankuta. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
4
|
+
modification, are permitted provided that the following conditions
|
5
|
+
are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright
|
11
|
+
notice, this list of conditions and the following disclaimer in the
|
12
|
+
documentation and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
15
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
16
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
17
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
18
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
19
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
20
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
21
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
22
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
23
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
24
|
+
SUCH DAMAGE.
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
Prawn-Shadings is copyrighted free software by Alexander Mankuta <alex@pointlessone.org>.
|
2
|
+
You can redistribute it and/or modify it under either the terms of the
|
3
|
+
2-clause BSDL (see the file BSDL), or the conditions below:
|
4
|
+
|
5
|
+
1. You may make and give away verbatim copies of the source form of the
|
6
|
+
software without restriction, provided that you duplicate all of the
|
7
|
+
original copyright notices and associated disclaimers.
|
8
|
+
|
9
|
+
2. You may modify your copy of the software in any way, provided that
|
10
|
+
you do at least ONE of the following:
|
11
|
+
|
12
|
+
a) place your modifications in the Public Domain or otherwise
|
13
|
+
make them Freely Available, such as by posting said
|
14
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
15
|
+
the author to include your modifications in the software.
|
16
|
+
|
17
|
+
b) use the modified software only within your corporation or
|
18
|
+
organization.
|
19
|
+
|
20
|
+
c) give non-standard binaries non-standard names, with
|
21
|
+
instructions on where to get the original software distribution.
|
22
|
+
|
23
|
+
d) make other distribution arrangements with the author.
|
24
|
+
|
25
|
+
3. You may distribute the software in object code or binary form,
|
26
|
+
provided that you do at least ONE of the following:
|
27
|
+
|
28
|
+
a) distribute the binaries and library files of the software,
|
29
|
+
together with instructions (in the manual page or equivalent)
|
30
|
+
on where to get the original distribution.
|
31
|
+
|
32
|
+
b) accompany the distribution with the machine-readable source of
|
33
|
+
the software.
|
34
|
+
|
35
|
+
c) give non-standard binaries non-standard names, with
|
36
|
+
instructions on where to get the original software distribution.
|
37
|
+
|
38
|
+
d) make other distribution arrangements with the author.
|
39
|
+
|
40
|
+
4. You may modify and include the part of the software into any other
|
41
|
+
software (possibly commercial). But some files in the distribution
|
42
|
+
are not written by the author, so that they are not under these terms.
|
43
|
+
|
44
|
+
For the list of those files and their copying conditions, see the
|
45
|
+
file LEGAL.
|
46
|
+
|
47
|
+
5. The scripts and library files supplied as input to or produced as
|
48
|
+
output from the software do not automatically fall under the
|
49
|
+
copyright of the software, but belong to whomever generated them,
|
50
|
+
and may be sold commercially, and may be aggregated with this
|
51
|
+
software.
|
52
|
+
|
53
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
54
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
55
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
56
|
+
PURPOSE.
|
data/README.md
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
|
2
|
+
Prawn Shadings
|
3
|
+
==============
|
4
|
+
|
5
|
+
[](https://travis-ci.org/cheba/prawn-shadings)
|
6
|
+
|
7
|
+
This is a Prawn extension that implements advanced PDF shadings.
|
8
|
+
|
9
|
+
Specifically, it a bit refactors already implemented linear and radial gradients
|
10
|
+
and adds a few more advanced ones.
|
11
|
+
|
12
|
+
Currently implemented shadings:
|
13
|
+
|
14
|
+
* Axial Shadings (or linear gradients) (PDF Type 2 Shading)
|
15
|
+
* Radial Shading (PDF Type 3 Shading)
|
16
|
+
* Free-Form Gouraud-Shaded Triangle Meshes (PDF Type 4 Shading)
|
17
|
+
* Lattice-Form Gouraud-Shaded Triangle Meshes (PDF Type 5 Shading)
|
18
|
+
* Coons Patch Meshes (PDF Type 6 Shading)
|
19
|
+
* Tensor-Product Patch Meshes (PDF Type 7 Shading)
|
20
|
+
|
21
|
+
API is simple but you need to know what data to supply. Please get familiar with
|
22
|
+
Section 4.6.3 of PDF Reference.
|
23
|
+
|
24
|
+
|
25
|
+
A Word of Warning
|
26
|
+
-----------------
|
27
|
+
|
28
|
+
Please note that this gem gives you ability use all these shadings in your
|
29
|
+
generated PDF but gives no guarantee that PDF will be rendered correctly.
|
30
|
+
|
31
|
+
Just a few examples:
|
32
|
+
|
33
|
+
* OS X Preview has problems with flags other than 0 in Type 4, 6 and 7 type
|
34
|
+
shadings.
|
35
|
+
* OS X Previews renders Type 7 shadings exactly the same as Type 6 shadings
|
36
|
+
ignoring all 4 extra control points.
|
37
|
+
* PDF.js can not render pretty much any of these.
|
38
|
+
|
39
|
+
Installation
|
40
|
+
------------
|
41
|
+
|
42
|
+
gem install prawn-shadings
|
43
|
+
|
44
|
+
|
45
|
+
Usage
|
46
|
+
-----
|
47
|
+
|
48
|
+
require 'prawn/shadings'
|
49
|
+
|
50
|
+
|
51
|
+
Testing
|
52
|
+
-------
|
53
|
+
|
54
|
+
To run the tests:
|
55
|
+
|
56
|
+
$ rake
|
57
|
+
|
58
|
+
|
59
|
+
Contributing
|
60
|
+
------------
|
61
|
+
|
62
|
+
1. Fork it.
|
63
|
+
2. Create a branch (`git checkout -b my_shading`)
|
64
|
+
3. Commit your changes (`git commit -am "Added Shading"`)
|
65
|
+
4. Push to the branch (`git push origin my_shading`)
|
66
|
+
5. Open a [Pull Request][1]
|
67
|
+
|
68
|
+
[1]: http://github.com/cheba/prawn-shadings/pulls
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
require 'rdoc/task'
|
3
|
+
|
4
|
+
desc "genrates documentation"
|
5
|
+
RDoc::Task.new do |rdoc|
|
6
|
+
rdoc.rdoc_files.include( "README.md",
|
7
|
+
"LICENSE", "BSDL",
|
8
|
+
"lib/" )
|
9
|
+
rdoc.main = "README.md"
|
10
|
+
rdoc.rdoc_dir = "doc/html"
|
11
|
+
rdoc.title = "Prawn Shadings Documentation"
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Run all rspec files"
|
15
|
+
RSpec::Core::RakeTask.new("spec")
|
16
|
+
|
17
|
+
desc "Run tests"
|
18
|
+
task default: :spec
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'prawn/shadings'
|
@@ -0,0 +1,649 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'prawn/graphics/patterns'
|
4
|
+
|
5
|
+
module Prawn
|
6
|
+
module Graphics
|
7
|
+
|
8
|
+
##
|
9
|
+
# This module implements Shading patterns.
|
10
|
+
#
|
11
|
+
# Both fill_gradient and stroke_gradient accept absolutely identical sets of
|
12
|
+
# arguments. The only difference, obviously, is that one sets fill shading
|
13
|
+
# and the other sets stroke shading.
|
14
|
+
#
|
15
|
+
# == Basic gradients
|
16
|
+
#
|
17
|
+
# Linear and radial gradients are one of the simplest shadings supported by
|
18
|
+
# PDF.
|
19
|
+
#
|
20
|
+
# === Linear gradient
|
21
|
+
#
|
22
|
+
# fill_gradient(from, to, color1, color2)
|
23
|
+
#
|
24
|
+
# from::
|
25
|
+
# This is the starting point of the gradient. It must be an arrays of
|
26
|
+
# two values: <tt>[x, y]</tt>.
|
27
|
+
#
|
28
|
+
# to::
|
29
|
+
# This is the ending point of the gradient. Format is the same:
|
30
|
+
# <tt>[x, y]</tt>.
|
31
|
+
#
|
32
|
+
# color1::
|
33
|
+
# Color of the gradient at the starting point.
|
34
|
+
#
|
35
|
+
# color2::
|
36
|
+
# Color of the gradient at the ending point of the gradient.
|
37
|
+
#
|
38
|
+
# Please note that both colors must be in the same color space.
|
39
|
+
#
|
40
|
+
# === Radial gradient
|
41
|
+
#
|
42
|
+
# fill_gradient(from, r1, to, r2, color1, color2)
|
43
|
+
#
|
44
|
+
# from::
|
45
|
+
# This is the starting point of the gradient. It must be an arrays of
|
46
|
+
# two values: <tt>[x, y]</tt>.
|
47
|
+
#
|
48
|
+
# r1::
|
49
|
+
# Radius of the starting circle.
|
50
|
+
#
|
51
|
+
# to::
|
52
|
+
# This is the ending point of the gradient. Format is the same:
|
53
|
+
# <tt>[x, y]</tt>.
|
54
|
+
#
|
55
|
+
# r2::
|
56
|
+
# Radius of the ending circle.
|
57
|
+
#
|
58
|
+
# color1::
|
59
|
+
# Color of the gradient at the starting point.
|
60
|
+
#
|
61
|
+
# color2::
|
62
|
+
# Color of the gradient at the ending point of the gradient.
|
63
|
+
#
|
64
|
+
# Please note that both colors must be in the same color space.
|
65
|
+
#
|
66
|
+
#
|
67
|
+
# == More complex gradients
|
68
|
+
#
|
69
|
+
# PDF is capable of creating very complex shadings. Though, this
|
70
|
+
# functionality is rarely used.
|
71
|
+
#
|
72
|
+
# Because this gradients require variable amounts of data to be properly
|
73
|
+
# constructed gradient methods are called with a single parameter stating
|
74
|
+
# the type of gradient you want to produce and a block that returns actual
|
75
|
+
# data for gradient.
|
76
|
+
#
|
77
|
+
# [Note]
|
78
|
+
# This types of shading are not supported by all renderers.
|
79
|
+
# For example PDF.js doesn't support any of this shadings. OS X Preview
|
80
|
+
# (10.8) has problems with flags other than 0 and renders Tensor-Product
|
81
|
+
# Patch Meshes exactly the same as Coons Patch Meshes.
|
82
|
+
#
|
83
|
+
# === Free-Form Gouraud-Shaded Triangle Meshes
|
84
|
+
#
|
85
|
+
# fill_gradient(:ffgstm) {
|
86
|
+
# [
|
87
|
+
# # vertices
|
88
|
+
# ]
|
89
|
+
# }
|
90
|
+
#
|
91
|
+
# The data is an Array of vertices. Each vertex has the following format:
|
92
|
+
# <tt>[flag, x, y, color]</tt>.
|
93
|
+
#
|
94
|
+
# Colors of all vertices must be in the same color space.
|
95
|
+
#
|
96
|
+
# Please refer to <b>PDF Refernce, Section 4.6.3, Shading Types, Type
|
97
|
+
# 4 Shadings</b> for more details on the meaning of the vertex fields.
|
98
|
+
#
|
99
|
+
#
|
100
|
+
# === Lattice-Form Gouraud-Shaded Triangle Meshes
|
101
|
+
#
|
102
|
+
# fill_gradient(:lfgstm) {
|
103
|
+
# [
|
104
|
+
# # data
|
105
|
+
# ]
|
106
|
+
# }
|
107
|
+
#
|
108
|
+
# The data is a rectangular matrix of vertices. That is an array of rows (arrays)
|
109
|
+
# of vertices. It must have at least 2 rows and at least 2 column. Each vertex has
|
110
|
+
# the following format: <tt>[x, y, color]</tt>.
|
111
|
+
#
|
112
|
+
# Colors of all vertices must be in the same color space.
|
113
|
+
#
|
114
|
+
# Please refer to <b>PDF Refernce, Section 4.6.3, Shading Types, Type
|
115
|
+
# 5 Shadings</b> for more details on the meaning of the vertex fields.
|
116
|
+
#
|
117
|
+
#
|
118
|
+
# === Coons Patch Meshes
|
119
|
+
#
|
120
|
+
# fill_gradient(:cpm) {
|
121
|
+
# [
|
122
|
+
# # data
|
123
|
+
# ]
|
124
|
+
# }
|
125
|
+
#
|
126
|
+
# The data is an array of patches. There are two types of patches. They're
|
127
|
+
# distinguished by their flag parameter.
|
128
|
+
#
|
129
|
+
# First one is a stand alone patches. Their flag = 0. They have a form of <tt>[0 (flag),
|
130
|
+
# x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, x6, y6, x7, y7, x8, y8, x9, y9, x10, y10,
|
131
|
+
# x11, y11, x12, y12, c1, c2, c3, c4]</tt>.
|
132
|
+
#
|
133
|
+
# The second is a edge-sharing patches. Their flag is either 1, 2, or 3. And
|
134
|
+
# they have 4 less points (because they borrow them from previous patch) and
|
135
|
+
# 2 less colors. They have a form of <tt>[flag, x5, y5, x6, y6, x7, y7, x8, y8, x9, y9,
|
136
|
+
# x10, y10, x11, y11, x12, y12, c3, c4]</tt>.
|
137
|
+
#
|
138
|
+
# Please refer to <b>PDF Refernce, Section 4.6.3, Shading Types, Type
|
139
|
+
# 6 Shadings</b> for more details on the meaning of the patch fields.
|
140
|
+
#
|
141
|
+
# === Tensor-Product Patch Meshes
|
142
|
+
#
|
143
|
+
# fill_gradient(:tppm) {
|
144
|
+
# [
|
145
|
+
# # data
|
146
|
+
# ]
|
147
|
+
# }
|
148
|
+
#
|
149
|
+
# This type of shading is very similar to Coons Patch Mesh except it has
|
150
|
+
# 4 extra pairs of coordinates in every patch, right before the colors.
|
151
|
+
#
|
152
|
+
# Please refer to <b>PDF Refernce, Section 4.6.3, Shading Types, Type
|
153
|
+
# 7 Shadings</b> for more details on the meaning of the patch fields.
|
154
|
+
#
|
155
|
+
module Patterns
|
156
|
+
|
157
|
+
##
|
158
|
+
# :call-seq:
|
159
|
+
# fill_gradient(from, to, color1, color2)
|
160
|
+
# fill_gradient(from, r1, to, r2, color1, color2)
|
161
|
+
# fill_gradient(complex_shading_type) { }
|
162
|
+
#
|
163
|
+
# Sets the fill gradient.
|
164
|
+
def fill_gradient(*args, &block)
|
165
|
+
if args[1].is_a?(Array) || args[2].is_a?(Array) || args[0].is_a?(Symbol)
|
166
|
+
set_gradient(:fill, *args, &block)
|
167
|
+
else
|
168
|
+
warn "[DEPRECATION] 'fill_gradient(point, width, height,...)' is deprecated in favor of 'fill_gradient(from, to,...)'. " +
|
169
|
+
"Old arguments will be removed in release 1.1"
|
170
|
+
set_gradient :fill, args[0], [args[0].first, args[0].last - args[2]], args[3], args[4]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# :call-seq:
|
176
|
+
# stroke_gradient(from, to, color1, color2)
|
177
|
+
# stroke_gradient(from, r1, to, r2, color1, color2)
|
178
|
+
# stroke_gradient(complex_shading_type) { }
|
179
|
+
#
|
180
|
+
# Sets the stroke gradient.
|
181
|
+
def stroke_gradient(*args, &block)
|
182
|
+
if args[1].is_a?(Array) || args[2].is_a?(Array) || args[0].is_a?(Symbol)
|
183
|
+
set_gradient(:stroke, *args, &block)
|
184
|
+
else
|
185
|
+
warn "[DEPRECATION] 'stroke_gradient(point, width, height,...)' is deprecated in favor of 'stroke_gradient(from, to,...)'. " +
|
186
|
+
"Old arguments will be removed in release 1.1"
|
187
|
+
set_gradient :stroke, args[0], [args[0].first, args[0].last - args[2]], args[3], args[4]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
private
|
192
|
+
|
193
|
+
def set_gradient(type, *grad, &block)
|
194
|
+
patterns = page.resources[:Pattern] ||= {}
|
195
|
+
|
196
|
+
registry_key = gradient_registry_key grad, &block
|
197
|
+
|
198
|
+
if patterns["SP#{registry_key}"]
|
199
|
+
shading = patterns["SP#{registry_key}"]
|
200
|
+
else
|
201
|
+
unless shading = gradient_registry[registry_key]
|
202
|
+
shading = gradient(*grad, &block)
|
203
|
+
gradient_registry[registry_key] = shading
|
204
|
+
end
|
205
|
+
|
206
|
+
patterns["SP#{registry_key}"] = shading
|
207
|
+
end
|
208
|
+
|
209
|
+
operator = case type
|
210
|
+
when :fill
|
211
|
+
'scn'
|
212
|
+
when :stroke
|
213
|
+
'SCN'
|
214
|
+
else
|
215
|
+
raise ArgumentError, "unknown type '#{type}'"
|
216
|
+
end
|
217
|
+
|
218
|
+
set_color_space type, :Pattern
|
219
|
+
add_content "/SP#{registry_key} #{operator}"
|
220
|
+
end
|
221
|
+
|
222
|
+
def gradient_registry_key(gradient)
|
223
|
+
if gradient[0].is_a?(Symbol) # all kinds of patches
|
224
|
+
[
|
225
|
+
gradient[0],
|
226
|
+
*yield
|
227
|
+
]
|
228
|
+
elsif gradient[1].is_a?(Array) # axial
|
229
|
+
[
|
230
|
+
map_to_absolute(gradient[0]),
|
231
|
+
map_to_absolute(gradient[1]),
|
232
|
+
gradient[2], gradient[3]
|
233
|
+
]
|
234
|
+
else # radial
|
235
|
+
[
|
236
|
+
map_to_absolute(gradient[0]),
|
237
|
+
gradient[1],
|
238
|
+
map_to_absolute(gradient[2]),
|
239
|
+
gradient[3],
|
240
|
+
gradient[4], gradient[5]
|
241
|
+
]
|
242
|
+
end.hash
|
243
|
+
end
|
244
|
+
|
245
|
+
def gradient_registry
|
246
|
+
@gradient_registry ||= {}
|
247
|
+
end
|
248
|
+
|
249
|
+
def gradient(*args, &block)
|
250
|
+
if args.length != 1 && args.length != 4 && args.length != 6
|
251
|
+
raise ArgumentError, "Unknown type of gradient: #{args.inspect}"
|
252
|
+
end
|
253
|
+
|
254
|
+
case args.length
|
255
|
+
when 1
|
256
|
+
case args.first
|
257
|
+
when :ffgstm # Free-Form Gouraud-Shaded Triangle Meshes
|
258
|
+
shading_type_4(&block)
|
259
|
+
when :lfgstm # Lattice-Form Gouraud-Shaded Triangle Meshes
|
260
|
+
shading_type_5(&block)
|
261
|
+
when :cpm # Coons Patch Meshes
|
262
|
+
shading_type_6(&block)
|
263
|
+
when :tppm # Tensor-Product Patch Meshes
|
264
|
+
shading_type_7(&block)
|
265
|
+
else
|
266
|
+
raise ArgumentError, "Unknown type of gradient: #{args.inspect}"
|
267
|
+
end
|
268
|
+
when 4
|
269
|
+
shading_type_2(*args)
|
270
|
+
when 6
|
271
|
+
shading_type_3(*args)
|
272
|
+
else
|
273
|
+
raise ArgumentError, "Unknown type of gradient: #{args.inspect}"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def shading_type_2(from, to, from_color, to_color)
|
278
|
+
color1 = normalize_color(from_color).dup.freeze
|
279
|
+
color2 = normalize_color(to_color).dup.freeze
|
280
|
+
|
281
|
+
if color_type(color1) != color_type(color2)
|
282
|
+
raise ArgumentError, "Both colors must be of the same color space: #{color1.inspect} and #{color2.inspect}"
|
283
|
+
end
|
284
|
+
|
285
|
+
process_color color1
|
286
|
+
process_color color2
|
287
|
+
|
288
|
+
shader = ref!({
|
289
|
+
:FunctionType => 2,
|
290
|
+
:Domain => [0.0, 1.0],
|
291
|
+
:C0 => color1,
|
292
|
+
:C1 => color2,
|
293
|
+
:N => 1.0,
|
294
|
+
})
|
295
|
+
|
296
|
+
shading = ref!({
|
297
|
+
:ShadingType => 2, # axial shading
|
298
|
+
:ColorSpace => color_space(color1),
|
299
|
+
:Coords => [0, 0, to.first - from.first, to.last - from.last],
|
300
|
+
:Function => shader,
|
301
|
+
:Extend => [true, true],
|
302
|
+
})
|
303
|
+
|
304
|
+
ref!({
|
305
|
+
:PatternType => 2, # shading pattern
|
306
|
+
:Shading => shading,
|
307
|
+
:Matrix => [1, 0, 0, 1] + map_to_absolute(from),
|
308
|
+
})
|
309
|
+
end
|
310
|
+
|
311
|
+
def shading_type_3(from, r1, to, r2, from_color, to_color)
|
312
|
+
color1 = normalize_color(from_color).dup.freeze
|
313
|
+
color2 = normalize_color(to_color).dup.freeze
|
314
|
+
|
315
|
+
if color_type(color1) != color_type(color2)
|
316
|
+
raise ArgumentError, "Both colors must be of the same color space: #{color1.inspect} and #{color2.inspect}"
|
317
|
+
end
|
318
|
+
|
319
|
+
process_color color1
|
320
|
+
process_color color2
|
321
|
+
|
322
|
+
shader = ref!({
|
323
|
+
:FunctionType => 2,
|
324
|
+
:Domain => [0.0, 1.0],
|
325
|
+
:C0 => color1,
|
326
|
+
:C1 => color2,
|
327
|
+
:N => 1.0,
|
328
|
+
})
|
329
|
+
|
330
|
+
shading = ref!({
|
331
|
+
:ShadingType => 3, # radial shading
|
332
|
+
:ColorSpace => color_space(color1),
|
333
|
+
:Coords => [0, 0, r1, to.first - from.first, to.last - from.last, r2],
|
334
|
+
:Function => shader,
|
335
|
+
:Extend => [true, true],
|
336
|
+
})
|
337
|
+
|
338
|
+
ref!({
|
339
|
+
:PatternType => 2, # shading pattern
|
340
|
+
:Shading => shading,
|
341
|
+
:Matrix => [1, 0, 0, 1] + map_to_absolute(from),
|
342
|
+
})
|
343
|
+
end
|
344
|
+
|
345
|
+
def shading_type_4
|
346
|
+
data = yield
|
347
|
+
|
348
|
+
x_min = x_max = data[0][1]
|
349
|
+
y_min = y_max = data[0][2]
|
350
|
+
data_color_space = color_space(data[0][3])
|
351
|
+
|
352
|
+
data.each do |(_, x, y, c, extra)|
|
353
|
+
x_min = x if x_min > x
|
354
|
+
x_max = x if x_max < x
|
355
|
+
y_min = y if y_min > y
|
356
|
+
y_max = y if y_max < y
|
357
|
+
if color_space(c) != data_color_space
|
358
|
+
raise ArgumentError, "Colors of all vertices must be in the same color space. Expected #{data_color_space} got #{color_space(c)}"
|
359
|
+
end
|
360
|
+
if extra
|
361
|
+
raise ArgumentError, "Unexpected extra data in vertices stream: #{extra.inspect}"
|
362
|
+
end
|
363
|
+
end
|
364
|
+
x_min = x_min.to_f
|
365
|
+
x_max = x_max.to_f
|
366
|
+
y_min = y_min.to_f
|
367
|
+
y_max = y_max.to_f
|
368
|
+
dx = x_max - x_min
|
369
|
+
dy = y_max - y_min
|
370
|
+
|
371
|
+
|
372
|
+
shading = ref!({
|
373
|
+
:ShadingType => 4, # Free-Form Gouraud-Shaded Triangle Meshes
|
374
|
+
:ColorSpace => data_color_space,
|
375
|
+
:BitsPerCoordinate => 32,
|
376
|
+
:BitsPerComponent => 8,
|
377
|
+
:BitsPerFlag => 8,
|
378
|
+
:Decode => [x_min, x_max, y_min, y_max, 0, 1, 0, 1, 0, 1]
|
379
|
+
})
|
380
|
+
|
381
|
+
data.each do |(f, x, y, color)|
|
382
|
+
unless [0, 1, 2].include? f
|
383
|
+
raise ArgumentError, "Flag must be 0, 1, or 2. Got #{f}."
|
384
|
+
end
|
385
|
+
|
386
|
+
shading << [
|
387
|
+
f,
|
388
|
+
normalize_coord(x, x_min, dx), normalize_coord(y, y_min, dy),
|
389
|
+
*(normalize_color(color).map{|c| (c * 255.0).round })
|
390
|
+
].pack('CNNC*')
|
391
|
+
end
|
392
|
+
|
393
|
+
|
394
|
+
ref!({
|
395
|
+
:PatternType => 2, # shading pattern
|
396
|
+
:Shading => shading,
|
397
|
+
:Matrix => [1, 0,
|
398
|
+
0, 1] + map_to_absolute([0, 0]),
|
399
|
+
})
|
400
|
+
end
|
401
|
+
|
402
|
+
def shading_type_5
|
403
|
+
data = yield
|
404
|
+
|
405
|
+
x_min = x_max = data[0][0][0]
|
406
|
+
y_min = y_max = data[0][0][1]
|
407
|
+
data_color_space = color_space(data[0][0][2])
|
408
|
+
|
409
|
+
if data.length < 2
|
410
|
+
raise ArgumentError, "Lattice-Form Gouraud-Shaded Triangle Meshes Shading requires at least 2 rows of vertices"
|
411
|
+
end
|
412
|
+
|
413
|
+
vertices_per_row = data[0].length
|
414
|
+
if vertices_per_row < 2
|
415
|
+
raise ArgumentError, "Lattice-Form Gouraud-Shaded Triangle Meshes Shading requires at least 2 vertices per row"
|
416
|
+
end
|
417
|
+
|
418
|
+
data.each do |row|
|
419
|
+
if vertices_per_row != row.length
|
420
|
+
raise ArgumentError, "Lattice-Form Gouraud-Shaded Triangle Meshes Shading requires data to be organized in rectangular matrix"
|
421
|
+
end
|
422
|
+
|
423
|
+
row.each do |(x, y, c)|
|
424
|
+
x_min = x if x_min > x
|
425
|
+
x_max = x if x_max < x
|
426
|
+
y_min = y if y_min > y
|
427
|
+
y_max = y if y_max < y
|
428
|
+
|
429
|
+
if color_space(c) != data_color_space
|
430
|
+
raise ArgumentError, "Colors of all vertices must be in the same color space. Expected #{data_color_space} got #{color_space(c)}"
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
x_min = x_min.to_f
|
435
|
+
x_max = x_max.to_f
|
436
|
+
y_min = y_min.to_f
|
437
|
+
y_max = y_max.to_f
|
438
|
+
dx = x_max - x_min
|
439
|
+
dy = y_max - y_min
|
440
|
+
|
441
|
+
|
442
|
+
shading = ref!({
|
443
|
+
:ShadingType => 5, # Lattice-Form Gouraud-Shaded Triangle Meshes
|
444
|
+
:ColorSpace => :DeviceRGB,
|
445
|
+
:BitsPerCoordinate => 32,
|
446
|
+
:BitsPerComponent => 8,
|
447
|
+
:VerticesPerRow => data[0].length,
|
448
|
+
:Decode => [x_min, x_max, y_min, y_max, 0, 1, 0, 1, 0, 1]
|
449
|
+
})
|
450
|
+
|
451
|
+
|
452
|
+
data.each do |row|
|
453
|
+
row.each do |(x, y, color)|
|
454
|
+
shading << [
|
455
|
+
normalize_coord(x, x_min, dx), normalize_coord(y, y_min, dy),
|
456
|
+
*(normalize_color(color).map{|c| (c * 255.0).round })
|
457
|
+
].pack('NNC*')
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
|
462
|
+
ref!({
|
463
|
+
:PatternType => 2, # shading pattern
|
464
|
+
:Shading => shading,
|
465
|
+
:Matrix => [1, 0,
|
466
|
+
0, 1] + map_to_absolute([0, 0]),
|
467
|
+
})
|
468
|
+
end
|
469
|
+
|
470
|
+
def shading_type_6
|
471
|
+
data = yield
|
472
|
+
|
473
|
+
x_min = x_max = data[0][1]
|
474
|
+
y_min = y_max = data[0][2]
|
475
|
+
data_color_space = color_space(data[0][25])
|
476
|
+
|
477
|
+
data.each do |values|
|
478
|
+
f = values[0]
|
479
|
+
coords = if f == 0
|
480
|
+
values[1..24]
|
481
|
+
else
|
482
|
+
values[1..16]
|
483
|
+
end
|
484
|
+
coords.each_with_index do |c, i|
|
485
|
+
if i.even? # x coordinate
|
486
|
+
x_min = c if x_min > c
|
487
|
+
x_max = c if x_max < c
|
488
|
+
else # y coordinate
|
489
|
+
y_min = c if y_min > c
|
490
|
+
y_max = c if y_max < c
|
491
|
+
end
|
492
|
+
end
|
493
|
+
end
|
494
|
+
x_min = x_min.to_f
|
495
|
+
x_max = x_max.to_f
|
496
|
+
y_min = y_min.to_f
|
497
|
+
y_max = y_max.to_f
|
498
|
+
dx = x_max - x_min
|
499
|
+
dy = y_max - y_min
|
500
|
+
|
501
|
+
|
502
|
+
shading = ref!({
|
503
|
+
:ShadingType => 6, # Coons Patch Meshes
|
504
|
+
:ColorSpace => data_color_space,
|
505
|
+
:BitsPerCoordinate => 32,
|
506
|
+
:BitsPerComponent => 8,
|
507
|
+
:BitsPerFlag => 8,
|
508
|
+
:Decode => [x_min, x_max, y_min, y_max, 0, 1, 0, 1, 0, 1]
|
509
|
+
})
|
510
|
+
|
511
|
+
|
512
|
+
data.each do |values|
|
513
|
+
f = values.shift
|
514
|
+
unless [0, 1, 2, 3].include? f
|
515
|
+
raise ArgumentError, "Flag must be 0, 1, 2, or 3. Got #{f}."
|
516
|
+
end
|
517
|
+
|
518
|
+
if f == 0
|
519
|
+
coords = values.shift(24)
|
520
|
+
colors = values.shift(4)
|
521
|
+
else
|
522
|
+
coords = values.shift(16)
|
523
|
+
colors = values.shift(2)
|
524
|
+
end
|
525
|
+
|
526
|
+
shading << [f].pack('C')
|
527
|
+
|
528
|
+
coords.each_with_index do |c, i|
|
529
|
+
if i.even? # x coordinate
|
530
|
+
shading << [normalize_coord(c, x_min, dx)].pack('N')
|
531
|
+
else
|
532
|
+
shading << [normalize_coord(c, y_min, dy)].pack('N')
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
colors.each do |color|
|
537
|
+
if color_space(color) != data_color_space
|
538
|
+
raise ArgumentError, "Colors of all vertices must be in the same color space. Expected #{data_color_space} got #{color_space(color)}"
|
539
|
+
end
|
540
|
+
shading << normalize_color(color).map{|c| (c * 255.0).round }.pack('C*')
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
|
545
|
+
ref!({
|
546
|
+
:PatternType => 2, # shading pattern
|
547
|
+
:Shading => shading,
|
548
|
+
:Matrix => [1, 0,
|
549
|
+
0, 1] + map_to_absolute([0, 0]),
|
550
|
+
})
|
551
|
+
end
|
552
|
+
|
553
|
+
def shading_type_7
|
554
|
+
data = yield
|
555
|
+
|
556
|
+
x_min = x_max = data[0][1]
|
557
|
+
y_min = y_max = data[0][2]
|
558
|
+
data_color_space = color_space(data[0][33])
|
559
|
+
|
560
|
+
data.each do |values|
|
561
|
+
f = values[0]
|
562
|
+
coords = if f == 0
|
563
|
+
values[1..32]
|
564
|
+
else
|
565
|
+
values[1..24]
|
566
|
+
end
|
567
|
+
coords.each_with_index do |c, i|
|
568
|
+
if i.even? # x coordinate
|
569
|
+
x_min = c if x_min > c
|
570
|
+
x_max = c if x_max < c
|
571
|
+
else # y coordinate
|
572
|
+
y_min = c if y_min > c
|
573
|
+
y_max = c if y_max < c
|
574
|
+
end
|
575
|
+
end
|
576
|
+
end
|
577
|
+
x_min = x_min.to_f
|
578
|
+
x_max = x_max.to_f
|
579
|
+
y_min = y_min.to_f
|
580
|
+
y_max = y_max.to_f
|
581
|
+
dx = x_max - x_min
|
582
|
+
dy = y_max - y_min
|
583
|
+
|
584
|
+
|
585
|
+
shading = ref!({
|
586
|
+
:ShadingType => 7, # Tensor-Product Patch Meshes
|
587
|
+
:ColorSpace => data_color_space,
|
588
|
+
:BitsPerCoordinate => 32,
|
589
|
+
:BitsPerComponent => 8,
|
590
|
+
:BitsPerFlag => 8,
|
591
|
+
:Decode => [x_min, x_max, y_min, y_max, 0, 1, 0, 1, 0, 1]
|
592
|
+
})
|
593
|
+
|
594
|
+
|
595
|
+
data.each do |values|
|
596
|
+
f = values.shift
|
597
|
+
unless [0, 1, 2, 3].include? f
|
598
|
+
raise ArgumentError, "Flag must be 0, 1, 2, or 3. Got #{f}."
|
599
|
+
end
|
600
|
+
|
601
|
+
if f == 0
|
602
|
+
coords = values.shift(32)
|
603
|
+
colors = values.shift(4)
|
604
|
+
else
|
605
|
+
coords = values.shift(24)
|
606
|
+
colors = values.shift(2)
|
607
|
+
end
|
608
|
+
|
609
|
+
shading << [f].pack('C')
|
610
|
+
|
611
|
+
coords.each_with_index do |c, i|
|
612
|
+
if i.even? # x coordinate
|
613
|
+
shading << [normalize_coord(c, x_min, dx)].pack('N')
|
614
|
+
else
|
615
|
+
shading << [normalize_coord(c, y_min, dy)].pack('N')
|
616
|
+
end
|
617
|
+
end
|
618
|
+
|
619
|
+
colors.each do |color|
|
620
|
+
if color_space(color) != data_color_space
|
621
|
+
raise ArgumentError, "Colors of all vertices must be in the same color space. Expected #{data_color_space} got #{color_space(color)}"
|
622
|
+
end
|
623
|
+
shading << normalize_color(color).map{|c| (c * 255.0).round }.pack('C*')
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
|
628
|
+
ref!({
|
629
|
+
:PatternType => 2, # shading pattern
|
630
|
+
:Shading => shading,
|
631
|
+
:Matrix => [1, 0,
|
632
|
+
0, 1] + map_to_absolute([0, 0]),
|
633
|
+
})
|
634
|
+
end
|
635
|
+
|
636
|
+
def normalize_coord(coord, min, delta)
|
637
|
+
diff = (coord.to_f - min) / delta
|
638
|
+
if diff > 1
|
639
|
+
diff = 1.0
|
640
|
+
elsif diff < 0
|
641
|
+
diff = 0.0;
|
642
|
+
end
|
643
|
+
|
644
|
+
(diff * 0xffffffff).round
|
645
|
+
end
|
646
|
+
end
|
647
|
+
end
|
648
|
+
end
|
649
|
+
|