Class: HotCocoa::Graphics::Canvas

Inherits:
Object
  • Object
show all
Defined in:
lib/hotcocoa/graphics/canvas.rb

Overview

drawing destination for writing a PDF, PNG, GIF, JPG, or TIF file

Constant Summary collapse

BlendModes =
{
  :normal => KCGBlendModeNormal,
  :darken => KCGBlendModeDarken,
  :multiply => KCGBlendModeMultiply,
  :screen => KCGBlendModeScreen,
  :overlay => KCGBlendModeOverlay,
  :darken => KCGBlendModeDarken,
  :lighten => KCGBlendModeLighten,
  :colordodge => KCGBlendModeColorDodge,
  :colorburn => KCGBlendModeColorBurn,
  :softlight => KCGBlendModeSoftLight,
  :hardlight => KCGBlendModeHardLight,
  :difference => KCGBlendModeDifference,
  :exclusion => KCGBlendModeExclusion,
  :hue => KCGBlendModeHue,
  :saturation => KCGBlendModeSaturation,
  :color => KCGBlendModeColor,
  :luminosity => KCGBlendModeLuminosity,
}
DefaultOptions =
{:quality => 0.8, :width => 400, :height => 400}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}, &block) ⇒ Canvas

create a new canvas with the given width, height, and output filename (pdf, png, jpg, gif, or tif)



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/hotcocoa/graphics/canvas.rb', line 85

def initialize(options={}, &block)
  if options[:size]
    options[:width] = options[:size][0]
    options[:height] = options[:size][1]
  end
  options = DefaultOptions.merge(options)

  @width = options[:width]
  @height = options[:height]
  @output = options[:filename] || 'test'
  @stacksize = 0
  @colorspace = CGColorSpaceCreateDeviceRGB() # => CGColorSpaceRef
  @autoclosepath = false

  case options[:type]
  when :pdf
    @filetype = :pdf
    # CREATE A PDF DRAWING CONTEXT
    # url = NSURL.fileURLWithPath(image)
    url = CFURLCreateFromFileSystemRepresentation(nil, @output, @output.length, false)
    pdfrect = CGRect.new(CGPoint.new(0, 0), CGSize.new(width, height)) # Landscape
    #@ctx = CGPDFContextCreateWithURL(url, pdfrect, nil)
    consumer = CGDataConsumerCreateWithURL(url);
    pdfcontext = CGPDFContextCreate(consumer, pdfrect, nil);
    CGPDFContextBeginPage(pdfcontext, nil)
    @ctx = pdfcontext
  when :image, :render
    # CREATE A BITMAP DRAWING CONTEXT
    @filetype = File.extname(@output).downcase[1..-1].intern if options[:type] == :image

    @bits_per_component = 8
    @colorspace = CGColorSpaceCreateDeviceRGB() # => CGColorSpaceRef
    #alpha = KCGImageAlphaNoneSkipFirst # opaque background
    alpha = KCGImageAlphaPremultipliedFirst # transparent background

    # 8 integer bits/component; 32 bits/pixel; 3-component colorspace; kCGImageAlphaPremultipliedFirst; 57141 bytes/row.
    bytes = @bits_per_component * 4 * @width.ceil
    @ctx = CGBitmapContextCreate(nil, @width, @height, @bits_per_component, bytes, @colorspace, alpha) # =>  CGContextRef
  when :context
    @ctx = options[:context]
  else
    raise "ERROR: output file type #{ext} not recognized"
  end

  # antialiasing
  CGContextSetAllowsAntialiasing(@ctx, true)

  # set defaults
  fill          # set the default fill
  nostroke      # no stroke by default
  strokewidth   # set the default stroke width
  font          # set the default font
  antialias     # set the default antialias state
  autoclosepath # set the autoclosepath default
  quality(options[:quality])   # set the compression default
  push  # save the pristine default default graphics state (retrieved by calling "reset")
  push  # create a new graphics state for the user to mess up
  if block_given?
    case block.arity
    when 0
      send(:instance_eval, &block)
    else
      block.call(self)
    end
  end
