Clean edges

I am drawing some objects on a mac with retina. Specifically, I am trying to draw roundRects with a gradient, so I am using pictures and masks. Here is the code in the canvas paint event:

[code] dim startcolor as Color = &cA1A19900
dim endColor as color = &c6A696900
dim samt,eamt as Double

dim p as new picture(g.Width, g.Height, 32)

//SET MASK
p.mask.Graphics.ForeColor = &cFFFFFF00
p.mask.Graphics.FillRect(0, 0,g.Width, g.Height)
p.mask.Graphics.ForeColor = &c00000000
p.mask.Graphics.FillRoundRect(0, 0,g.Width, g.Height,10,10)

//SET GRADIENT
For i As Integer = 0 to p.Height
samt = 1 - (i / p.Height)
eamt = i / p.Height
p.Graphics.ForeColor = RGB((startColor.Red * samt) + (endColor.Red * eamt), _
(startColor.Green *samt) + (endColor.Green * eamt), _
(startColor.Blue * samt) + (endColor.Blue * eamt))

p.Graphics.DrawLine(0, i, p.Width, i)

Next

g.drawpicture(p, 0, 0)[/code]
I have enable high res using plists, but my question is why when I use a canvas to draw/fill a roundRec the edges are not as clean as a roundRec control?

This could be a problem of the render quality. Without a declare or plug-in, Xojo uses the default which is a compromise between speed and quality. Search the forum for the declare; I am sure it is around here where CGContext (= Graphics) drawing routines are discussed.

this may be what you are looking for

  #If TargetMacOS Then
    Dim Interpolation_Mode As Integer
    // provided by Sam Rowlands
    Const kCGInterpolationDefault = 0
    Const kCGInterpolationNone = 1
    Const kCGInterpolationLow = 2
    Const kCGInterpolationMedium = 4
    Const kCGInterpolationHigh = 3
    //
    Interpolation_Mode=kCGInterpolationNone
    //If w2>w1 Or h2>h1 Then Interpolation_Mode=kCGInterpolationMedium ' reduced images
    //
    Interpolation_Mode=2
    Declare Sub CGContextSetInterpolationQuality Lib "Cocoa" ( context As Integer, quality As Integer )
    CGContextSetInterpolationQuality( g.handle( g.HandleTypeCGContextRef ), Interpolation_Mode )
    //
  #EndIf

Dave,
I have inserted you code in the paint event, but changing the Interpolation_Mode has no effect on the outcome of the image that is drawn…

you did insert this code just PRIOR to the drawpicture event?
if can’t be global… it must alter the “graphics” object just prior to its use.

and to be fair… its Sam Rowlands code, not mine :slight_smile:

and in your code (above)… “G” would p.Graphics I believe

Correction, Sam’s code
Here is my paint event:

[code] dim startcolor as Color = &cA1A19900
dim endColor as color = &c6A696900
dim samt,eamt as Double

dim p as new picture(g.Width, g.Height, 32)

//SET MASK
p.mask.Graphics.ForeColor = &cFFFFFF00
p.mask.Graphics.FillRect(0, 0,g.Width, g.Height)
p.mask.Graphics.ForeColor = &c00000000
p.mask.Graphics.FillRoundRect(0, 0,g.Width, g.Height, 15, 15)

//SET GRADIENT
For i As Integer = 0 to p.Height
samt = 1 - (i / p.Height)
eamt = i / p.Height
p.Graphics.ForeColor = RGB((startColor.Red * samt) + (endColor.Red * eamt), _
(startColor.Green *samt) + (endColor.Green * eamt), _
(startColor.Blue * samt) + (endColor.Blue * eamt))

p.Graphics.DrawLine(0, i, p.Width, i)

Next

#If TargetMacOS Then
Dim Interpolation_Mode As Integer
// provided by Sam Rowlands
Const kCGInterpolationDefault = 0
Const kCGInterpolationNone = 1
Const kCGInterpolationLow = 2
Const kCGInterpolationMedium = 4
Const kCGInterpolationHigh = 3

Interpolation_Mode=kCGInterpolationHigh
Declare Sub CGContextSetInterpolationQuality Lib "Cocoa" ( context As Integer, quality As Integer )
CGContextSetInterpolationQuality(g.handle(g.HandleTypeCGContextRef ), Interpolation_Mode )
//

#EndIf

g.drawpicture(p, 0, 0)[/code]

try this

 dim startcolor as Color = &cA1A19900
  dim endColor as color = &c6A696900
  dim samt,eamt as Double
dim pg as graphics // <------ ADDED
  
  dim p as new picture(g.Width, g.Height, 32)
  
  //SET MASK
  p.mask.Graphics.ForeColor = &cFFFFFF00
  p.mask.Graphics.FillRect(0, 0,g.Width, g.Height)
  p.mask.Graphics.ForeColor = &c00000000
  p.mask.Graphics.FillRoundRect(0, 0,g.Width, g.Height, 15, 15)
  
  //SET GRADIENT 
pg=p.graphics // <----- ADDED

 #If TargetMacOS Then
    Dim Interpolation_Mode As Integer
    // provided by Sam Rowlands
    Const kCGInterpolationDefault = 0
    Const kCGInterpolationNone = 1
    Const kCGInterpolationLow = 2
    Const kCGInterpolationMedium = 4
    Const kCGInterpolationHigh = 3

    Interpolation_Mode=kCGInterpolationHigh
    Declare Sub CGContextSetInterpolationQuality Lib "Cocoa" ( context As Integer, quality As Integer )
    CGContextSetInterpolationQuality(pg.handle(pg.HandleTypeCGContextRef ), Interpolation_Mode ) // <--- USE PG not G

  #EndIf



  For i As Integer = 0 to p.Height
    samt = 1 - (i / p.Height)
    eamt = i / p.Height
  pg.ForeColor = RGB((startColor.Red * samt) + (endColor.Red * eamt), _
    (startColor.Green *samt) + (endColor.Green * eamt), _
    (startColor.Blue * samt) + (endColor.Blue * eamt))
    
