Canvas & CGContext

Mac OSX specific forum
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Canvas & CGContext

Post by wilbert »

Is it possible to get a CGContextRef for a PureBasic canvas inside StartDrawing(CanvasOutput( )).
It would be great if it were possible to use OS X drawing functions to draw curves etc.
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Re: Canvas & CGContext

Post by freak »

There is no CGContext. The drawing is done by direct pixel manipulation in memory.
quidquid Latine dictum sit altum videtur
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Canvas & CGContext

Post by wilbert »

Thanks for the answer.
I hoped it would be possible. I guess the only workaround is to create a cgimage and draw that onto the canvas.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Canvas & CGContext

Post by wilbert »

Is it allowed to create a context like this ?

Code: Select all

Structure ColorComponents
  r.f
  g.f
  b.f
  a.f
EndStructure

ImportC ""
  
  ; colorspace functions
  CGColorSpaceCreateDeviceRGB()
  CGColorSpaceRelease(colorspace)
  
  ; bitmapcontext functions
  CGBitmapContextCreate(*bdata, width, height, bitsPerComponent, bytesPerRow, colorspace, bitmapInfo)
  CGContextRelease(context)
  
  ; cgcontext functions to init and restore / release some things 
  CGContextSaveGState(context)
  CGContextRestoreGState(context)
  CGContextScaleCTM(context, sx.f, sy.f)
  CGContextTranslateCTM(context, tx.f, ty.f)
  CGContextSetStrokeColorSpace(context, colorspace)
  CGContextSetFillColorSpace(context, colorspace)
  
  ; cgcontext drawing related functions
  
  CGContextSetStrokeColor(context, color)
  CGContextSetFillColor(context, color)
  
  CGContextClearRect(context, x.f, y.f, w.f, h.f)
  
  CGContextBeginPath(context)
  CGContextMoveToPoint(context, x.f, y.f)
  CGContextAddLineToPoint(context, x.f, y.f)
  CGContextAddArc(context, x.f, y.f, radius.f, startAngle.f, endAngle.f, clockwise)
  CGContextClosePath(context)
  CGContextStrokePath(context)
  CGContextFillPath(context)
  
  CGContextSetShadow(context, x_offset.f, y_offset.f, blur.f)
  
  CGContextFillRect(context, x.f, y.f, w.f, h.f)
  
EndImport

