rqrcode 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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