pg.DrawLine(0, i, p.Width, i)
    
  Next

  g.drawpicture(p, 0, 0)

actually, you may need to apply this interpolation to the MASK, not the image now that I think about it
but it needs to be done, BEFORE you draw anything on it.

I was working on that when I saw your post, I even applied to to both just to see. Unfortunately, no change… :frowning:

[code] dim startcolor as Color = &cA1A19900
dim endColor as color = &c6A696900
dim samt,eamt as Double
dim pg as graphics // <------ ADDED
dim pmg as Graphics

dim p as new picture(g.Width, g.Height, 32)

//SET GRADIENT
pg=p.graphics // <----- ADDED
pmg = p.mask.graphics

#If TargetMacOS Then
Dim Interpolation_Mode As Integer
// provided by Sam Rowlands
Const kCGInterpolationDefault = 0
Const kCGInterpolationNone = 1
Const kCGInterpolationLow = 2
Const kCGInterpolationMedium = 4
Const kCGInterpolationHigh = 3

Interpolation_Mode=kCGInterpolationHigh
Declare Sub CGContextSetInterpolationQuality Lib "Cocoa" ( context As Integer, quality As Integer )
CGContextSetInterpolationQuality(pg.handle(pg.HandleTypeCGContextRef ), Interpolation_Mode ) // <--- USE PG not G
CGContextSetInterpolationQuality(pmg.handle(pmg.HandleTypeCGContextRef ), Interpolation_Mode )

#EndIf

//SET MASK
pmg.ForeColor = &cFFFFFF00
pmg.FillRect(0, 0,g.Width, g.Height)
pmg.ForeColor = &c00000000
pmg.FillRoundRect(0, 0,g.Width, g.Height, 15, 15)

For i As Integer = 0 to p.Height
samt = 1 - (i / p.Height)
eamt = i / p.Height
pg.ForeColor = RGB((startColor.Red * samt) + (endColor.Red * eamt), _
(startColor.Green *samt) + (endColor.Green * eamt), _
(startColor.Blue * samt) + (endColor.Blue * eamt))

pg.DrawLine(0, i, p.Width, i)

Next

g.drawpicture(p, 0, 0)[/code]

I don’t have retina to test but I think the problem is a Picture only has the pixels you request. This line creates WidthxHeight pixels for a non-retina size.

dim p as new picture(g.Width, g.Height, 32)

Drawing that picture with interpolation still doesn’t have that extra retina resolution. Plus I don’t think you want to interpolate when drawing at size.

Anyways, try making the Picture twice as big (so it has retina number of pixels) but draw it scaled down into the same size, so retina pixels in the same space.

dim p as new picture(g.Width*2, g.Height*2, 32) //... g.drawpicture(p, 0, 0, g.Width, g.Height, 0, 0, p.Width, p.Height)

Will
Thank you so much, that fixed the problem!
Good to know about how pictures and their number of pixels.

welcome to the wonderful world of supporting retina & hidpi :stuck_out_tongue:

Huh… What… I didn’t do it, no-one saw me do it, you can’t prove a thing…

It could be easier… Read More to find out how a housewife makes $3,000,000,000 an hour, with a mobile phone, toilet paper tube, PVA glue and some sticky backed plastic.

Ok, so everything was working great with drawing roundRecs using masks etc, but I am now trying to only do the drawing once and store the image as the canvas backdrop so I dont have to keep redrawing it…
The only problem is now I dont have a graphics object to call .drawPicture on unless I create a new picture object for the backdrop property, but then I run into the same scaling issue where I will only have half as many pixels as I need, and I cant just say self.backdrop = p because p is 2 times as big as I need…

Any thoughts there?

Don’t.
Store it as a picture variable at the double size, and use myvariable.drawpicture in the Paint event.

That doesn’t solve the problem of not redrawing it on every canvas refresh…

Another way to draw the 2x picture crisply is to set it’s Vertical and HorizontalResolution to 144. Then you don’t have to do the scaling in the draw, it’s just g.DrawPicture(mypic, x, y) and it’s crisp. Unfortunately this doesn’t work in the Backdrop (I don’t know if this is a bug).

What is working is to create an ‘Image’ Picture. For example, I put a 100x100 Canvas on a Window and put this in Canvas.Open

[code]Sub Open()

dim p As new Picture(200, 200)
p.Graphics.ForeColor = &c00FF00
p.Graphics.FillOval(0, 0, 200, 200)

dim img As new Picture(100, 100, Array(p))

me.Backdrop = img

End Sub[/code]

Notice p is created at 200x200 pixels but img is 100x100 points. This is enough to work, you don’t even need to set the V/H Resolution of p. img knows it’s supposed to fill the area 100x100 points and since it’s only bitmap is 200x200 pixels that gets drawn in the 100x100 point area.

Doesn’t the canvas refresh involve the redrawing anyway? including the backdrop.

And what is the problem you have with the redrawing?

Julen

Will, I will have to give this a shot.

The problem is that I am creating custom controls (many of which have a gradient) that need to be scroll-able across a window. When the window has a few of these controls and needs to scroll, all of the redrawing is slowing down the scroll pretty bad. I did not know if the backdrop got redrawn, I was hoping it didn’t.

Graphics are not my strong suit…