hotcocoa 0.5

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.
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