hotcocoa 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/History.txt +4 -0
  2. data/bin/hotcocoa +31 -0
  3. data/lib/hotcocoa/application_builder.rb +320 -0
  4. data/lib/hotcocoa/attributed_string.rb +143 -0
  5. data/lib/hotcocoa/behaviors.rb +7 -0
  6. data/lib/hotcocoa/data_sources/combo_box_data_source.rb +44 -0
  7. data/lib/hotcocoa/data_sources/table_data_source.rb +18 -0
  8. data/lib/hotcocoa/delegate_builder.rb +85 -0
  9. data/lib/hotcocoa/graphics/canvas.rb +836 -0
  10. data/lib/hotcocoa/graphics/color.rb +781 -0
  11. data/lib/hotcocoa/graphics/elements/particle.rb +75 -0
  12. data/lib/hotcocoa/graphics/elements/rope.rb +99 -0
  13. data/lib/hotcocoa/graphics/elements/sandpainter.rb +71 -0
  14. data/lib/hotcocoa/graphics/gradient.rb +63 -0
  15. data/lib/hotcocoa/graphics/image.rb +488 -0
  16. data/lib/hotcocoa/graphics/path.rb +325 -0
  17. data/lib/hotcocoa/graphics/pdf.rb +71 -0
  18. data/lib/hotcocoa/graphics.rb +161 -0
  19. data/lib/hotcocoa/kernel_ext.rb +14 -0
  20. data/lib/hotcocoa/kvo_accessors.rb +48 -0
  21. data/lib/hotcocoa/layout_view.rb +448 -0
  22. data/lib/hotcocoa/mapper.rb +227 -0
  23. data/lib/hotcocoa/mapping_methods.rb +40 -0
  24. data/lib/hotcocoa/mappings/alert.rb +25 -0
  25. data/lib/hotcocoa/mappings/application.rb +112 -0
  26. data/lib/hotcocoa/mappings/array_controller.rb +87 -0
  27. data/lib/hotcocoa/mappings/box.rb +39 -0
  28. data/lib/hotcocoa/mappings/button.rb +92 -0
  29. data/lib/hotcocoa/mappings/collection_view.rb +44 -0
  30. data/lib/hotcocoa/mappings/color.rb +28 -0
  31. data/lib/hotcocoa/mappings/column.rb +21 -0
  32. data/lib/hotcocoa/mappings/combo_box.rb +24 -0
  33. data/lib/hotcocoa/mappings/control.rb +33 -0
  34. data/lib/hotcocoa/mappings/font.rb +44 -0
  35. data/lib/hotcocoa/mappings/gradient.rb +15 -0
  36. data/lib/hotcocoa/mappings/image.rb +15 -0
  37. data/lib/hotcocoa/mappings/image_view.rb +43 -0
  38. data/lib/hotcocoa/mappings/label.rb +25 -0
  39. data/lib/hotcocoa/mappings/layout_view.rb +9 -0
  40. data/lib/hotcocoa/mappings/menu.rb +71 -0
  41. data/lib/hotcocoa/mappings/menu_item.rb +47 -0
  42. data/lib/hotcocoa/mappings/movie.rb +13 -0
  43. data/lib/hotcocoa/mappings/movie_view.rb +27 -0
  44. data/lib/hotcocoa/mappings/notification.rb +17 -0
  45. data/lib/hotcocoa/mappings/popup.rb +110 -0
  46. data/lib/hotcocoa/mappings/progress_indicator.rb +68 -0
  47. data/lib/hotcocoa/mappings/scroll_view.rb +29 -0
  48. data/lib/hotcocoa/mappings/search_field.rb +9 -0
  49. data/lib/hotcocoa/mappings/secure_text_field.rb +17 -0
  50. data/lib/hotcocoa/mappings/segmented_control.rb +97 -0
  51. data/lib/hotcocoa/mappings/slider.rb +25 -0
  52. data/lib/hotcocoa/mappings/sort_descriptor.rb +13 -0
  53. data/lib/hotcocoa/mappings/sound.rb +9 -0
  54. data/lib/hotcocoa/mappings/speech_synthesizer.rb +25 -0
  55. data/lib/hotcocoa/mappings/split_view.rb +21 -0
  56. data/lib/hotcocoa/mappings/status_bar.rb +7 -0
  57. data/lib/hotcocoa/mappings/status_item.rb +9 -0
  58. data/lib/hotcocoa/mappings/table_view.rb +110 -0
  59. data/lib/hotcocoa/mappings/text_field.rb +41 -0
  60. data/lib/hotcocoa/mappings/text_view.rb +13 -0
  61. data/lib/hotcocoa/mappings/timer.rb +25 -0
  62. data/lib/hotcocoa/mappings/toolbar.rb +97 -0
  63. data/lib/hotcocoa/mappings/toolbar_item.rb +36 -0
  64. data/lib/hotcocoa/mappings/view.rb +67 -0
  65. data/lib/hotcocoa/mappings/web_view.rb +22 -0
  66. data/lib/hotcocoa/mappings/window.rb +118 -0
  67. data/lib/hotcocoa/mappings/xml_parser.rb +41 -0
  68. data/lib/hotcocoa/mappings.rb +109 -0
  69. data/lib/hotcocoa/mvc.rb +175 -0
  70. data/lib/hotcocoa/notification_listener.rb +62 -0
  71. data/lib/hotcocoa/object_ext.rb +22 -0
  72. data/lib/hotcocoa/plist.rb +45 -0
  73. data/lib/hotcocoa/standard_rake_tasks.rb +17 -0
  74. data/lib/hotcocoa/template.rb +27 -0
  75. data/lib/hotcocoa/virtual_file_system.rb +172 -0
  76. data/lib/hotcocoa.rb +26 -0
  77. data/template/Rakefile +5 -0
  78. data/template/config/build.yml +8 -0
  79. data/template/lib/application.rb +45 -0
  80. data/template/lib/menu.rb +32 -0
  81. data/template/resources/HotCocoa.icns +0 -0
  82. data/test/test_helper.rb +3 -0
  83. data/test/test_hotcocoa.rb +11 -0
  84. metadata +137 -0
