prawn-shadings 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/cheba/prawn-shadings.png)](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
|
+
|