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 CHANGED
@@ -1,3 +1,8 @@
1
- *0.1.0* (Feb 23rd, 2008)
1
+ *0.2.0* (Feb 23rd, 2008)
2
+
3
+ * Split files up [DR]
4
+ * added rdoc comment ... more to do there
5
+
6
+ *0.1.0* (Feb 22rd, 2008)
2
7
 
3
8
  * Initial Release [DR]
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
- = Project: rQRCode, Encode QRCodes
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
- controller:
47
+ <b>Controller:</b>
48
48
  @qr = RQRCode::QRCode.new( 'my string to generate', :size => 4, :level => :h )
49
49
 
50
- view: (minimal styling added)
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>
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
 
@@ -1,5 +1,5 @@
1
1
  require 'rqrcode/core_ext/array/behavior'
2
2
 
3
- class Array
3
+ class Array #:nodoc:
4
4
  include CoreExtensions::Array::Behavior
5
5
  end
@@ -1,5 +1,5 @@
1
- module CoreExtensions
2
- module Array
1
+ module CoreExtensions #:nodoc:
2
+ module Array #:nodoc:
3
3
  module Behavior
4
4
  def extract_options!
5
5
  last.is_a?(::Hash) ? pop : {}
@@ -1,5 +1,5 @@
1
1
  require 'rqrcode/core_ext/integer/bitwise'
2
2
 
3
- class Integer
3
+ class Integer #:nodoc:
4
4
  include CoreExtensions::Integer::Bitwise
5
5
  end
@@ -1,5 +1,5 @@
1
- module CoreExtensions
2
- module Integer
1
+ module CoreExtensions #:nodoc:
2
+ module Integer #:nodoc:
3
3
  module Bitwise
4
4
  def rszf(count)
5
5
  # zero fill right shift
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