@@ -0,0 +1,488 @@
1
+ # Ruby Cocoa Graphics is a graphics library providing a simple object-oriented
2
+ # interface into the power of Mac OS X's Core Graphics and Core Image drawing libraries.
3
+ # With a few lines of easy-to-read code, you can write scripts to draw simple or complex
4
+ # shapes, lines, and patterns, process and filter images, create abstract art or visualize
5
+ # scientific data, and much more.
6
+ #
7
+ # Inspiration for this project was derived from Processing and NodeBox. These excellent
8
+ # graphics programming environments are more full-featured than RCG, but they are implemented
9
+ # in Java and Python, respectively. RCG was created to offer similar functionality using
10
+ # the Ruby programming language.
11
+ #
12
+ # Author:: James Reynolds (mailto:drtoast@drtoast.com)
13
+ # Copyright:: Copyright (c) 2008 James Reynolds
14
+ # License:: Distributes under the same terms as Ruby
15
+
16
+ module HotCocoa::Graphics
17
+
18
+ # load a raw image file for use on a canvas
19
+ class Image
20
+
21
+
22
+ BlendModes = {
23
+ :normal => 'CISourceOverCompositing',
24
+ :multiply => 'CIMultiplyBlendMode',
25
+ :screen => 'CIScreenBlendMode',
26
+ :overlay => 'CIOverlayBlendMode',
27
+ :darken => 'CIDarkenBlendMode',
28
+ :lighten => 'CILightenBlendMode',
29
+ :colordodge => 'CIColorDodgeBlendMode',
30
+ :colorburn => 'CIColorBurnBlendMode',
31
+ :softlight => 'CISoftLightBlendMode',
32
+ :hardlight => 'CIHardLightBlendMode',
33
+ :difference => 'CIDifferenceBlendMode',
34
+ :exclusion => 'CIExclusionBlendMode',
35
+ :hue => 'CIHueBlendMode',
36
+ :saturation => 'CISaturationBlendMode',
37
+ :color => 'CIColorBlendMode',
38
+ :luminosity => 'CILuminosityBlendMode',
39
+ # following modes not available in CGContext:
40
+ :maximum => 'CIMaximumCompositing',
41
+ :minimum => 'CIMinimumCompositing',
42
+ :add => 'CIAdditionCompositing',
43
+ :atop => 'CISourceAtopCompositing',
44
+ :in => 'CISourceInCompositing',
45
+ :out => 'CISourceOutCompositing',
46
+ :over => 'CISourceOverCompositing'
47
+ }
48
+ BlendModes.default('CISourceOverCompositing')
49
+
50
+ attr_reader :cgimage
51
+
52
+ # load the image from the given path
53
+ def initialize(img, verbose=false)
54
+ self.verbose(verbose)
55
+ case img
56
+ when String
57
+ # if it's the path to an image file, load as a CGImage
58
+ @path = img
59
+ File.exists?(@path) or raise "ERROR: file not found: #{@path}"
60
+
61
+ nsimage = NSImage.alloc.initWithContentsOfFile(img)
62
+ nsdata = nsimage.TIFFRepresentation
63
+ @nsbitmapimage = NSBitmapImageRep.imageRepWithData(nsdata)
64
+ # cgimagesource = CGImageSourceCreateWithData(nsdata) # argh, doesn't work
65
+ @ciimage = CIImage.alloc.initWithBitmapImageRep(@nsbitmapimage)
66
+ when Canvas
67
+ puts "Image.new with canvas" if @verbose
68
+ @path = 'canvas'
69
+ @cgimage = img.cgimage
70
+ when Image
71
+ # copy image?
72
+ else
73
+ raise "ERROR: image type not recognized: #{img.class}"
74
+ end
75
+ # save the original
76
+ @original = @ciimage.copy
77
+ puts "Image.new from [#{@path}] at [#{x},#{y}] with #{width}x#{height}" if @verbose
78
+ self
79
+ end
80
+
81
+ # reload the bitmap image
82
+ def reset
83
+ @ciimage = CIImage.alloc.initWithBitmapImageRep(@nsbitmapimage)
84
+ self
85
+ end
86
+
87
+ # set registration mode to :center or :corner
88
+ def registration(mode=:center)
89
+ @registration = mode
90
+ end
91
+
92
+ # print the parameters of the path
93
+ def to_s
94
+ "image.to_s: #{@path} at [#{x},#{y}] with #{width}x#{height}"
95
+ end
96
+
97
+ # print drawing functions if verbose is true
98
+ def verbose(tf=true)
99
+ @verbose = tf
100
+ end
101
+
102
+ # return the width of the image
103
+ def width
104
+ @ciimage ? @ciimage.extent.size.width : CGImageGetWidth(@cgimage)
105
+ end
106
+
107
+ # return the height of the image
108
+ def height
109
+ @ciimage ? @ciimage.extent.size.height : CGImageGetHeight(@cgimage)
110
+ end
111
+
112
+ # return the x coordinate of the image's origin
113
+ def x
114
+ @ciimage ? @ciimage.extent.origin.x : 0
115
+ end
116
+
117
+ # return the y coordinate of the image's origin
118
+ def y
119
+ @ciimage ? @ciimage.extent.origin.y : 0
120
+ end
121
+
122
+ # def translate(x,y)
123
+ # matrix = CGAffineTransformMakeTranslation(x,y)
124
+ # @ciimage = @ciimage.imageByApplyingTransform(matrix)
125
+ # @ciimage.extent
126
+ # self
127
+ # end
128
+
129
+ # RESIZING/MOVING
130
+
131
+ # scale the image by multiplying the width by a multiplier, optionally scaling height using aspect ratio
132
+ def scale(multiplier, aspect=1.0)
133
+ puts "image.scale: #{multiplier},#{aspect}" if @verbose
134
+ filter 'CILanczosScaleTransform', :inputScale => multiplier.to_f, :inputAspectRatio => aspect.to_f
135
+ self
136
+ end
137
+
138
+ # scale image to fit within a box of w,h using CIAffineTransform (sharper)
139
+ def fit2(w, h)
140
+ width_multiplier = w.to_f / width
141
+ height_multiplier = h.to_f / height
142
+ multiplier = width_multiplier < height_multiplier ? width_multiplier : height_multiplier
143
+ puts "image.fit2: #{multiplier}" if @verbose
144
+ transform = NSAffineTransform.transform
145
+ transform.scaleBy(multiplier)
146
+ filter 'CIAffineTransform', :inputTransform => transform
147
+ self
148
+ end
149
+
150
+ # scale image to fit within a box of w,h using CILanczosScaleTransform
151
+ def fit(w, h)
152
+ # http://gigliwood.com/weblog/Cocoa/Core_Image,_part_2.html
153
+ old_w = self.width.to_f
154
+ old_h = self.height.to_f
155
+ old_x = self.x
156
+ old_y = self.y
157
+
158
+ # choose a scaling factor
159
+ width_multiplier = w.to_f / old_w
160
+ height_multiplier = h.to_f / old_h
161
+ multiplier = width_multiplier < height_multiplier ? width_multiplier : height_multiplier
162
+
163
+ # crop result to integer pixel dimensions
164
+ new_width = (self.width * multiplier).truncate
165
+ new_height = (self.height * multiplier).truncate
166
+
167
+ puts "image.fit: old size #{old_w}x#{old_h}, max target #{w}x#{h}, multiplier #{multiplier}, new size #{new_width}x#{new_height}" if @verbose
168
+ clamp
169
+ scale(multiplier)
170
+ crop(old_x, old_y, new_width, new_height)
171
+ #origin(:bottomleft)
172
+ self
173
+ end
174
+
175
+ # resize the image to have new dimensions w,h
176
+ def resize(w, h)
177
+ oldw = width
178
+ oldh = height
179
+ puts "image.resize #{oldw}x#{oldh} => #{w}x#{h}" if @verbose
180
+ width_ratio = w.to_f / oldw.to_f
181
+ height_ratio = h.to_f / oldh.to_f
182
+ aspect = width_ratio / height_ratio # (works when stretching tall, gives aspect = 0.65)
183
+ scale(height_ratio,aspect)
184
+ origin(:bottomleft)
185
+ self
186
+ end
187
+
188
+ # crop the image to a rectangle from x1,y2 with width x height
189
+ def crop(x=nil,y=nil,w=nil,h=nil)
190
+
191
+ # crop to largest square if no parameters were given
192
+ unless x
193
+ if (self.width > self.height)
194
+ side = self.height
195
+ x = (self.width - side) / 2
196
+ y = 0
197
+ else
198
+ side = self.width
199
+ y = (self.height - side) / 2
200
+ x = 0
201
+ end
202
+ w = h = side
203
+ end
204
+
205
+ puts "image.crop [#{x},#{y}] with #{w},#{h}" if @verbose
206
+ #vector = CIVector.vectorWithX_Y_Z_W(x.to_f,y.to_f,w.to_f,h.to_f)
207
+ vector = CIVector.vectorWithX x.to_f, Y:y.to_f, Z:w.to_f, W:h.to_f
208
+ filter('CICrop', :inputRectangle => vector)
209
+ origin(:bottomleft)
210
+ self
211
+ end
212
+
213
+ # apply an affine transformation using matrix parameters a,b,c,d,tx,ty
214
+ def transform(a, b, c, d, tx, ty)
215
+ puts "image.transform #{a},#{b},#{c},#{d},#{tx},#{ty}" if @verbose
216
+ transform = CGAffineTransformMake(a, b, c, d, tx, ty) # FIXME: needs to be NSAffineTransform?
217
+ filter 'CIAffineTransform', :inputTransform => transform
218
+ self
219
+ end
220
+
221
+ # translate image by tx,ty
222
+ def translate(tx, ty)
223
+ puts "image.translate #{tx},#{ty}" if @verbose
224
+ #transform = CGAffineTransformMakeTranslation(tx,ty);
225
+ transform = NSAffineTransform.transform
226
+ transform.translateXBy tx, yBy:ty
227
+ filter 'CIAffineTransform', :inputTransform => transform
228
+ self
229
+ end
230
+
231
+ # rotate image by degrees
232
+ def rotate(deg)
233
+ puts "image.rotate #{deg}" if @verbose
234
+ #transform = CGAffineTransformMakeRotation(radians(deg));
235
+ transform = NSAffineTransform.transform
236
+ transform.rotateByDegrees(-deg)
237
+ filter 'CIAffineTransform', :inputTransform => transform
238
+ self
239
+ end
240
+
241
+ # set the origin to the specified location (:center, :bottomleft, etc)
242
+ def origin(location=:bottomleft)
243
+ movex, movey = reorient(x, y, width, height, location)
244
+ translate(movex, movey)
245
+ end
246
+
247
+
248
+ # FILTERS
249
+
250
+ # apply a crystallizing effect with pixel radius 1-100
251
+ def crystallize(radius=20.0)
252
+ filter 'CICrystallize', :inputRadius => radius
253
+ self
254
+ end
255
+
256
+ # apply a gaussian blur with pixel radius 1-100
257
+ def blur(radius=10.0)
258
+ filter 'CIGaussianBlur', :inputRadius => inrange(radius, 1.0, 100.0)
259
+ self
260
+ end
261
+
262
+ # sharpen the image given a radius (0-100) and intensity factor
263
+ def sharpen(radius=2.50, intensity=0.50)
264
+ filter 'CIUnsharpMask', :inputRadius => radius, :inputIntensity => intensity
265
+ self
266
+ end
267
+
268
+ # apply a gaussian blur with pixel radius 1-100
269
+ def motionblur(radius=10.0, angle=90.0)
270
+ oldx, oldy, oldw, oldh = [x, y, width, height]
271
+ clamp
272
+ filter 'CIMotionBlur', :inputRadius => radius, :inputAngle => radians(angle)
273
+ crop(oldx, oldy, oldw, oldh)
274
+ self
275
+ end
276
+
277
+ # rotate pixels around x,y with radius and angle
278
+ def twirl(x=0, y=0, radius=300, angle=90.0)
279
+ filter 'CITwirlDistortion', :inputCenter => CIVector.vectorWithX(x, Y:y), :inputRadius => radius, :inputAngle => radians(angle)
280
+ self
281
+ end
282
+
283
+ # apply a bloom effect
284
+ def bloom(radius=10, intensity=1.0)
285
+ filter 'CIBloom', :inputRadius => inrange(radius, 0, 100), :inputIntensity => inrange(intensity, 0.0, 1.0)
286
+ self
287
+ end
288
+
289
+ # adjust the hue of the image by rotating the color wheel from 0 to 360 degrees
290
+ def hue(angle=180)
291
+ filter 'CIHueAdjust', :inputAngle => radians(angle)
292
+ self
293
+ end
294
+
295
+ # remap colors so they fall within shades of a single color
296
+ def monochrome(color=Color.gray)
297
+ filter 'CIColorMonochrome', :inputColor => CIColor.colorWithRed(color.r, green:color.g, blue:color.b, alpha:color.a)
298
+ self
299
+ end
300
+
301
+ # adjust the reference white point for an image and maps all colors in the source using the new reference
302
+ def whitepoint(color=Color.white.ish)
303
+ filter 'CIWhitePointAdjust', :inputColor => CIColor.colorWithRed(color.r, green:color.g, blue:color.b, alpha:color.a)
304
+ self
305
+ end
306
+
307
+ # reduce colors with a banding effect
308
+ def posterize(levels=6.0)
309
+ filter 'CIColorPosterize', :inputLevels => inrange(levels, 1.0, 300.0)
310
+ self
311
+ end
312
+
313
+ # detect edges
314
+ def edges(intensity=1.0)
315
+ filter 'CIEdges', :inputIntensity => inrange(intensity, 0.0,10.0)
316
+ self
317
+ end
318
+
319
+ # apply woodblock-like effect
320
+ def edgework(radius=1.0)
321
+ filter 'CIEdgeWork', :inputRadius => inrange(radius, 0.0,20.0)
322
+ self
323
+ end
324
+
325
+ # adjust exposure by f-stop
326
+ def exposure(ev=0.5)
327
+ filter 'CIExposureAdjust', :inputEV => inrange(ev, -10.0, 10.0)
328
+ self
329
+ end
330
+
331
+ # adjust saturation
332
+ def saturation(value=1.5)
333
+ filter 'CIColorControls', :inputSaturation => value, :inputBrightness => 0.0, :inputContrast => 1.0
334
+ self
335
+ end
336
+
337
+ # adjust brightness (-1 to 1)
338
+ def brightness(value=1.1)
339
+ filter 'CIColorControls', :inputSaturation => 1.0, :inputBrightness => inrange(value, -1.0, 1.0), :inputContrast => 1.0
340
+ self
341
+ end
342
+
343
+ # adjust contrast (0 to 4)
344
+ def contrast(value=1.5)
345
+ #value = inrange(value,0.25,100.0)
346
+ filter 'CIColorControls', :inputSaturation => 1.0, :inputBrightness => 0.0, :inputContrast => value
347
+ self
348
+ end
349
+
350
+ # fill with a gradient from color0 to color1 from [x0,y0] to [x1,y1]
351
+ def gradient(color0, color1, x0 = x / 2, y0 = y, x1 = x / 2, y1 = height)
352
+ filter 'CILinearGradient',
353
+ :inputColor0 => color0.rgb,
354
+ :inputColor1 => color1.rgb,
355
+ :inputPoint0 => CIVector.vectorWithX(x0, Y:y0),
356
+ :inputPoint1 => CIVector.vectorWithX(x1, Y:y1)
357
+ self
358
+ end
359
+
360
+ # use the gray values of the input image as a displacement map (doesn't work with PNG?)
361
+ def displacement(image, scale=50.0)
362
+ filter 'CIDisplacementDistortion', :inputDisplacementImage => image.ciimage, :inputScale => inrange(scale, 0.0, 200.0)
363
+ self
364
+ end
365
+
366
+ # simulate a halftone screen given a center point, angle(0-360), width(1-50), and sharpness(0-1)
367
+ def dotscreen(dx=0, dy=0, angle=0, width=6, sharpness=0.7)
368
+ filter 'CIDotScreen',
369
+ :inputCenter => CIVector.vectorWithX(dx.to_f, Y:dy.to_f),
370
+ :inputAngle => max(0, min(angle, 360)),
371
+ :inputWidth => max(1, min(width, 50)),
372
+ :inputSharpness => max(0, min(sharpness, 1))
373
+ self
374
+ end
375
+
376
+ # extend pixels at edges to infinity for nicer sharpen/blur effects
377
+ def clamp
378
+ puts "image.clamp" if @verbose
379
+ filter 'CIAffineClamp', :inputTransform => NSAffineTransform.transform
380
+ self
381
+ end
382
+
383
+ # blend with the given image using mode (:add, etc)
384
+ def blend(image, mode)
385
+ case image
386
+ when String
387
+ ciimage_background = CIImage.imageWithContentsOfURL(NSURL.fileURLWithPath(image))
388
+ when Image
389
+ ciimage_background = image.ciimage
390
+ else
391
+ raise "ERROR: Image: type not recognized"
392
+ end
393
+ filter BlendModes[mode], :inputBackgroundImage => ciimage_background
394
+ self
395
+ end
396
+
397
+ # draw this image to the specified context
398
+ def draw(ctx,x=0,y=0,w=width,h=height)
399
+ ciimage
400
+ # imgx = x + self.x
401
+ # imyy = y + self.y
402
+ resize(w, h) if w != self.width || h != self.height
403
+
404
+ # add the ciimage's own origin coordinates to the target point
405
+ x = x + self.x
406
+ y = y + self.y
407
+
408
+ puts "image.draw #{x},#{y} #{w}x#{h}" if @verbose
409
+ cicontext = CIContext.contextWithCGContext ctx, options:nil
410
+ #cicontext.drawImage_atPoint_fromRect(ciimage, [x,y], CGRectMake(self.x,self.y,w,h))
411
+ cicontext.drawImage ciimage, atPoint:CGPointMake(x,y), fromRect:CGRectMake(self.x,self.y,w,h)
412
+ end
413
+
414
+ # return the CIImage for this Image object
415
+ def ciimage
416
+ @ciimage ||= CIImage.imageWithCGImage(@cgimage)
417
+ end
418
+
419
+ # return an array of n colors chosen randomly from the source image.
420
+ # if type = :grid, choose average colors from each square in a grid with n squares
421
+ def colors(n=32, type=:random)
422
+ ciimage
423
+ colors = []
424
+ if (type == :grid) then
425
+ filtername = 'CIAreaAverage'
426
+ f = CIFilter.filterWithName(filtername)
427
+ f.setDefaults
428
+ f.setValue_forKey(@ciimage, 'inputImage')
429
+
430
+ extents = []
431
+
432
+ rows = Math::sqrt(n).truncate
433
+ cols = rows
434
+ w = self.width
435
+ h = self.height
436
+ block_width = w / cols
437
+ block_height = h / rows
438
+ rows.times do |row|
439
+ cols.times do |col|
440
+ x = col * block_width
441
+ y = row * block_height
442
+ extents.push([x, y, block_width, block_height])
443
+ end
444
+ end
445
+ extents.each do |extent|
446
+ x, y, w, h = extent
447
+ extent = CIVector.vectorWithX x.to_f, Y:y.to_f, Z:w.to_f, W:h.to_f
448
+ f.setValue_forKey(extent, 'inputExtent')
449
+ ciimage = f.valueForKey('outputImage') # CIImageRef
450
+ nsimg = NSBitmapImageRep.alloc.initWithCIImage(ciimage)
451
+ nscolor = nsimg.colorAtX 0, y:0 # NSColor
452
+ #r,g,b,a = nscolor.getRed_green_blue_alpha_()
453
+ r,b,b,a = [nscolor.redComponent,nscolor.greenComponent,nscolor.blueComponent,nscolor.alphaComponent]
454
+ colors.push(Color.new(r,g,b,1.0))
455
+ end
456
+ elsif (type == :random)
457
+ nsimg = NSBitmapImageRep.alloc.initWithCIImage(@ciimage)
458
+ n.times do |i|
459
+ x = rand(self.width)
460
+ y = rand(self.height)
461
+ nscolor = nsimg.colorAtX x, y:y # NSColor
462
+ #r,g,b,a = nscolor.getRed_green_blue_alpha_()
463
+ r,g,b,a = [nscolor.redComponent,nscolor.greenComponent,nscolor.blueComponent,nscolor.alphaComponent]
464
+ colors.push(Color.new(r,g,b,1.0))
465
+ end
466
+ end
467
+ colors
468
+ end
469
+
470
+ private
471
+
472
+ # apply the named CoreImage filter using a hash of parameters
473
+ def filter(filtername, parameters)
474
+ ciimage
475
+ f = CIFilter.filterWithName(filtername)
476
+ f.setDefaults
477
+ f.setValue @ciimage, forKey:'inputImage'
478
+ parameters.each do |key,value|
479
+ f.setValue value, forKey:key
480
+ end
481
+ puts "image.filter #{filtername}" if @verbose
482
+ @ciimage = f.valueForKey('outputImage') # CIImageRef
483
+ self
484
+ end
485
+
486
+ end
487
+
488
+ end