Procedure RenderOnCanvas(canvas)
  
  Protected cv_w, cv_h, csRGB, ctx
  Protected color.ColorComponents
  
  If StartDrawing(CanvasOutput(canvas))
    
    ; init some things
    cv_w = OutputWidth()
    cv_h = OutputHeight()
    csRGB = CGColorSpaceCreateDeviceRGB()
    ctx = CGBitmapContextCreate(DrawingBuffer(), cv_w, cv_h, 8, cv_w << 2, csRGB, 1)
    CGContextScaleCTM(ctx, 1, -1)
    CGContextTranslateCTM(ctx, 0, -cv_h)
    CGContextSetStrokeColorSpace(ctx, csRGB)
    CGContextSetFillColorSpace(ctx, csRGB)
    
    ; actual drawing
    CGContextSetShadow(ctx, 2, -2, 2)
    color\r = 1
    color\g = 0.5
    color\b = 0
    color\a = 1
    CGContextSetFillColor(ctx, color)
    CGContextFillRect(ctx, 5, 5, 50, 50)
    
    color\r = 0.5
    color\g = 1
    color\b = 0
    color\a = 1
    CGContextSetFillColor(ctx, color)
    CGContextBeginPath(ctx)
    CGContextAddArc(ctx, 80, 80, 25, 0, #PI, #True)
    CGContextFillPath(ctx)
    
    ; release some things
    CGContextRelease(ctx)
    CGColorSpaceRelease(csRGB)
    StopDrawing()
    
  EndIf
  
EndProcedure

If OpenWindow(0, 0, 0, 320, 220, "CanvasGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  
  CanvasGadget(0, 10, 10, 300, 200)
  RenderOnCanvas(0)
  
  Repeat
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
  
EndIf
Last edited by wilbert on Sat Aug 27, 2011 5:46 pm, edited 2 times in total.
User avatar
J. Baker
Addict
Addict
Posts: 2178
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Re: Canvas & CGContext

Post by J. Baker »

Awesome wilbert! I was just reading about this earlier. Thanks! ;)
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef

Mac: 10.13.6 / 1.4GHz Core 2 Duo / 2GB DDR3 / Nvidia 320M
PC: Win 7 / AMD 64 4000+ / 3GB DDR / Nvidia 720GT


Even the vine knows it surroundings but the man with eyes does not.
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Re: Canvas & CGContext

Post by freak »

wilbert wrote:Is it allowed to create a context like this ?
The fact that StartDrawing() returns the pixel data pointer for the canvas on OSX is not documented. While i don't think this will really change, its not good to rely on it. If you use the DrawingBuffer() command, you will get the same pointer but it will be more future proof.

Nice work btw.
quidquid Latine dictum sit altum videtur
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Canvas & CGContext

Post by wilbert »

Thanks Freak.
I changed it so it's compatible now :D
It also works on images if you use ImageOutput instead of CanvasOutput.

A little note ...
The coordinate space of a CGContext is vertically flipped comparing to that of PureBasic.
To make it in line with what PureBasic uses, I used CGContextScaleCTM and CGContextTranslateCTM.
This doesn't affect the shadow so the y_offset of the shadow is a bit illogical now.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Canvas & CGContext

Post by wilbert »

I simplified some things and added some more imports

cgcontext.pbi

Code: Select all

Enumeration
  #kCGLineJoinMiter
  #kCGLineJoinRound
  #kCGLineJoinBevel
EndEnumeration

Enumeration
  #kCGLineCapButt
  #kCGLineCapRound
  #kCGLineCapSquare
EndEnumeration

Enumeration
  #kCGPathFill
  #kCGPathEOFill
  #kCGPathStroke
  #kCGPathFillStroke
  #kCGPathEOFillStroke
EndEnumeration

Enumeration
  #kCGBlendModeNormal
  #kCGBlendModeMultiply
  #kCGBlendModeScreen
  #kCGBlendModeOverlay
  #kCGBlendModeDarken
  #kCGBlendModeLighten
  #kCGBlendModeColorDodge
  #kCGBlendModeColorBurn
  #kCGBlendModeSoftLight
  #kCGBlendModeHardLight
  #kCGBlendModeDifference
  #kCGBlendModeExclusion
  #kCGBlendModeHue
  #kCGBlendModeSaturation
  #kCGBlendModeColor
  #kCGBlendModeLuminosity
EndEnumeration

ImportC ""
  
  ; colorspace functions
  CGColorSpaceCreateDeviceRGB()
  CGColorSpaceRelease(colorspace)
  
  ; bitmapcontext functions
  CGBitmapContextCreate(*bdata, width, height, bitsPerComponent, bytesPerRow, colorspace, bitmapInfo)
  CGContextRelease(context)
  
  ; cgcontext functions to init and restore / release some things 
  CGContextSaveGState(context)
  CGContextRestoreGState(context)
  CGContextScaleCTM(context, sx.f, sy.f)
  CGContextTranslateCTM(context, tx.f, ty.f)
  CGContextSetStrokeColorSpace(context, colorspace)
  CGContextSetFillColorSpace(context, colorspace)
  CGContextSetStrokeColor(context, color)
  CGContextSetFillColor(context, color)
  
  ; cgcontext drawing related functions
  CGContextAddArc(context, x.f, y.f, radius.f, startAngle.f, endAngle.f, clockwise)
  CGContextAddArcToPoint(context, x1.f, y1.f, x2.f, y2.f, radius.f)
  CGContextAddCurveToPoint(context, cp1x.f, cp1y.f, cp2x.f, cp2y.f, x.f, y.f)
  CGContextAddEllipseInRect(context, x.f, y.f, w.f, h.f)
  CGContextAddLineToPoint(context, x.f, y.f)
  CGContextAddQuadCurveToPoint(context, cpx.f, cpy.f, x.f, y.f)
  CGContextAddRect(context, x.f, y.f, w.f, h.f)
  CGContextBeginPath(context)
  CGContextClearRect(context, x.f, y.f, w.f, h.f)
  CGContextClosePath(context)
  CGContextDrawImage(context, x.f, y.f, w.f, h.f, image)
  CGContextDrawPath(context, mode)
  CGContextDrawTiledImage(context, x.f, y.f, w.f, h.f, image)
  CGContextFillEllipseInRect(context, x.f, y.f, w.f, h.f)
  CGContextFillPath(context)
  CGContextFillRect(context, x.f, y.f, w.f, h.f)
  CGContextMoveToPoint(context, x.f, y.f)
  CGContextSetAlpha(context, alpha.f)
  CGContextSetBlendMode(context, mode)
  CGContextSetLineCap(context, cap)
  CGContextSetLineJoin(context, join)
  CGContextSetLineWidth(context, width.f)
  CGContextSetMiterLimit(context, limit.f)
  CGContextSetShadow(context, x_offset.f, y_offset.f, blur.f)
  CGContextSetShadowWithColor(context, x_offset.f, y_offset.f, blur.f, cgcolor)
  CGContextStrokeEllipseInRect(context, x.f, y.f, w.f, h.f)
  CGContextStrokePath(context)
  CGContextStrokeRect(context, x.f, y.f, w.f, h.f)
  CGContextStrokeRectWithWidth(context, x.f, y.f, w.f, h.f, width.f)
  
EndImport

Procedure CGContextCreate(flipped = #True)
  Protected w, h, csRGB, ctx
  w = OutputWidth()
  h = OutputHeight()
  csRGB = CGColorSpaceCreateDeviceRGB()
  ctx = CGBitmapContextCreate(DrawingBuffer(), w, h, 8, w << 2, csRGB, 1)
  If flipped
    CGContextScaleCTM(ctx, 1, -1)
    CGContextTranslateCTM(ctx, 0, -h)
  EndIf
  CGContextSetStrokeColorSpace(ctx, csRGB)
  CGContextSetFillColorSpace(ctx, csRGB)
  CGColorSpaceRelease(csRGB)
  ProcedureReturn ctx
EndProcedure

Procedure CGContextSetStrokeColorRGB(ctx, r.f, g.f, b.f, a.f = 1.0)
  CGContextSetStrokeColor(ctx, @r)
EndProcedure

Procedure CGContextSetFillColorRGB(ctx, r.f, g.f, b.f, a.f = 1.0)
  CGContextSetFillColor(ctx, @r)
EndProcedure

Procedure CGContextClearShadow(ctx)
  CGContextSetShadowWithColor(ctx, 0, 0, 0, #Null)
EndProcedure
test code

Code: Select all

IncludeFile "cgcontext.pbi"

Procedure RenderOnCanvas(canvas)
  
  Protected ctx
  
  If StartDrawing(CanvasOutput(canvas))
    
    ; create a context
    ctx = CGContextCreate()
    
    ; drawing
    CGContextSetShadow(ctx, 2, -2, 2)
    CGContextSetFillColorRGB(ctx, 1, 0.5, 0)
    CGContextFillRect(ctx, 5, 5, 50, 50)
    CGContextFillRect(ctx, 105, 105, 50, 50)
    CGContextSetStrokeColorRGB(ctx, 0.9, 0.9, 0)
    CGContextSetFillColorRGB(ctx, 0.5, 1, 0)
    CGContextSetLineWidth(ctx, 2)
    CGContextClearShadow(ctx)
    CGContextBeginPath(ctx)
    CGContextAddArc(ctx, 80, 80, 25, #PI, 0, #True)
    CGContextAddQuadCurveToPoint(ctx, 130, 120, 105, 140)
    CGContextAddQuadCurveToPoint(ctx, 80, 160, 40, 140)
    CGContextDrawPath(ctx, #kCGPathFillStroke)
    
    ; release context and stop drawing
    CGContextRelease(ctx)
    StopDrawing()
    
  EndIf
  
EndProcedure

If OpenWindow(0, 0, 0, 320, 220, "CanvasGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  
  CanvasGadget(0, 10, 10, 300, 200)
  RenderOnCanvas(0)
  
  Repeat
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
  
EndIf
Last edited by wilbert on Sun Aug 28, 2011 8:03 am, edited 2 times in total.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8425
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Canvas & CGContext

Post by netmaestro »

I wish I had a mac so I could see how this looks. Any chance you could post a screenshot?
BERESHEIT
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Canvas & CGContext

Post by wilbert »

It doesn't show much Netmaestro. :oops:
I made it a little more complicated so it shows at least some curves. :)
Image
The advantage of CGContext functions is that you can draw arcs, curves, use blend modes, transparency and that everything is antialiased.
It should make it easier to create better looking canvas examples.
Last edited by wilbert on Wed May 31, 2017 3:13 pm, edited 1 time in total.
Christian
Enthusiast
Enthusiast
Posts: 154
Joined: Mon Dec 08, 2003 7:50 pm
Location: Germany

Re: Canvas & CGContext

Post by Christian »

Hi there,

I compiled this code (PB 5.51), but it only shows a blank canvas. Is it supposed to work still or do internal changes in PB made since 2011 make it impossible?
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Canvas & CGContext

Post by wilbert »

The drawing function imports in its current form are only working on the 32 bit version of PureBasic.
Making it work on x64 isn't straightforward and would require a lot of changes.

If it has enough functionality for you, I recommend using the PureBasic VectorDrawing library.
It wasn't available when the code was created but serves more or less the same purpose and has the advantage of being cross platform.
Windows (x64)
Raspberry Pi OS (Arm64)
Christian
Enthusiast
Enthusiast
Posts: 154
Joined: Mon Dec 08, 2003 7:50 pm
Location: Germany

Re: Canvas & CGContext

Post by Christian »

Thanks Wilbert.

Actually I was not to much in the Path-Function. I was rather looking for a way to make the CanvasGadget display in Retina resolution. Thought that the CGContext* functions might be a possibility, when adjusting the NSSize of the image (as suggested in http://www.purebasic.fr/english/viewtop ... 11#p442611, which works perfectly for a ImageGadget) and drawing the image it directly on the canvas CGContextRef.
Post Reply