end

Instance Attribute Details

#ctxObject (readonly)

We make the context available so developers can directly use underlying CG methods on objects created by this wrapper



53
54
55
# File 'lib/hotcocoa/graphics/canvas.rb', line 53

def ctx
  @ctx
end

#heightObject

Returns the value of attribute height.



49
50
51
# File 'lib/hotcocoa/graphics/canvas.rb', line 49

def height
  @height
end

#widthObject

Returns the value of attribute width.



49
50
51
# File 'lib/hotcocoa/graphics/canvas.rb', line 49

def width
  @width
end

Class Method Details

.for_context(options = {}, &block) ⇒ Object



71
72
73
74
# File 'lib/hotcocoa/graphics/canvas.rb', line 71

def for_context(options={}, &block)
  options[:type] = :context
  Canvas.new(options, &block)
end

.for_current_context(options = {}, &block) ⇒ Object



76
77
78
79
80
# File 'lib/hotcocoa/graphics/canvas.rb', line 76

def for_current_context(options={}, &block)
  options[:type] = :context
  options[:context] = NSGraphicsContext.currentContext.graphicsPort
  Canvas.new(options, &block)
end

.for_image(options = {}, &block) ⇒ Object



66
67
68
69
# File 'lib/hotcocoa/graphics/canvas.rb', line 66

def for_image(options={}, &block)
  options[:type] = :image
  Canvas.new(options, &block)
end

.for_pdf(options = {}, &block) ⇒ Object



61
62
63
64
# File 'lib/hotcocoa/graphics/canvas.rb', line 61

def for_pdf(options={}, &block)
  options[:type] = :pdf
  Canvas.new(options, &block)
end

.for_rendering(options = {}, &block) ⇒ Object



56
57
58
59
# File 'lib/hotcocoa/graphics/canvas.rb', line 56

def for_rendering(options={}, &block)
  options[:type] = :render
  Canvas.new(options, &block)
end

Instance Method Details

#alpha(val = 1.0) ⇒ Object

set the alpha value for subsequently drawn objects



165
166
167
# File 'lib/hotcocoa/graphics/canvas.rb', line 165

def alpha(val=1.0)
  CGContextSetAlpha(@ctx, val)
end

#antialias(tf = true) ⇒ Object

set whether or not drawn paths should be antialiased (true/false)



160
161
162
# File 'lib/hotcocoa/graphics/canvas.rb', line 160

def antialias(tf=true)
  CGContextSetShouldAntialias(@ctx, tf)
end

#arc(x, y, radius, start_angle, end_angle) ⇒ Object

