rqrcode 0.1.0 → 0.2.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.
- data/CHANGELOG +6 -1
- data/COPYING +19 -0
- data/README +44 -44
- data/lib/rqrcode/core_ext/array.rb +1 -1
- data/lib/rqrcode/core_ext/array/behavior.rb +2 -2
- data/lib/rqrcode/core_ext/integer.rb +1 -1
- data/lib/rqrcode/core_ext/integer/bitwise.rb +2 -2
- data/lib/rqrcode/extensions.rb +0 -0
- data/lib/rqrcode/extensions/image.rb +1006 -0
- data/lib/rqrcode/qrcode.rb +3 -923
- data/lib/rqrcode/qrcode/qr_8bit_byte.rb +35 -0
- data/lib/rqrcode/qrcode/qr_bit_buffer.rb +56 -0
- data/lib/rqrcode/qrcode/qr_code.rb +394 -0
- data/lib/rqrcode/qrcode/qr_math.rb +63 -0
- data/lib/rqrcode/qrcode/qr_polynomial.rb +78 -0
- data/lib/rqrcode/qrcode/qr_rs_block.rb +134 -0
- data/lib/rqrcode/qrcode/qr_util.rb +254 -0
- data/test/{unit/qrcode_test.rb → runtest.rb} +2 -1
- metadata +23 -22
- data/test/test_helper.rb +0 -2
- data/test/unit/test_data.rb +0 -21
data/CHANGELOG
CHANGED
data/COPYING
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2008 Duncan Robertson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to
|
5
|
+
deal in the Software without restriction, including without limitation the
|
6
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
7
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
19
|
+
|
data/README
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
=
|
1
|
+
= rQRCode, Encode QRCodes
|
2
2
|
|
3
3
|
rQRCode is a library for encoding QR Codes in Ruby. It has a simple interface with all the standard qrcode options. It was adapted from the Javascript library by Kazuhiko Arase.
|
4
4
|
|
@@ -6,16 +6,16 @@ rQRCode is a library for encoding QR Codes in Ruby. It has a simple interface wi
|
|
6
6
|
|
7
7
|
Let's clear up some rQRCode stuff.
|
8
8
|
|
9
|
-
# rQRCode is a *standalone library*. It requires no other libraries. Just Ruby!
|
10
|
-
# It is an encoding library. You can't decode QR codes with it.
|
11
|
-
# The interface is simple and assumes you just want to encode a string into a QR code
|
12
|
-
# QR code is trademarked by Denso Wave inc
|
9
|
+
# rQRCode is a *standalone library*. It requires no other libraries. Just Ruby!
|
10
|
+
# It is an encoding library. You can't decode QR codes with it.
|
11
|
+
# The interface is simple and assumes you just want to encode a string into a QR code
|
12
|
+
# QR code is trademarked by Denso Wave inc
|
13
13
|
|
14
14
|
= Rescources
|
15
15
|
|
16
|
-
# wikipedia [http://en.wikipedia.org/wiki/QR_Code]
|
17
|
-
# Denso-Wave website [http://www.denso-wave.com/qrcode/index-e.html]
|
18
|
-
# kaywa [http://qrcode.kaywa.com/]
|
16
|
+
# wikipedia [http://en.wikipedia.org/wiki/QR_Code]
|
17
|
+
# Denso-Wave website [http://www.denso-wave.com/qrcode/index-e.html]
|
18
|
+
# kaywa [http://qrcode.kaywa.com/]
|
19
19
|
|
20
20
|
|
21
21
|
== Installing
|
@@ -44,44 +44,44 @@ You have installed the gem already, yeah?
|
|
44
44
|
|
45
45
|
=== Simple QRCode generation to view (RubyOnRails)
|
46
46
|
|
47
|
-
|
47
|
+
<b>Controller:</b>
|
48
48
|
@qr = RQRCode::QRCode.new( 'my string to generate', :size => 4, :level => :h )
|
49
49
|
|
50
|
-
|
51
|
-
<style type="text/css">
|
52
|
-
table {
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
}
|
58
|
-
td {
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
}
|
68
|
-
td.black { background-color: #000; }
|
69
|
-
td.white { background-color: #fff; }
|
70
|
-
</style>
|
71
|
-
|
72
|
-
<table>
|
73
|
-
<% (0...@qr.module_count).each do |x| %>
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
<% end %>
|
84
|
-
</table>
|
50
|
+
<b>View: (minimal styling added)</b>
|
51
|
+
<style type="text/css">
|
52
|
+
table {
|
53
|
+
border-width: 0;
|
54
|
+
border-style: none;
|
55
|
+
border-color: #0000ff;
|
56
|
+
border-collapse: collapse;
|
57
|
+
}
|
58
|
+
td {
|
59
|
+
border-width: 0;
|
60
|
+
border-style: none;
|
61
|
+
border-color: #0000ff;
|
62
|
+
border-collapse: collapse;
|
63
|
+
padding: 0;
|
64
|
+
margin: 0;
|
65
|
+
width: 10px;
|
66
|
+
height: 10px;
|
67
|
+
}
|
68
|
+
td.black { background-color: #000; }
|
69
|
+
td.white { background-color: #fff; }
|
70
|
+
</style>
|
71
|
+
|
72
|
+
<table>
|
73
|
+
<% (0...@qr.module_count).each do |x| %>
|
74
|
+
<tr>
|
75
|
+
<% (0...@qr.module_count).each do |y| %>
|
76
|
+
<% if @qr.is_dark(x,y) %>
|
77
|
+
<td class="black"/>
|
78
|
+
<% else %>
|
79
|
+
<td class="white"/>
|
80
|
+
<% end %>
|
81
|
+
<% end %>
|
82
|
+
</tr>
|
83
|
+
<% end %>
|
84
|
+
</table>
|
85
85
|
|
86
86
|
== Contact
|
87
87
|
|
File without changes
|
@@ -0,0 +1,1006 @@
|
|
1
|
+
# file: pureimage.rb
|
2
|
+
#
|
3
|
+
# Copyright (C) 2005 NISHIMOTO Keisuke.
|
4
|
+
|
5
|
+
require 'iconv'
|
6
|
+
require 'zlib'
|
7
|
+
|
8
|
+
module PureImage
|
9
|
+
|
10
|
+
######################################################################
|
11
|
+
# Font.
|
12
|
+
######################################################################
|
13
|
+
|
14
|
+
class CharImage
|
15
|
+
|
16
|
+
attr_reader :pixels, :width
|
17
|
+
|
18
|
+
def initialize(width, pixels)
|
19
|
+
@width = width
|
20
|
+
@pixels = pixels
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
# Font file format:
|
26
|
+
#
|
27
|
+
# class Location
|
28
|
+
# short code
|
29
|
+
# int offset
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# class CharImage
|
33
|
+
# short width
|
34
|
+
# byte[] pixels
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# class Font
|
38
|
+
# int length
|
39
|
+
# short height
|
40
|
+
# short ascent
|
41
|
+
# short descent
|
42
|
+
# Location[] locations
|
43
|
+
# CharImage[] images
|
44
|
+
# end
|
45
|
+
|
46
|
+
class Font
|
47
|
+
|
48
|
+
def initialize(file)
|
49
|
+
begin
|
50
|
+
@iconv = $KCODE != 'NONE' ? Iconv.new('UTF-8', $KCODE) : nil
|
51
|
+
inp = File.new(file, "r")
|
52
|
+
inp.binmode
|
53
|
+
@file = file
|
54
|
+
rescue
|
55
|
+
raise "Cannot read font file: " + file
|
56
|
+
end
|
57
|
+
begin
|
58
|
+
length = read_int(inp)
|
59
|
+
@height = read_short(inp)
|
60
|
+
@ascent = read_short(inp)
|
61
|
+
@descent = read_short(inp)
|
62
|
+
@locations = Hash.new
|
63
|
+
for i in 0..(length - 1)
|
64
|
+
code = read_short(inp)
|
65
|
+
offset = read_int(inp)
|
66
|
+
@locations[code] = offset
|
67
|
+
end
|
68
|
+
@font = Hash.new()
|
69
|
+
read_char_image(0x20)
|
70
|
+
rescue
|
71
|
+
raise "Cannot read font file: " + file
|
72
|
+
ensure
|
73
|
+
inp.close
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def image(code)
|
78
|
+
char_image = @font[code]
|
79
|
+
if char_image == nil then
|
80
|
+
begin
|
81
|
+
char_image = read_char_image(code)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
return char_image != nil ? char_image : @font[0x20]
|
85
|
+
end
|
86
|
+
|
87
|
+
def string_width(str)
|
88
|
+
unicode = to_unicode(str)
|
89
|
+
width = 0
|
90
|
+
for i in 0..(unicode.length - 1)
|
91
|
+
width += width(unicode[i])
|
92
|
+
end
|
93
|
+
return width
|
94
|
+
end
|
95
|
+
|
96
|
+
def width(code)
|
97
|
+
return image(code).width
|
98
|
+
end
|
99
|
+
|
100
|
+
def height
|
101
|
+
return @height
|
102
|
+
end
|
103
|
+
|
104
|
+
def ascent
|
105
|
+
return @ascent
|
106
|
+
end
|
107
|
+
|
108
|
+
def descent
|
109
|
+
return @descent
|
110
|
+
end
|
111
|
+
|
112
|
+
def to_unicode(str)
|
113
|
+
# 0000 0000-0000 007F | 0xxxxxxx
|
114
|
+
# 0000 0080-0000 07FF | 110xxxxx 10xxxxxx (1)
|
115
|
+
# 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx (2-3)
|
116
|
+
# 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (4-6)
|
117
|
+
unicode = Array.new
|
118
|
+
mode = 0; code = 0
|
119
|
+
utf8_str = @iconv != nil ? @iconv.iconv(str) : str
|
120
|
+
utf8_str.each_byte {|c|
|
121
|
+
if mode == 0 then
|
122
|
+
if c <= 0x7f then
|
123
|
+
code = c
|
124
|
+
unicode.push(code)
|
125
|
+
elsif c <= 0xdf then
|
126
|
+
code = c & 0x1f
|
127
|
+
mode = 1
|
128
|
+
elsif c <= 0xef then
|
129
|
+
code = c & 0xf
|
130
|
+
mode = 2
|
131
|
+
elsif c < 0xf7 then
|
132
|
+
code = c & 0x7
|
133
|
+
mode = 4
|
134
|
+
end
|
135
|
+
elsif mode == 1 then
|
136
|
+
code = code << 6 | (c & 0x3f)
|
137
|
+
mode = 0
|
138
|
+
unicode.push(code);
|
139
|
+
elsif mode == 2 then
|
140
|
+
code = code << 6 | (c & 0x3f)
|
141
|
+
mode = 3
|
142
|
+
elsif mode == 3 then
|
143
|
+
code = code << 6 | (c & 0x3f)
|
144
|
+
mode = 0
|
145
|
+
unicode.push(code)
|
146
|
+
elsif mode == 4 then
|
147
|
+
code = code << 6 | (c & 0x3f)
|
148
|
+
mode = 5
|
149
|
+
elsif mode == 5 then
|
150
|
+
code = code << 6 | (c & 0x3f)
|
151
|
+
mode = 5
|
152
|
+
elsif mode == 5 then
|
153
|
+
code = code << 6 | (c & 0x3f)
|
154
|
+
mode = 0
|
155
|
+
unicode.push(code)
|
156
|
+
end
|
157
|
+
}
|
158
|
+
return unicode
|
159
|
+
end
|
160
|
+
|
161
|
+
def read_char_image(code)
|
162
|
+
begin
|
163
|
+
inp = File.new(@file, "r")
|
164
|
+
inp.binmode
|
165
|
+
rescue
|
166
|
+
raise "Cannot read font file: " + file
|
167
|
+
end
|
168
|
+
begin
|
169
|
+
offset = @locations[code]
|
170
|
+
if offset == nil then
|
171
|
+
return nil
|
172
|
+
end
|
173
|
+
inp.seek(offset)
|
174
|
+
width = read_short(inp)
|
175
|
+
pixels = Array.new(width * @height)
|
176
|
+
for i in 0..(pixels.length - 1)
|
177
|
+
pixels[i] = inp.getc
|
178
|
+
end
|
179
|
+
@font[code] = CharImage.new(width, pixels)
|
180
|
+
rescue
|
181
|
+
raise "Cannot read font file: " + @file
|
182
|
+
ensure
|
183
|
+
inp.close
|
184
|
+
end
|
185
|
+
end
|
186
|
+
private :read_char_image
|
187
|
+
|
188
|
+
def read_short(inp)
|
189
|
+
return (inp.getc & 0xff) << 8 | (inp.getc & 0xff)
|
190
|
+
end
|
191
|
+
private :read_short
|
192
|
+
|
193
|
+
def read_int(inp)
|
194
|
+
return (inp.getc & 0xff) << 24 | (inp.getc & 0xff) << 16 \
|
195
|
+
| (inp.getc & 0xff) << 8 | (inp.getc & 0xff)
|
196
|
+
end
|
197
|
+
private :read_int
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
######################################################################
|
202
|
+
# Image.
|
203
|
+
######################################################################
|
204
|
+
|
205
|
+
class Image
|
206
|
+
|
207
|
+
attr_reader :width, :height, :enable_alpha, :pixels
|
208
|
+
attr_accessor :color, :alpha, :font
|
209
|
+
|
210
|
+
def initialize(width, height, c = 0xffffff, enable_alpha = false)
|
211
|
+
size = width * height
|
212
|
+
@width = width
|
213
|
+
@height = height
|
214
|
+
@enable_alpha = enable_alpha
|
215
|
+
@color = 0x000000
|
216
|
+
@alpha = 255
|
217
|
+
@pixels = Array.new(size)
|
218
|
+
r = (c >> 16) & 0xff
|
219
|
+
g = (c >> 8) & 0xff
|
220
|
+
b = c & 0xff
|
221
|
+
a = 0xff
|
222
|
+
@pixels.fill([r, g, b, a])
|
223
|
+
end
|
224
|
+
|
225
|
+
def get(x, y)
|
226
|
+
if x >= 0 && x < @width && y >= 0 && y < @height then
|
227
|
+
return @pixels[x + y * @width]
|
228
|
+
else
|
229
|
+
return 0
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def set(x, y, c)
|
234
|
+
if x >= 0 && x < @width && y >= 0 && y < @height then
|
235
|
+
@pixels[x + y * @width] = c
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def draw_string(str, x, y, c = @color, a = @alpha)
|
240
|
+
red = (c >> 16) & 0xff
|
241
|
+
green = (c >> 8) & 0xff
|
242
|
+
blue = c & 0xff
|
243
|
+
height = @font.height
|
244
|
+
ascent = @font.ascent
|
245
|
+
unicode_str = @font.to_unicode(str)
|
246
|
+
unicode_str.each {|code|
|
247
|
+
char_image = @font.image(code)
|
248
|
+
width = char_image.width
|
249
|
+
pixels = char_image.pixels
|
250
|
+
index = 0
|
251
|
+
for j in 0..(height - 1)
|
252
|
+
y0 = y - ascent + j
|
253
|
+
for i in 0..(width - 1)
|
254
|
+
font_alpha = pixels[index]
|
255
|
+
if font_alpha > 0 then
|
256
|
+
x0 = x + i
|
257
|
+
col = get(x0, y0)
|
258
|
+
alpha = (a * font_alpha / 255).to_i
|
259
|
+
rev_alpha = 255 - alpha
|
260
|
+
r = (col[0] * rev_alpha + red * alpha) / 255
|
261
|
+
r = r <= 255 ? r.to_i : 255
|
262
|
+
g = (col[1] * rev_alpha + green * alpha) / 255
|
263
|
+
g = g <= 255 ? g.to_i : 255
|
264
|
+
b = (col[2] * rev_alpha + blue * alpha) / 255
|
265
|
+
b = b <= 255 ? b.to_i : 255
|
266
|
+
col = [r, g, b, col[3]]
|
267
|
+
set(x0, y0, col)
|
268
|
+
end
|
269
|
+
index += 1
|
270
|
+
end
|
271
|
+
end
|
272
|
+
x += width
|
273
|
+
}
|
274
|
+
end
|
275
|
+
|
276
|
+
def draw_line(x0, y0, x1, y1, c = @color, a = @alpha)
|
277
|
+
if a <= 0 then return end
|
278
|
+
red = (c >> 16) & 0xff
|
279
|
+
green = (c >> 8) & 0xff
|
280
|
+
blue = c & 0xff
|
281
|
+
dx = (x1 - x0).abs
|
282
|
+
dy = (y1 - y0).abs
|
283
|
+
if(dx >= dy) then
|
284
|
+
# dx >= dy
|
285
|
+
if x0 > x1 then
|
286
|
+
tmp = x0; x0 = x1; x1 = tmp
|
287
|
+
tmp = y0; y0 = y1; y1 = tmp
|
288
|
+
end
|
289
|
+
y = y0; tmp = (dx / 2).to_i; iy = y0 < y1 ? 1 : -1
|
290
|
+
if a == 255 then
|
291
|
+
for x in x0..x1
|
292
|
+
col = get(x, y)
|
293
|
+
set(x, y, [red, green, blue, col[3]])
|
294
|
+
tmp -= dy
|
295
|
+
if tmp < 0 then
|
296
|
+
y += iy
|
297
|
+
tmp += dx
|
298
|
+
end
|
299
|
+
end
|
300
|
+
else
|
301
|
+
alpha = a
|
302
|
+
rev_alpha = 255 - a
|
303
|
+
for x in x0..x1
|
304
|
+
col = get(x, y)
|
305
|
+
r = (col[0] * rev_alpha + red * alpha) / 255
|
306
|
+
r = r <= 255 ? r.to_i : 255
|
307
|
+
g = (col[1] * rev_alpha + green * alpha) / 255
|
308
|
+
g = g <= 255 ? g.to_i : 255
|
309
|
+
b = (col[2] * rev_alpha + blue * alpha) / 255
|
310
|
+
b = b <= 255 ? b.to_i : 255
|
311
|
+
set(x, y, [r, g, b, col[3]])
|
312
|
+
tmp -= dy
|
313
|
+
if tmp < 0 then
|
314
|
+
y += iy
|
315
|
+
tmp += dx
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
else
|
320
|
+
# dx < dy
|
321
|
+
if y0 > y1 then
|
322
|
+
tmp = x0; x0 = x1; x1 = tmp
|
323
|
+
tmp = y0; y0 = y1; y1 = tmp
|
324
|
+
end
|
325
|
+
x = x0; tmp = (dy / 2).to_i; ix = x0 < x1 ? 1 : -1
|
326
|
+
if a == 255 then
|
327
|
+
for y in y0..y1
|
328
|
+
col = get(x, y)
|
329
|
+
set(x, y, [red, green, blue, col[3]])
|
330
|
+
tmp -= dx
|
331
|
+
if tmp < 0 then
|
332
|
+
x += ix
|
333
|
+
tmp += dy
|
334
|
+
end
|
335
|
+
end
|
336
|
+
else
|
337
|
+
alpha = a
|
338
|
+
rev_alpha = 255 - a
|
339
|
+
for y in y0..y1
|
340
|
+
col = get(x, y)
|
341
|
+
r = (col[0] * rev_alpha + red * alpha) / 255
|
342
|
+
r = r <= 255 ? r.to_i : 255
|
343
|
+
g = (col[1] * rev_alpha + green * alpha) / 255
|
344
|
+
g = g <= 255 ? g.to_i : 255
|
345
|
+
b = (col[2] * rev_alpha + blue * alpha) / 255
|
346
|
+
b = b <= 255 ? b.to_i : 255
|
347
|
+
set(x, y, [r, g, b, col[3]])
|
348
|
+
tmp -= dx
|
349
|
+
if tmp < 0 then
|
350
|
+
x += ix
|
351
|
+
tmp += dy
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
def draw_hline(x, y, width, c = @color, a = @alpha)
|
359
|
+
if y < 0 || y >= @height || @width <= 0 || a <= 0 then return end
|
360
|
+
x0 = x
|
361
|
+
x1 = x + width
|
362
|
+
x0 = x0 >= 0 ? x0 : 0
|
363
|
+
x0 = x0 < @width ? x0 : -1
|
364
|
+
x1 = x1 >= 0 ? x1 : -1
|
365
|
+
x1 = x1 < @width ? x1 : @width - 1
|
366
|
+
if x0 < 0 || x1 < 0 then return end
|
367
|
+
i0 = x0 + y * @width
|
368
|
+
i1 = x1 + y * @width
|
369
|
+
red = (c >> 16) & 0xff
|
370
|
+
green = (c >> 8) & 0xff
|
371
|
+
blue = c & 0xff
|
372
|
+
if a == 255 then
|
373
|
+
for i in i0..i1
|
374
|
+
@pixels[i] = [red, green, blue, @pixels[i][3]]
|
375
|
+
end
|
376
|
+
else
|
377
|
+
alpha = a
|
378
|
+
rev_alpha = 255 - a
|
379
|
+
for i in i0..i1
|
380
|
+
c = @pixels[i]
|
381
|
+
r = c[0]
|
382
|
+
g = c[1]
|
383
|
+
b = c[2]
|
384
|
+
a = c[3]
|
385
|
+
r = (rev_alpha * r + alpha * red) / 255
|
386
|
+
r = r <= 255 ? r.to_i : 255
|
387
|
+
g = (rev_alpha * g + alpha * green) / 255
|
388
|
+
g = g <= 255 ? g.to_i : 255
|
389
|
+
b = (rev_alpha * b + alpha * blue) / 255
|
390
|
+
b = b <= 255 ? b.to_i : 255
|
391
|
+
@pixels[i] = [r, g, b, a]
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
def draw_vline(x, y, height, c = @color, a = @alpha)
|
397
|
+
if x < 0 || x >= @width || @height <= 0 || a == 0 then return end
|
398
|
+
y0 = y
|
399
|
+
y1 = y + height
|
400
|
+
y0 = y0 >= 0 ? y0 : 0
|
401
|
+
y0 = y0 < @height ? y0 : -1
|
402
|
+
y1 = y1 >= 0 ? y1 : -1
|
403
|
+
y1 = y1 < @height ? y1 : @height - 1
|
404
|
+
if y0 < 0 || y1 < 0 then return end
|
405
|
+
i = x + y0 * @width
|
406
|
+
red = (c >> 16) & 0xff
|
407
|
+
green = (c >> 8) & 0xff
|
408
|
+
blue = c & 0xff
|
409
|
+
if a == 255 then
|
410
|
+
for y in y0..y1
|
411
|
+
@pixels[i] = [red, green, blue, @pixels[i][3]]
|
412
|
+
i += @width
|
413
|
+
end
|
414
|
+
else
|
415
|
+
alpha = a
|
416
|
+
rev_alpha = 255 - a
|
417
|
+
for y in y0..y1
|
418
|
+
c = @pixels[i]
|
419
|
+
r = c[0]
|
420
|
+
g = c[1]
|
421
|
+
b = c[2]
|
422
|
+
a = c[3]
|
423
|
+
r = (rev_alpha * r + alpha * red) / 255
|
424
|
+
r = r <= 255 ? r.to_i : 255
|
425
|
+
g = (rev_alpha * g + alpha * green) / 255
|
426
|
+
g = g <= 255 ? g.to_i : 255
|
427
|
+
b = (rev_alpha * b + alpha * blue) / 255
|
428
|
+
b = b <= 255 ? b.to_i : 255
|
429
|
+
@pixels[i] = [r, g, b, a]
|
430
|
+
i += @width
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
def draw_rect(x, y, width, height, c = @color, a = @alpha)
|
436
|
+
x0 = x; y0 = y
|
437
|
+
x1 = x + width; y1 = y + height
|
438
|
+
draw_hline(x0, y0, width, c, a)
|
439
|
+
draw_hline(x0, y1, width, c, a)
|
440
|
+
draw_vline(x0, y0 + 1, height - 2, c, a)
|
441
|
+
draw_vline(x1, y0 + 1, height - 2, c, a)
|
442
|
+
end
|
443
|
+
|
444
|
+
def fill_rect(x, y, width, height, c = @color, a = @alpha)
|
445
|
+
x0 = x; x1 = x + width
|
446
|
+
y0 = y; y1 = y + height
|
447
|
+
for y in y0..y1
|
448
|
+
draw_hline(x0, y, width, c, a)
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
def fill_polygon(xa, ya, c = @color, a = @alpha)
|
453
|
+
point_count = xa.length <= ya.length ? xa.length : ya.length
|
454
|
+
if point_count <= 2 then return end
|
455
|
+
if xa[0] != xa[point_count - 1] || ya[0] != ya[point_count - 1] then
|
456
|
+
xa.push(xa[0]); ya.push(ya[0])
|
457
|
+
point_count += 1
|
458
|
+
end
|
459
|
+
line_count = point_count - 1
|
460
|
+
ymin = ya[0]; ymax = ya[0]
|
461
|
+
for i in 0..(point_count - 1)
|
462
|
+
ymin = ymin > ya[i] ? ya[i] : ymin
|
463
|
+
ymax = ymax < ya[i] ? ya[i] : ymax
|
464
|
+
end
|
465
|
+
x0a = Array.new(line_count); y0a = Array.new(line_count)
|
466
|
+
x1a = Array.new(line_count); y1a = Array.new(line_count)
|
467
|
+
for i in 0..(line_count - 1)
|
468
|
+
x0 = xa[i]; y0 = ya[i]
|
469
|
+
x1 = xa[i + 1]; y1 = ya[i + 1]
|
470
|
+
if(y0 > y1) then
|
471
|
+
tmp = x0; x0 = x1; x1 = tmp
|
472
|
+
tmp = y0; y0 = y1; y1 = tmp
|
473
|
+
end
|
474
|
+
x0a[i] = x0; y0a[i] = y0
|
475
|
+
x1a[i] = x1; y1a[i] = y1
|
476
|
+
end
|
477
|
+
xpa = Array.new
|
478
|
+
for y in ymin..ymax
|
479
|
+
xpa.clear
|
480
|
+
for i in 0..(line_count - 1)
|
481
|
+
x0 = x0a[i]; y0 = y0a[i]
|
482
|
+
x1 = x1a[i]; y1 = y1a[i]
|
483
|
+
if y >= y0 && y < y1 && y0 != y1 then
|
484
|
+
x = x0 + (y - y0) * (x1 - x0) / (y1 - y0)
|
485
|
+
xpa.push(x)
|
486
|
+
end
|
487
|
+
end
|
488
|
+
xpa.sort!
|
489
|
+
for i in 0..(xpa.length / 2 - 1)
|
490
|
+
x0 = xpa[i * 2]; x1 = xpa[i * 2 + 1]
|
491
|
+
w = x1 - x0
|
492
|
+
draw_hline(x0, y, w, c, a)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
def draw(shape, c = @color, a = @alpha)
|
498
|
+
y_min = shape.y_min
|
499
|
+
y_max = shape.y_max
|
500
|
+
for y in y_min..y_max
|
501
|
+
xs = shape.xpoints(y)
|
502
|
+
xs.sort!
|
503
|
+
for i in 0..(xs.length / 2 - 1)
|
504
|
+
x0 = xs[i * 2]; x1 = xs[i * 2 + 1]
|
505
|
+
w = x1 - x0
|
506
|
+
draw_hline(x0.to_i, y.to_i, w.to_i, c, a)
|
507
|
+
end
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
def draw_image(image, sx, sy, width, height, untialiasing = false)
|
512
|
+
imageW = image.width
|
513
|
+
imageH = image.height
|
514
|
+
xsa = Array.new(width)
|
515
|
+
xea = Array.new(width)
|
516
|
+
ysa = Array.new(height)
|
517
|
+
yea = Array.new(height)
|
518
|
+
for i in 0..(width - 1)
|
519
|
+
xsa[i] = i * imageW / width
|
520
|
+
end
|
521
|
+
for i in 0..(width - 2)
|
522
|
+
xea[i] = xsa[i + 1] - 1
|
523
|
+
end
|
524
|
+
xea[width - 1] = imageW - 1
|
525
|
+
for i in 0..(height - 1)
|
526
|
+
ysa[i] = i * imageH / height
|
527
|
+
end
|
528
|
+
for i in 0..(height - 2)
|
529
|
+
yea[i] = ysa[i + 1] - 1
|
530
|
+
end
|
531
|
+
yea[height - 1] = imageH - 1
|
532
|
+
for iy in 0..(height - 1)
|
533
|
+
for ix in 0..(width - 1)
|
534
|
+
xs = xsa[ix]
|
535
|
+
xe = xea[ix]
|
536
|
+
ys = ysa[iy]
|
537
|
+
ye = yea[iy]
|
538
|
+
if untialiasing then
|
539
|
+
area = (xe - xs + 1) * (ye - ys + 1)
|
540
|
+
imageColor = [0, 0, 0, 0]
|
541
|
+
for j in ys..ye
|
542
|
+
for i in xs..xe
|
543
|
+
c = image.get(i, j)
|
544
|
+
imageColor[0] += c[0]
|
545
|
+
imageColor[1] += c[1]
|
546
|
+
imageColor[2] += c[2]
|
547
|
+
imageColor[3] += c[3]
|
548
|
+
end
|
549
|
+
end
|
550
|
+
imageColor[0] /= area
|
551
|
+
imageColor[1] /= area
|
552
|
+
imageColor[2] /= area
|
553
|
+
imageColor[3] /= area
|
554
|
+
else
|
555
|
+
imageColor = image.get((xs + xe) / 2, (ys + ye) / 2)
|
556
|
+
end
|
557
|
+
a = imageColor[3]
|
558
|
+
if a == 255 then
|
559
|
+
set(sx + ix, sy + iy, imageColor);
|
560
|
+
elsif a > 0
|
561
|
+
ra = 255 - a
|
562
|
+
color = get(sx + ix, sy + iy)
|
563
|
+
color[0] = (a * imageColor[0] + ra * color[0]) / 255
|
564
|
+
color[1] = (a * imageColor[1] + ra * color[1]) / 255
|
565
|
+
color[2] = (a * imageColor[2] + ra * color[2]) / 255
|
566
|
+
set(sx + ix, sy + iy, color);
|
567
|
+
end
|
568
|
+
end
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
end
|
573
|
+
|
574
|
+
######################################################################
|
575
|
+
# Shape
|
576
|
+
######################################################################
|
577
|
+
|
578
|
+
class Shape
|
579
|
+
def x_min
|
580
|
+
end
|
581
|
+
def x_max
|
582
|
+
end
|
583
|
+
def y_min
|
584
|
+
return 0
|
585
|
+
end
|
586
|
+
def y_max
|
587
|
+
return 0
|
588
|
+
end
|
589
|
+
def xpoints(y)
|
590
|
+
return []
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
######################################################################
|
595
|
+
# Affine
|
596
|
+
######################################################################
|
597
|
+
|
598
|
+
=begin
|
599
|
+
|
600
|
+
xd = a1 * x + b1 * y + d1
|
601
|
+
yd = a2 * x + b2 * y + d2
|
602
|
+
|
603
|
+
y = (-a2 * xd + a1 * yd - a1 * d2 + a2 * d1) / (a1 * b2 - a2 * b1)
|
604
|
+
x = (-b2 * xd + b1 * yd - b1 * d2 + b2 * d1) / (a2 * b1 - a1 * b2)
|
605
|
+
|
606
|
+
(a1, a2): x axis vector.
|
607
|
+
(b1, b2): y axis vector.
|
608
|
+
(d1, d2): move vector.
|
609
|
+
|
610
|
+
=end
|
611
|
+
|
612
|
+
class Affine
|
613
|
+
|
614
|
+
def initialize(a1, a2, b1, b2, d1, d2)
|
615
|
+
@a1 = a1; @a2 = a2
|
616
|
+
@b1 = b1; @b2 = b2
|
617
|
+
@d1 = d1; @d2 = d2
|
618
|
+
@a1d2a2d1 = -a1 * d2 + a2 * d1
|
619
|
+
@b1d2b2d1 = -b1 * d2 + b2 * d1
|
620
|
+
@a1b2a2b1 = a1 * b2 - a2 * b1
|
621
|
+
@a2b1a1b2 = a2 * b1 - a1 * b2
|
622
|
+
end
|
623
|
+
|
624
|
+
def xd(x, y)
|
625
|
+
return @a1 * x + @b1 * y + @d1
|
626
|
+
end
|
627
|
+
|
628
|
+
def yd(x, y)
|
629
|
+
return @a2 * x + @b2 * y + @d2
|
630
|
+
end
|
631
|
+
|
632
|
+
def x(xd, yd)
|
633
|
+
return (-@b2 * xd + @b1 * yd + @b1d2b2d1) / @a2b1a1b2
|
634
|
+
end
|
635
|
+
|
636
|
+
def y(xd, yd)
|
637
|
+
return (-@a2 * xd + @a1 * yd + @a1d2a2d1) / @a1b2a2b1
|
638
|
+
end
|
639
|
+
|
640
|
+
end
|
641
|
+
|
642
|
+
######################################################################
|
643
|
+
# Image filter.
|
644
|
+
######################################################################
|
645
|
+
|
646
|
+
class ImageFilter
|
647
|
+
def effect(src_image, src_x, src_y, width, height, dst_image, dst_x, dst_y)
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
######################################################################
|
652
|
+
# Color.
|
653
|
+
######################################################################
|
654
|
+
|
655
|
+
class Color
|
656
|
+
|
657
|
+
attr_accessor :red, :green, :blue
|
658
|
+
|
659
|
+
def initialize(red, green, blue)
|
660
|
+
@red = red.to_i
|
661
|
+
@green = green.to_i
|
662
|
+
@blue = blue.to_i
|
663
|
+
end
|
664
|
+
|
665
|
+
def to_hsb
|
666
|
+
return Color.rgb_to_hsb(@red, @green, @blue)
|
667
|
+
end
|
668
|
+
|
669
|
+
def Color.hsb_to_rgb(hue, saturation, brightness)
|
670
|
+
r = 0; g = 0; b = 0
|
671
|
+
if saturation == 0.0
|
672
|
+
r = g = b = (brightness * 255.0 + 0.5).to_i
|
673
|
+
else
|
674
|
+
h = (hue - hue.to_i.to_f) * 6.0
|
675
|
+
f = h - h.to_i.to_f
|
676
|
+
p = brightness * (1.0 - saturation)
|
677
|
+
q = brightness * (1.0 - saturation * f)
|
678
|
+
t = brightness * (1.0 - (saturation * (1.0 - f)))
|
679
|
+
case h.to_i
|
680
|
+
when 0
|
681
|
+
r = (brightness * 255.0 + 0.5).to_i
|
682
|
+
g = (t * 255.0 + 0.5).to_i
|
683
|
+
b = (p * 255.0 + 0.5).to_i
|
684
|
+
when 1
|
685
|
+
r = (q * 255.0 + 0.5).to_i
|
686
|
+
g = (brightness * 255.0 + 0.5).to_i
|
687
|
+
b = (p * 255.0 + 0.5).to_i
|
688
|
+
when 2
|
689
|
+
r = (p * 255.0 + 0.5).to_i
|
690
|
+
g = (brightness * 255.0 + 0.5).to_i
|
691
|
+
b = (t * 255.0 + 0.5).to_i
|
692
|
+
when 3
|
693
|
+
r = (p * 255.0 + 0.5).to_i
|
694
|
+
g = (q * 255.0 + 0.5).to_i
|
695
|
+
b = (brightness * 255.0 + 0.5).to_i
|
696
|
+
when 4
|
697
|
+
r = (t * 255.0 + 0.5).to_i
|
698
|
+
g = (p * 255.0 + 0.5).to_i
|
699
|
+
b = (brightness * 255.0 + 0.5).to_i
|
700
|
+
when 5
|
701
|
+
r = (brightness * 255.0 + 0.5).to_i
|
702
|
+
g = (p * 255.0 + 0.5).to_i;
|
703
|
+
b = (q * 255.0 + 0.5).to_i;
|
704
|
+
end
|
705
|
+
end
|
706
|
+
return r, g, b
|
707
|
+
end
|
708
|
+
|
709
|
+
def Color.rgb_to_hsb(r, g, b)
|
710
|
+
|
711
|
+
hue = saturation = brightness = 0.0
|
712
|
+
|
713
|
+
cmax = (r > g) ? r : g
|
714
|
+
cmax = b > cmax ? b : cmax
|
715
|
+
cmin = (r < g) ? r : g
|
716
|
+
cmin = b < cmin ? b : cmin;
|
717
|
+
|
718
|
+
brightness = cmax.to_f / 255.0;
|
719
|
+
if cmax != 0
|
720
|
+
saturation = (cmax - cmin).to_f / cmax.to_f
|
721
|
+
else
|
722
|
+
saturation = 0.0
|
723
|
+
end
|
724
|
+
if saturation == 0.0
|
725
|
+
hue = 0.0
|
726
|
+
else
|
727
|
+
redc = (cmax - r).to_f / (cmax - cmin).to_f
|
728
|
+
greenc = (cmax - g).to_f / (cmax - cmin).to_f
|
729
|
+
bluec = (cmax - b).to_f / (cmax - cmin).to_f
|
730
|
+
if r == cmax
|
731
|
+
hue = bluec - greenc
|
732
|
+
elsif g == cmax
|
733
|
+
hue = 2.0 + redc - bluec
|
734
|
+
else
|
735
|
+
hue = 4.0 + greenc - redc
|
736
|
+
end
|
737
|
+
hue = hue / 6.0
|
738
|
+
if hue < 0.0
|
739
|
+
hue = hue + 1.0
|
740
|
+
end
|
741
|
+
end
|
742
|
+
|
743
|
+
return hue, saturation, brightness
|
744
|
+
|
745
|
+
end
|
746
|
+
|
747
|
+
BLACK = Color.new( 0, 0, 0)
|
748
|
+
BLUE = Color.new( 0, 0, 255)
|
749
|
+
GREEN = Color.new( 0, 255, 0)
|
750
|
+
CYAN = Color.new( 0, 255, 255)
|
751
|
+
RED = Color.new(255, 0, 0)
|
752
|
+
MAGENTA = Color.new(255, 0, 255)
|
753
|
+
YELLOW = Color.new(255, 255, 0)
|
754
|
+
WHITE = Color.new(255, 255, 255)
|
755
|
+
|
756
|
+
end
|
757
|
+
|
758
|
+
######################################################################
|
759
|
+
# PNG utility.
|
760
|
+
######################################################################
|
761
|
+
|
762
|
+
class ImageIO
|
763
|
+
|
764
|
+
def save(image, outp)
|
765
|
+
end
|
766
|
+
|
767
|
+
def load(inp)
|
768
|
+
end
|
769
|
+
|
770
|
+
end
|
771
|
+
|
772
|
+
class PNGIO < ImageIO
|
773
|
+
|
774
|
+
SIGNATURE = "\x89PNG\x0d\x0a\x1a\x0a"
|
775
|
+
|
776
|
+
def save_file(image, file)
|
777
|
+
end
|
778
|
+
|
779
|
+
def save(image, outp)
|
780
|
+
if outp.kind_of?(String)
|
781
|
+
begin
|
782
|
+
outp = File.new(outp, "w")
|
783
|
+
rescue
|
784
|
+
raise "Cannot open PNG stream: " + outp.to_s
|
785
|
+
end
|
786
|
+
end
|
787
|
+
outp.binmode
|
788
|
+
begin
|
789
|
+
# PNG file signature
|
790
|
+
outp.write(SIGNATURE)
|
791
|
+
# IHDR image header
|
792
|
+
# width, height
|
793
|
+
# color depth, color type,
|
794
|
+
# compress method, filter type, interlace type
|
795
|
+
data = ""
|
796
|
+
append_int(data, image.width)
|
797
|
+
append_int(data, image.height)
|
798
|
+
data << 8
|
799
|
+
data << (image.enable_alpha ? 6 : 2)
|
800
|
+
data << 0 << 0 << 0
|
801
|
+
write_chunk(outp, "IHDR", data)
|
802
|
+
# IDAT image data
|
803
|
+
# image data
|
804
|
+
data = to_idat(image)
|
805
|
+
write_chunk(outp, "IDAT", data)
|
806
|
+
# IEND end
|
807
|
+
write_chunk(outp, "IEND", "")
|
808
|
+
rescue
|
809
|
+
raise "Cannot write PNG stream: " + outp.to_s
|
810
|
+
ensure
|
811
|
+
outp.close
|
812
|
+
end
|
813
|
+
end
|
814
|
+
|
815
|
+
def load(inp)
|
816
|
+
if inp.kind_of?(String)
|
817
|
+
begin
|
818
|
+
inp = File.new(inp, "r")
|
819
|
+
rescue
|
820
|
+
raise "Cannot open PNG file: " + inp.to_s
|
821
|
+
end
|
822
|
+
end
|
823
|
+
inp.binmode
|
824
|
+
begin
|
825
|
+
# PNG file signature.
|
826
|
+
signature = inp.read(8)
|
827
|
+
if signature != SIGNATURE then
|
828
|
+
raise "Illegal PNG format: " + inp.to_s
|
829
|
+
end
|
830
|
+
# Read PNG chunks.
|
831
|
+
idat = ""
|
832
|
+
width = -1
|
833
|
+
height = -1
|
834
|
+
while !inp.eof
|
835
|
+
type, data = read_chunk(inp)
|
836
|
+
if type == "IHDR" then
|
837
|
+
# IHDR: Read header.
|
838
|
+
width = to_int(data, 0)
|
839
|
+
height = to_int(data, 4)
|
840
|
+
depth = data[8].to_i
|
841
|
+
color_type = data[9].to_i
|
842
|
+
compress = data[10].to_i
|
843
|
+
filter = data[11].to_i
|
844
|
+
interlace = data[12].to_i
|
845
|
+
# Check support PNG format.
|
846
|
+
if depth != 8 then
|
847
|
+
raise "Not support color depth: " + depth
|
848
|
+
end
|
849
|
+
if color_type != 2 && color_type != 6 then
|
850
|
+
raise "Not support color type: " + color_type
|
851
|
+
end
|
852
|
+
if compress != 0 then
|
853
|
+
raise "Not support compress method: " + compress
|
854
|
+
end
|
855
|
+
if filter != 0 then
|
856
|
+
raise "Not support filter method: " + filter
|
857
|
+
end
|
858
|
+
if interlace != 0 then
|
859
|
+
raise "Not support filter method: " + interlace
|
860
|
+
end
|
861
|
+
elsif type == "IDAT" then
|
862
|
+
# IDAT: Read pixel data.
|
863
|
+
idat << data
|
864
|
+
elsif type == "IEND" then
|
865
|
+
# IEND: PNG data end.
|
866
|
+
break
|
867
|
+
end
|
868
|
+
end
|
869
|
+
idat = Zlib::Inflate.inflate(idat)
|
870
|
+
if idat.length == 0 || width < 0 || height < 0 then
|
871
|
+
raise "Illegal format: " + inp.to_s
|
872
|
+
end
|
873
|
+
# Create image.
|
874
|
+
if color_type == 2 then
|
875
|
+
# RGB, 8bit, disable alpha
|
876
|
+
image = Image.new(width, height, 0xffffff, false)
|
877
|
+
dat_index = 0; pix_index = 0
|
878
|
+
pixels = image.pixels
|
879
|
+
for y in 0..(height - 1)
|
880
|
+
dat_index += 1
|
881
|
+
for x in 0..(width - 1)
|
882
|
+
r = idat[dat_index ].to_i
|
883
|
+
g = idat[dat_index + 1].to_i
|
884
|
+
b = idat[dat_index + 2].to_i
|
885
|
+
dat_index += 3
|
886
|
+
pixels[pix_index] = [r, g, b, 255]
|
887
|
+
pix_index += 1
|
888
|
+
end
|
889
|
+
end
|
890
|
+
elsif color_type == 6 then
|
891
|
+
# RGB, 8bit, enable alpha
|
892
|
+
image = Image.new(width, height, 0xffffff, true)
|
893
|
+
dat_index = 0; pix_index = 0
|
894
|
+
pixels = image.pixels
|
895
|
+
for y in 0..(height - 1)
|
896
|
+
dat_index += 1
|
897
|
+
for x in 0..(width - 1)
|
898
|
+
r = idat[dat_index ].to_i
|
899
|
+
g = idat[dat_index + 1].to_i
|
900
|
+
b = idat[dat_index + 2].to_i
|
901
|
+
a = idat[dat_index + 3].to_i
|
902
|
+
dat_index += 4
|
903
|
+
pixels[pix_index] = [r, g, b, a]
|
904
|
+
pix_index += 1
|
905
|
+
end
|
906
|
+
end
|
907
|
+
end
|
908
|
+
rescue
|
909
|
+
raise "Cannot read PNG stream: " + inp.to_s
|
910
|
+
ensure
|
911
|
+
inp.close
|
912
|
+
end
|
913
|
+
return image
|
914
|
+
end
|
915
|
+
|
916
|
+
def to_idat(image)
|
917
|
+
data = ""
|
918
|
+
ymax = image.height - 1
|
919
|
+
xmax = image.width - 1
|
920
|
+
i = 0
|
921
|
+
pix = image.pixels
|
922
|
+
if !image.enable_alpha then
|
923
|
+
for y in 0..ymax
|
924
|
+
data << "\x00"
|
925
|
+
for x in 0..xmax
|
926
|
+
c = pix[i]
|
927
|
+
data << c[0] << c[1] << c[2]
|
928
|
+
i += 1
|
929
|
+
end
|
930
|
+
end
|
931
|
+
else
|
932
|
+
for y in 0..ymax
|
933
|
+
data << "\x00"
|
934
|
+
for x in 0..xmax
|
935
|
+
c = pix[i]
|
936
|
+
data << c[0] << c[1] << c[2] << c[3]
|
937
|
+
i += 1
|
938
|
+
end
|
939
|
+
end
|
940
|
+
end
|
941
|
+
return Zlib::Deflate.deflate(data)
|
942
|
+
end
|
943
|
+
private :to_idat
|
944
|
+
|
945
|
+
def append_int(str, c)
|
946
|
+
str << ((c >> 24) & 0xff)
|
947
|
+
str << ((c >> 16) & 0xff)
|
948
|
+
str << ((c >> 8) & 0xff)
|
949
|
+
str << (c & 0xff)
|
950
|
+
end
|
951
|
+
private :append_int
|
952
|
+
|
953
|
+
def write_chunk(outp, type, data)
|
954
|
+
# Data length, Cunk type, Chunk data, CRC32
|
955
|
+
write_int(outp, data.length)
|
956
|
+
outp.write(type)
|
957
|
+
outp.write(data)
|
958
|
+
crc32 = Zlib.crc32(type)
|
959
|
+
write_int(outp, Zlib.crc32(data, crc32))
|
960
|
+
end
|
961
|
+
private :write_chunk
|
962
|
+
|
963
|
+
def write_int(outp, value)
|
964
|
+
outp.putc((value >> 24) & 0xff)
|
965
|
+
outp.putc((value >> 16) & 0xff)
|
966
|
+
outp.putc((value >> 8) & 0xff)
|
967
|
+
outp.putc(value & 0xff)
|
968
|
+
end
|
969
|
+
private :write_int
|
970
|
+
|
971
|
+
def to_int(str, offset)
|
972
|
+
value = str[offset].to_i
|
973
|
+
value = (value << 8) | (str[offset + 1].to_i & 0xff)
|
974
|
+
value = (value << 8) | (str[offset + 2].to_i & 0xff)
|
975
|
+
value = (value << 8) | (str[offset + 3].to_i & 0xff)
|
976
|
+
return value
|
977
|
+
end
|
978
|
+
private :to_int
|
979
|
+
|
980
|
+
def read_chunk(inp)
|
981
|
+
# Data length, Cunk type, Chunk data, CRC32
|
982
|
+
length = read_int(inp)
|
983
|
+
type = inp.read(4);
|
984
|
+
data = inp.read(length)
|
985
|
+
crc32 = read_int(inp)
|
986
|
+
crc32_check = Zlib.crc32(type)
|
987
|
+
crc32_check = Zlib.crc32(data, crc32_check)
|
988
|
+
if crc32 != crc32_check then
|
989
|
+
raise "PNG format error(CRC32): type: " + type
|
990
|
+
end
|
991
|
+
return type, data
|
992
|
+
end
|
993
|
+
private :read_chunk
|
994
|
+
|
995
|
+
def read_int(inp)
|
996
|
+
value = inp.getc() & 0xff
|
997
|
+
value = (value << 8) | (inp.getc() & 0xff)
|
998
|
+
value = (value << 8) | (inp.getc() & 0xff)
|
999
|
+
value = (value << 8) | (inp.getc() & 0xff)
|
1000
|
+
return value
|
1001
|
+
end
|
1002
|
+
private :read_int
|
1003
|
+
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
end
|