draw the arc of a circle with center point x,y, radius, start angle (0 deg = 12 o'clock) and end angle



364
365
366
367
368
369
370
# File 'lib/hotcocoa/graphics/canvas.rb', line 364

def arc(x, y, radius, start_angle, end_angle)
  start_angle = radians(90-start_angle)
  end_angle = radians(90-end_angle)
  clockwise = 1 # 1 = clockwise, 0 = counterclockwise
  CGContextAddArc(@ctx, x, y, radius, start_angle, end_angle, clockwise)
  CGContextDrawPath(@ctx, KCGPathStroke)
end

#arcto(x1, y1, x2, y2, radius) ⇒ Object

draw an arc given the endpoints of two tangent lines and a radius



446
447
448
# File 'lib/hotcocoa/graphics/canvas.rb', line 446

def arcto(x1, y1, x2, y2, radius)
  CGContextAddArcToPoint(@ctx, x1, y1, x2, y2, radius)
end

#autoclosepath(tf = false) ⇒ Object

if true, automatically close the path after it is ended



401
402
403
# File 'lib/hotcocoa/graphics/canvas.rb', line 401

def autoclosepath(tf=false)
  @autoclosepath = tf
end

#background(r = 1, g = 1, b = 1, a = 1.0) ⇒ Object

draw a background color (given a Color object, or RGBA values)



286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/hotcocoa/graphics/canvas.rb', line 286

def background(r=1, g=1, b=1, a=1.0)
  case r
  when Color
    g = r.g
    b = r.b
    a = r.a
    r = r.r
  end
  push
  CGContextSetRGBFillColor(@ctx, r, g, b, a) # RGBA
  rect(0,0,@width,@height)
  pop
end

#beginclip(p, &block) ⇒ Object

clip subsequent drawing operations within the given path



554
555
556
557
558
559
560
561
562
# File 'lib/hotcocoa/graphics/canvas.rb', line 554

def beginclip(p, &block)
  push
  CGContextAddPath(@ctx, p.path)
  CGContextClip(@ctx)
  if block
    block.call
    endclip
  end
end

#beginpath(x, y) ⇒ Object

begin drawing a path at x,y



412
413
414
415
# File 'lib/hotcocoa/graphics/canvas.rb', line 412

def beginpath(x, y)
  CGContextBeginPath(@ctx)
  CGContextMoveToPoint(@ctx, x, y)
end

#blend(mode) ⇒ Object

set the canvas blend mode (:normal, :darken, :multiply, :screen, etc)



546
547
548
# File 'lib/hotcocoa/graphics/canvas.rb', line 546

def blend(mode)
  CGContextSetBlendMode(@ctx, BlendModes[mode])
end

#cartesian(res = 50, stroke = 1.0, fsize = 10) ⇒ Object

draw a cartesian coordinate grid for reference



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/hotcocoa/graphics/canvas.rb', line 316

def cartesian(res=50, stroke=1.0, fsize=10)
  # save previous state
  new_state do
    # set font and stroke
    fontsize(fsize)
    fill(Color.black)
    stroke(Color.red)
    strokewidth(stroke)
    # draw vertical numbered grid lines
    for x in (-width / res)..(width / res) do
      line(x * res, -height, x * res, height)
      text("#{x * res}", x * res, 0)
    end
    # draw horizontal numbered grid lines
    for y in (-height / res)..(height / res) do
      line(-width, y * res, width, y * res)
      text("#{y * res}", 0, y * res)
    end
    # draw lines intersecting center of canvas
    stroke(Color.black)
    line(-width, -height, width, height)
    line(width, -height, -width, height)
    line(0, height, width, 0)
    line(width / 2, 0, width / 2, height)
    line(0, height / 2, width, height / 2)
    # restore previous state
  end
end

#cgimageObject

return a CGImage of the canvas for reprocessing (only works if using a bitmap context)



630
631
632
633
# File 'lib/hotcocoa/graphics/canvas.rb', line 630

def cgimage
  CGBitmapContextCreateImage(@ctx)  # => CGImageRef (works with bitmap context only)
  #cgimageref = CGImageCreate(@width, @height, @bits_per_component, nil,nil,@colorspace, nil, @provider,nil,true,KCGRenderingIntentDefault)
end

#ciimageObject

return a CIImage of the canvas for reprocessing (only works if using a bitmap context)



636
637
638
639
# File 'lib/hotcocoa/graphics/canvas.rb', line 636

def ciimage
  cgimageref = self.cgimage
  CIImage.imageWithCGImage(cgimageref) # CIConcreteImage (CIImage)
end

#curve(cp1x, cp1y, cp2x, cp2y, x1, y1, x2, y2) ⇒ Object

draw a bezier curve from the current point, given the coordinates of two handle control points and an end point



373
374
375
376
377
# File 'lib/hotcocoa/graphics/canvas.rb', line 373

def curve(cp1x, cp1y, cp2x, cp2y, x1, y1, x2, y2)
  beginpath(x1, y1)
  CGContextAddCurveToPoint(@ctx, cp1x, cp1y, cp2x, cp2y, x2, y2)
  endpath
end

#curveto(cp1x, cp1y, cp2x, cp2y, x, y) ⇒ Object

draw a bezier curve from the current point, given the coordinates of two handle control points and an end point



436
437
438
# File 'lib/hotcocoa/graphics/canvas.rb', line 436

def curveto(cp1x, cp1y, cp2x, cp2y, x, y)
  CGContextAddCurveToPoint(@ctx, cp1x, cp1y, cp2x, cp2y, x, y)
end

#draw(object, *args) ⇒ Object

draw the given Path object



387
388
389
390
391
392
393
394
395
396
# File 'lib/hotcocoa/graphics/canvas.rb', line 387

def draw(object, *args)
  case object
  when Path
    draw_path(object, *args)
  when Image
    draw_image(object, *args)
  else
    raise ArgumentError.new("first parameter must be a Path or Image object not a #{object.class}")
  end
end

#endclipObject

stop clipping drawing operations



565
566
567
# File 'lib/hotcocoa/graphics/canvas.rb', line 565

def endclip
  pop
end

#endpathObject

end the current path and draw it



418
419
420
421
422
423
# File 'lib/hotcocoa/graphics/canvas.rb', line 418

def endpath
  CGContextClosePath(@ctx) if @autoclosepath
  #mode = KCGPathStroke
  mode = KCGPathFillStroke
  CGContextDrawPath(@ctx, mode) # apply fill and stroke
end

#fill(r = 0, g = 0, b = 0, a = 1) ⇒ Object

set the current fill (given a Color object, or RGBA values)



175
176
177
178
179
180
181
182
183
184
185
# File 'lib/hotcocoa/graphics/canvas.rb', line 175

def fill(r=0, g=0, b=0, a=1)
  case r
  when Color
    g = r.g
    b = r.b
    a = r.a
    r = r.r
  end
  CGContextSetRGBFillColor(@ctx, r, g, b, a) # RGBA
  @fill = true
end

#font(name = "Helvetica", size = nil) ⇒ Object

set font by name and optional size



605
606
607
608
609
610
# File 'lib/hotcocoa/graphics/canvas.rb', line 605

def font(name="Helvetica", size=nil)
  fontsize(size) if size
  @fname = name
  fontsize unless @fsize
  CGContextSelectFont(@ctx, @fname, @fsize, KCGEncodingMacRoman)
end

#fontsize(points = 20) ⇒ Object

set font size in points



613
614
615
616
617
618
# File 'lib/hotcocoa/graphics/canvas.rb', line 613

def fontsize(points=20)
  @fsize = points
  font unless @fname
  #CGContextSetFontSize(@ctx,points)
  CGContextSelectFont(@ctx, @fname, @fsize, KCGEncodingMacRoman)
end

#glow(dx = 0.0, dy = 0.0, a = 2.0/3.0, blur = 5) ⇒ Object

apply a glow with offset dx,dy, alpha, and blur



535
536
537
538
# File 'lib/hotcocoa/graphics/canvas.rb', line 535

def glow(dx=0.0, dy=0.0, a=2.0/3.0, blur=5)
  color = CGColorCreate(@colorspace, [1.0, 1.0, 0.0, a])
  CGContextSetShadowWithColor(@ctx, [dx, dy], blur, color)
end

#gradient(gradient = Gradient.new, start_x = @width/2, start_y = 0, end_x = @width/2, end_y = @height) ⇒ Object

draw an axial(linear) gradient starting at sx,sy and ending at ex,ey



309
310
311
312
313
# File 'lib/hotcocoa/graphics/canvas.rb', line 309

def gradient(gradient=Gradient.new, start_x=@width/2, start_y=0, end_x=@width/2, end_y=@height)
  #options = KCGGradientDrawsBeforeStartLocation
  #options = KCGGradientDrawsAfterEndLocation
  CGContextDrawLinearGradient(@ctx, gradient.gradient, NSMakePoint(start_x, start_y), NSMakePoint(end_x, end_y), gradient.pre + gradient.post)
end

#grid(path, rows = 10, cols = 10) ⇒ Object

draw the path in a grid with rows, columns



451
452
453
454
455
456
457
458
459
460
461
462
463
464
# File 'lib/hotcocoa/graphics/canvas.rb', line 451

def grid(path, rows=10, cols=10)
  push
  rows.times do |row|
    tx = (row+1) * (self.height / rows) - (self.height / rows) / 2
    cols.times do |col|
      ty = (col+1) * (self.width / cols) - (self.width / cols) / 2
      push
      translate(tx, ty)
      draw(path)
      pop
    end
  end
  pop
end

#line(x1, y1, x2, y2) ⇒ Object

draw a line starting at x1,y1 and ending at x2,y2



349
350
351
352
353
354
# File 'lib/hotcocoa/graphics/canvas.rb', line 349

def line(x1, y1, x2, y2)
  CGContextAddLines(@ctx, [NSPoint.new(x1, y1), NSPoint.new(x2, y2)], 2)
  CGContextDrawPath(@ctx, KCGPathStroke) # apply stroke
  endpath

end

#linecap(style = :butt) ⇒ Object

set cap style to round, square, or butt



220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/hotcocoa/graphics/canvas.rb', line 220

def linecap(style=:butt)
  case style
  when :round
    cap = KCGLineCapRound
  when :square
    cap = KCGLineCapSquare
  when :butt
    cap = KCGLineCapButt
  else
    raise "ERROR: line cap style not recognized: #{style}"
  end
  CGContextSetLineCap(@ctx,cap)
end

#linedash(lengths = [10,2], phase = 0.0) ⇒ Object

set lengths of dashes and spaces, and distance before starting dashes



250
251
252
253
# File 'lib/hotcocoa/graphics/canvas.rb', line 250

def linedash(lengths=[10,2], phase=0.0)
  count=lengths.size
  CGContextSetLineDash(@ctx, phase, lengths, count)
end

#linejoin(style = :miter) ⇒ Object

set line join style to round, miter, or bevel



235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/hotcocoa/graphics/canvas.rb', line 235

def linejoin(style=:miter)
  case style
  when :round
    join = KCGLineJoinRound
  when :bevel
    join = KCGLineJoinBevel
  when :miter
    join = KCGLineJoinMiter
  else
    raise "ERROR: line join style not recognized: #{style}"
  end
  CGContextSetLineJoin(@ctx,join)
end

#lines(points) ⇒ Object

draw a series of lines connecting the given array of points



357
358
359
360
361
# File 'lib/hotcocoa/graphics/canvas.rb', line 357

def lines(points)
  CGContextAddLines(@ctx, points, points.size)
  CGContextDrawPath(@ctx, KCGPathStroke) # apply stroke
  endpath
end

#lineto(x, y) ⇒ Object

draw a line from the current point to x,y



431
432
433
# File 'lib/hotcocoa/graphics/canvas.rb', line 431

def lineto(x, y)
  CGContextAddLineToPoint(@ctx ,x, y)
end

#moveto(x, y) ⇒ Object

move the "pen" to x,y



426
427
428
# File 'lib/hotcocoa/graphics/canvas.rb', line 426

def moveto(x, y)
  CGContextMoveToPoint(@ctx, x, y)
end

#new_path(x, y, &block) ⇒ Object



405
406
407
408
409
# File 'lib/hotcocoa/graphics/canvas.rb', line 405

def new_path(x, y, &block)
  beginpath(x, y)
  block.call
  endpath
end

#new_state(&block) ⇒ Object

STATE



499
500
501
502
503
# File 'lib/hotcocoa/graphics/canvas.rb', line 499

def new_state(&block)
  push
  block.call
  pop
end

#newpageObject

begin a new PDF page



642
643
644
645
646
647
648
649
650
# File 'lib/hotcocoa/graphics/canvas.rb', line 642

def newpage
  if (@filetype == :pdf)
    CGContextFlush(@ctx)
    CGPDFContextEndPage(@ctx)
    CGPDFContextBeginPage(@ctx, nil)
  else
    puts "WARNING: newpage only valid when using PDF output"
  end
end

#nodashObject

revert to solid lines



256
257
258
# File 'lib/hotcocoa/graphics/canvas.rb', line 256

def nodash
  CGContextSetLineDash(@ctx, 0.0, nil, 0)
end

#nofillObject

remove current fill



188
189
190
191
# File 'lib/hotcocoa/graphics/canvas.rb', line 188

def nofill
  CGContextSetRGBFillColor(@ctx, 0.0, 0.0, 0.0, 0.0) # RGBA
  @fill = nil
end

#noshadowObject

stop using a shadow



541
542
543
# File 'lib/hotcocoa/graphics/canvas.rb', line 541

def noshadow
  CGContextSetShadowWithColor(@ctx, [0,0], 1, nil)
end

#nostrokeObject

don't use a stroke for subsequent drawing operations



214
215
216
217
# File 'lib/hotcocoa/graphics/canvas.rb', line 214

def nostroke
  CGContextSetRGBStrokeColor(@ctx, 0, 0, 0, 0) # RGBA
  @stroke = false
end

#nsimageObject

SAVING/EXPORTING



623
624
625
626
627
# File 'lib/hotcocoa/graphics/canvas.rb', line 623

def nsimage
  image = NSImage.alloc.init
  image.addRepresentation(NSBitmapImageRep.alloc.initWithCGImage(cgimage))
  image
end

#openObject

open the output file in its associated application



687
688
689
# File 'lib/hotcocoa/graphics/canvas.rb', line 687

def open
  system "open #{@output}"
end

#oval(x = 0, y = 0, w = 20, h = 20, reg = @registration) ⇒ Object

inscribe an oval starting at x,y inside a rectangle having dimensions w,h



275
276
277
278
279
280
281
282
283
# File 'lib/hotcocoa/graphics/canvas.rb', line 275

def oval(x=0, y=0, w=20, h=20, reg=@registration)
  # center the oval
  if (reg == :center)
    x = x - w / 2
    y = y - w / 2
  end
  CGContextAddEllipseInRect(@ctx, NSMakeRect(x, y, w, h))
  CGContextDrawPath(@ctx, KCGPathFillStroke) # apply fill and stroke
end

#popObject

pop the previous drawing context off the stack



512
513
514
515
# File 'lib/hotcocoa/graphics/canvas.rb', line 512

def pop
  CGContextRestoreGState(@ctx)
  @stacksize = @stacksize - 1
end

#pushObject

push the current drawing context onto the stack



506
507
508
509
# File 'lib/hotcocoa/graphics/canvas.rb', line 506

def push
  CGContextSaveGState(@ctx)
  @stacksize = @stacksize + 1
end

#qcurve(cpx, cpy, x1, y1, x2, y2) ⇒ Object

draw a quadratic bezier curve from x1,y1 to x2,y2, given the coordinates of one control point



380
381
382
383
384
# File 'lib/hotcocoa/graphics/canvas.rb', line 380

def qcurve(cpx, cpy, x1, y1, x2, y2)
  beginpath(x1, y1)
  CGContextAddQuadCurveToPoint(@ctx, cpx, cpy, x2, y2)
  endpath
end

#qcurveto(cpx, cpy, x, y) ⇒ Object

draw a quadratic bezier curve from the current point, given the coordinates of one control point and an end point



441
442
443
# File 'lib/hotcocoa/graphics/canvas.rb', line 441

def qcurveto(cpx, cpy, x, y)
  CGContextAddQuadCurveToPoint(@ctx, cpx, cpy, x, y)
end

#quality(factor = 0.8) ⇒ Object

set compression (0.0 = max, 1.0 = none)



170
171
172
# File 'lib/hotcocoa/graphics/canvas.rb', line 170

def quality(factor=0.8)
  @quality = factor
end

#radial(gradient, sx = @width/2, sy = @height/2, er = @width/2, ex = sx, ey = sy, sr = 0.0) ⇒ Object

draw a radial gradiant starting at sx,sy with radius er optional: specify ending at ex,ey and starting radius sr



302
303
304
305
306
# File 'lib/hotcocoa/graphics/canvas.rb', line 302

def radial(gradient, sx=@width/2, sy=@height/2, er=@width/2, ex=sx, ey=sy, sr=0.0)
  #options = KCGGradientDrawsBeforeStartLocation
  #options = KCGGradientDrawsAfterEndLocation
  CGContextDrawRadialGradient(@ctx, gradient.gradient, NSMakePoint(sx, sy), sr, NSMakePoint(ex, ey), er, gradient.pre + gradient.post)
end

#rect(x = 0, y = 0, w = 20, h = 20, reg = @registration) ⇒ Object

draw a rectangle starting at x,y and having dimensions w,h



264
265
266
267
268
269
270
271
272
# File 'lib/hotcocoa/graphics/canvas.rb', line 264

def rect(x=0, y=0, w=20, h=20, reg=@registration)
  # center the rectangle
  if (reg == :center)
    x = x - w / 2
    y = y - h / 2
  end
  CGContextAddRect(@ctx, NSMakeRect(x, y, w, h))
  CGContextDrawPath(@ctx, KCGPathFillStroke)
end

#registration(mode = :center) ⇒ Object

set registration mode to :center or :corner



470
471
472
# File 'lib/hotcocoa/graphics/canvas.rb', line 470

def registration(mode=:center)
  @registration = mode
end

#resetObject

restore the initial context



518
519
520
521
522
523
# File 'lib/hotcocoa/graphics/canvas.rb', line 518

def reset
  until (@stacksize <= 1)
    pop  # retrieve graphics states until we get to the default state
  end
  push  # push the retrieved pristine default state back onto the stack
end

#rotate(deg = 0) ⇒ Object

rotate by the specified degrees



475
476
477
# File 'lib/hotcocoa/graphics/canvas.rb', line 475

def rotate(deg=0)
  CGContextRotateCTM(@ctx, radians(-deg));
end

#saveObject

save the image to a file



653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
# File 'lib/hotcocoa/graphics/canvas.rb', line 653

def save

  properties = {}
  #    exif = {}
  # KCGImagePropertyExifDictionary
  #    exif[KCGImagePropertyExifUserComment] = 'Image downloaded from www.sheetmusicplus.com'
  #    exif[KCGImagePropertyExifAuxOwnerName] = 'www.sheetmusicplus.com'
  if @filetype == :pdf
    CGPDFContextEndPage(@ctx)
    CGContextFlush(@ctx)
    return
  elsif @filetype == :png
    format = NSPNGFileType
  elsif @filetype == :tif
    format = NSTIFFFileType
    properties[NSImageCompressionMethod] = NSTIFFCompressionLZW
    #properties[NSImageCompressionMethod] = NSTIFFCompressionNone
  elsif @filetype == :gif
    format = NSGIFFileType
    #properties[NSImageDitherTransparency] = 0 # 1 = dithered, 0 = not dithered
    #properties[NSImageRGBColorTable] = nil # For GIF input and output. It consists of a 768 byte NSData object that contains a packed RGB table with each component being 8 bits.
  elsif @filetype == :jpg
    format = NSJPEGFileType
    properties[NSImageCompressionFactor] = @quality # (jpeg compression, 0.0 = max, 1.0 = none)
    #properties[NSImageEXIFData] = exif
  end
  cgimageref = CGBitmapContextCreateImage(@ctx)                      # => CGImageRef
  bitmaprep = NSBitmapImageRep.alloc.initWithCGImage(cgimageref)     # => NSBitmapImageRep
  blob = bitmaprep.representationUsingType(format, properties:properties) # => NSConcreteData
  blob.writeToFile(@output, atomically:true)
  true
end

#scale(x, y = x) ⇒ Object

scale drawing context by x,y



485
486
487
# File 'lib/hotcocoa/graphics/canvas.rb', line 485

def scale(x, y=x)
  CGContextScaleCTM(@ctx, x, y)
end

#shadow(dx = 0.0, dy = 0.0, a = 2.0/3.0, blur = 5) ⇒ Object

apply a drop shadow with offset dx,dy, alpha, and blur



529
530
531
532
# File 'lib/hotcocoa/graphics/canvas.rb', line 529

def shadow(dx=0.0, dy=0.0, a=2.0/3.0, blur=5)
  color = CGColorCreate(@colorspace, [0.0, 0.0, 0.0, a])
  CGContextSetShadowWithColor(@ctx, [dx, dy], blur, color)
end

#skew(x = 0, y = 0) ⇒ Object



489
490
491
492
493
494
# File 'lib/hotcocoa/graphics/canvas.rb', line 489

def skew(x=0, y=0)
  x = Math::PI * x / 180.0
  y = Math::PI * y / 180.0
  transform = CGAffineTransformMake(1.0, Math::tan(y), Math::tan(x), 1.0, 0.0, 0.0)
  CGContextConcatCTM(@ctx, transform)
end

#stroke(r = 0, g = 0, b = 0, a = 1.0) ⇒ Object

set stroke color (given a Color object, or RGBA values)



196
197
198
199
200
201
202
203
204
205
206
# File 'lib/hotcocoa/graphics/canvas.rb', line 196

def stroke(r=0, g=0, b=0, a=1.0)
  case r
  when Color
    g = r.g
    b = r.b
    a = r.a
    r = r.r
  end
  CGContextSetRGBStrokeColor(@ctx, r, g, b, a) # RGBA
  @stroke = true
end

#strokewidth(width = 1) ⇒ Object

set stroke width



209
210
211
# File 'lib/hotcocoa/graphics/canvas.rb', line 209

def strokewidth(width=1)
  CGContextSetLineWidth(@ctx, width.to_f)
end

#text(txt = "A", x = 0, y = 0) ⇒ Object

write the text at x,y using the current fill



575
576
577
578
579
580
581
582
583
# File 'lib/hotcocoa/graphics/canvas.rb', line 575

def text(txt="A", x=0, y=0)
  txt = txt.to_s unless txt.kind_of?(String)
  if @registration == :center
    width = textwidth(txt)
    x = x - width / 2
    y = y + @fsize / 2
  end
  CGContextShowTextAtPoint(@ctx, x, y, txt, txt.length)
end

#textwidth(txt, width = nil) ⇒ Object

determine the width of the given text without drawing it



586
587
588
589
590
591
592
593
594
# File 'lib/hotcocoa/graphics/canvas.rb', line 586

def textwidth(txt, width=nil)
  push
  start = CGContextGetTextPosition(@ctx)
  CGContextSetTextDrawingMode(@ctx, KCGTextInvisible)
  CGContextShowText(@ctx, txt, txt.length)
  final = CGContextGetTextPosition(@ctx)
  pop
  final.x - start.x
end

#translate(x, y) ⇒ Object

translate drawing context by x,y



480
481
482
# File 'lib/hotcocoa/graphics/canvas.rb', line 480

def translate(x, y)
  CGContextTranslateCTM(@ctx, x, y);
end

#verbose(tf = true) ⇒ Object

print drawing functions if verbose is true



155
156
157
# File 'lib/hotcocoa/graphics/canvas.rb', line 155

def verbose(tf=true)
  @verbose = tf
end