DrawRect takes integers

We are confusing the range the software supports and what the hardware supports. Any LED based screen (ie. most monitors including iPad/Iphone etc) has a FINITE number of pixels in the horizontal and vertical locations. In Retina devices these can be referenced as fractional points ( 0.5 or 0.3333 on @2x or @3x devices)…

AntiAliasing, is a mind trick … it simply alters the colors of adjacent pixels to make the eye percieve a smoother transistion. This transition is cleaner on Retina devices because the pixels are smaller.


NonRetina Devices
# = pixel / point

@2x Devices
# = pixel

##
## = point

@3x devices

# = pixel

###
### = point
###

but nobody has a unit that is a FRACTIONAL pixel…

Never said either did not exist. Without antialiasing and the blurry edges it provides, subpixel positioning would be impossible.

Shades of color and color contrast permit all sorts of subpixel tricks, reason why some icons look very detailed, although they show on small grids.

Here’s the difference between integer coordinates (left) and float (right)

Integers clamp the geometry before rendering so the coordinates shift and you get warbles. With floats the rendering can take into account those subpixel positions and produces a more accurate curve, no warbles.

But if you’re only drawing crisp edged rects aligned on pixels this doesn’t really matter.

Although the bottom image was made in retina mode where the warbles are more pronounced because with the default setup of ScaleXY=2 you can only address every other pixel.

Will… cite your source… show how you drew the two curves that results in jaggies …
because again… assuming the same display/mode, the # of picture elements remains constant… it becomes a matter of how the underlying software controls the hardware.

on a @2x display, if you set one pixel to black and the one next to it in white you might get something like this, using integer number

pixel(0,0)=black
pixel(1,0)=white

[00][00][FF][FF]
[00][00][FF][FF]

with floating point numbers you might get this instead

pixel(0.0,0.0)=black
pixel(0.5,0.0)=white
pixel(1.0,0.0)=black

[00][7F][7F][FF]
[00][7F][7F][FF]

these values are not necessarily exact, but used to illustrate a point (pun intended)

I have an old bezier class that draws with lots of Graphics.drawLines, that’s the left rendering.

I’ve since created a class to replace Graphics which wraps almost all of the CGContext functions. To make the right rendering I hacked the bezier to use XGraphics which draws with lots of strokeLines, taking floats. One of the main reasons I made this class is for the floats.

Anyways the hacked code has a whole lot of other stuff going on, I can try to put together a standalone snippet later on.

This is where the difference is. In the sense that hardware is the pixel values in memory and the underlying software is Core Graphics. CG is designed to work with floating point coordinates and takes that into account when generating those pixels. It works out the pixel intensities for a line going from <2.23, 2.41> to <100.12, 35.2>. But, if you clamp the coordinates to integers before sending them to CG it ends up setting pixel intensities for a line going from <2, 2> to <100, 35>. That’s the warble. CG is very good at antialiasing the intended geometry so when coordinates are clamped to the lattice of integers this shifted geometry may be noticeable.

That is the exact point I have been trying to make… ultimately its designed to trick the eye…

I don’t know that it originally was. My understanding is that the CGContext subsystem was designed to be an intermediary resolution-less layer, this way it doesn’t matter what you specify it will come out beautifully (or crappy if you specify crap) on the rendering device, be it screen, a bitmap context, NSImage, PDF context, Post Script context.

We use it a lot when printing (because of the integer limitations in Xojo’s graphics functions), it enables us to print nice sharp lines at a width of less than a point (which is 1/72th of an inch). It can look a bit wrong on a non-retina display, but certainly is sharper on retina and then eye-bleeding sharp when printed.

I guess what I’m trying to say, what may be the wrong thing in this debate actually, is that Apple’s choice of floats is resolution-less, where as integers is limited to 72-DPI unless you change the scale of the context (at which point you’re no longer working in points).

I am sorry, but the algorithm used to draw the left curve is simply foul. It has nothing to do with the way Integer can truncate data in a Bezier curve.

The main issue with pixels is mathematical rounding versus optical effect rounding. unless some sophisticated algorithms are applied, edge cases like 1/2 pixels will render badly because every other time the pixel will fall on the bad side. I think Core Graphics precisely does some interpolation to decide which way the pixel will fall, resulting in smoother curves.

When antialiasing is used, subpixel positioning becomes just as natural as what happens in a photograph with details smaller than pixels : the brain tends to reconstruct the missing pieces. So it is not so much the eye that is tricked, but our perception. In the end, I do agree that drawing in Xojo is somewhat crude, with only Integer values.

In fonts, we use levels of precision way higher than the actual number of pixels used to represent the character (usually 1000 x 1000 today), but the imager’s antialiasing does a superb job at rendering these Bezier curves at all sizes.

But that’s precisely what’s happening. CG can render geometry with subpixel accuracy, so when the coordinates are clamped to integers it’s accurately rendering those warbled coordinates.

To be fair I was passing the float values directly to DrawLine which effectively Floors the value. By Rounding there’s less warble but it’s definitely still happening.

So wait, you do see the issue? :stuck_out_tongue:

Maybe I misunderstand your point too. I thought it was along the lines of ‘you don’t lose anything with just integers’ so I’m trying to point out that CG does have the capability of floats and integers impose a slight limitation. I do wonder why Graphics uses integers. Did way back when one or the other OSes not support floats? Could Xojo add floats and be backwards compatible?

Here’s the smallest demo I could make. First some float coordinates are made, then drawn with Graphics, then CG, then CG again with rounded values to show it’s the same as Graphics. (Note there’s better ways to draw a curve in cg but this code is replicating individual DrawLines).

[code]Sub Paint(g As Graphics, areas() As REALbasic.Rect)

//create shape
dim x(), y() As CGFloat
for i As integer = 0 to 110
x.Append 10 + i * 1.72
y.Append 10 + (i/55)^2 * 30
next

//draw with integers
g.ForeColor = &c000000
g.PenWidth = 2
for i As integer = 0 to x.Ubound-1
'g.DrawLine x(i), y(i), x(i+1), y(i+1) //flooring
g.DrawLine Round(x(i)), Round(y(i)), Round(x(i+1)), Round(y(i+1))
next

//setup cg
const cglib = “CoreGraphics”
declare sub CGContextScaleCTM lib cglib (cntxt As Ptr, sx As CGFloat, sy As CGFloat)
declare sub CGContextTranslateCTM lib cglib (cntxt As Ptr, tx As CGFloat, ty As CGFloat)
declare sub CGContextSetRGBStrokeColor lib cglib (cntxt As Ptr, r As CGFloat, g As CGFloat, b As CGFloat, a As CGFloat)
declare sub CGContextSetLineWidth lib cglib (c As Ptr, width As CGFloat)
declare sub CGContextMoveToPoint lib cglib (context As Ptr, x As CGFloat, y As CGFloat)
declare sub CGContextAddLineToPoint lib cglib (context As Ptr, x As CGFloat, y As CGFloat)
declare sub CGContextStrokePath lib cglib (cntxt As Ptr)
dim cntxt As Ptr = Ptr( g.Handle(Graphics.HandleTypeCGContextRef) )
CGContextScaleCTM(cntxt, 1, -1)
CGContextTranslateCTM(cntxt, 0, -g.Height)

//draw with cg, shifted down 100 and float
CGContextSetRGBStrokeColor(cntxt, 0, 0, 0, 1)
CGContextSetLineWidth(cntxt, 2)
for i As integer = 0 to x.Ubound-1
CGContextMoveToPoint(cntxt, x(i), y(i) + 100)
CGContextAddLineToPoint(cntxt, x(i+1), y(i+1) + 100)
CGContextStrokePath(cntxt)
next

//draw with cg, shifted down 200 and clamped to integer
CGContextSetRGBStrokeColor(cntxt, 0, 0, 0, 1)
CGContextSetLineWidth(cntxt, 2)
for i As integer = 0 to x.Ubound-1
CGContextMoveToPoint(cntxt, Round(x(i)), Round(y(i)) + 200)
CGContextAddLineToPoint(cntxt, Round(x(i+1)), Round(y(i+1)) + 200)
CGContextStrokePath(cntxt)
next

End Sub[/code]

screencapture in retina mode…

Way back when graphics were treated only as pixels. It is not a matter of supporting floating point values, it is a matter of conception of the way a screen should be addressed.

At the same time, font renderers did exactly the same : a pixel was lit, or not, and nothing in between.

The main innovation was antialiasing, where usage of grey pixels or contrasting colors enabled to create the impression of subpixel objects. While technically pixels remain quanta , the interpretation of the curves enable that trick. I am not sure Xojo needs to rework its integer addressing of pixels. What is needed is a software layer that does the imaging, just the way a photograph will interpret a curve in shades. What Core Graphics does so brilliantly.

I have issue with the way you proceed. A 17 pixel thick succession of lines is way too far from pixels to demonstrate anything. Besides, Xojo already anti-aliases drawoval, so it can never produce that kind of effect :

Here, each square is a blown up pixel.

Your demonstration would hold much more interest with pixel-level drawing.

I’m so confused (>‘.’)> O_o

What’s 17 pixels? I’m just drawing lines and showing the difference between integer and float coordinates. Also the curve is a parabola so part of a circle or oval doesn’t match.

What do you mean by ‘pixel-level drawing’? Setting individual pixels? That’s not what I’m taking about and I’m not sure how to better demonstrate it.

I measured the 17 pixels wide lines on your screen grab.

To stay in the topic of why Xojo uses Integers instead of doubles, the only place where fractional pixel positioning would eventually make a difference is precisely at the pixel level.

Note that already iOSGraphics use doubles for linWwidth, drawImage, drawLine, drawOval, etc. With some luck this part of the new framework will make it into the other platforms.

I just played with a small iOS test project for iPhone 6+ which has a 3x Retina, using iOSGraphics. Using doubles effectively taps into subPoint resolution.

g.LineWidth = 0.1 g.Drawline(0,10, 100, 10) g.Drawline(100,10.3, 200, 10.3)

Here is the result around 10, 100 :

Clearly the drawing in double taps into fractions of points, in a much simpler way than current Classic HiDPI support.

Note how lines smaller than pixel size (1/3) are rendered with shades of grey.

Oh I didn’t know that. That’s great news. I can only assume it’s in the pipeline!

Note how lines smaller than POINT size (1/3) are rendered with shades of grey.

I don’t understand why this concept is so hard to grasp. Subpixel like AntiAlias are mind tricks, they do not magically cause a monitor or iPhone to suddenly be able to show higher resolution images… they just make it SEEM like they do.

OK, so that’s what you thought I was saying? No, I understand what pixels are, if I was giving that impression it must’ve been late night writing. I’m just pointing out how anti-aliasing takes into account float coordinates so you will get a different result of pixels if those input coordinates have been rounded to integers, which is demonstrated by the code and picture.

My impression from what is being said here, is that the over-whelming perception is subpixels are the ability to control individual componets of a “pixel”, where as that is not true, a pixel is the smallest physical addressable unit… period.

A point is made up of 1 or more (4 in the case of @2x and 9 in the case of @3x) pixels…
But just because you tell a given POINT to be PURE RED (&cFF0000) , the underlying software may alter that “PURE RED” into something else at the encompassed PIXELs based on the values of PIXELS that are adjacent, in order to again “trick the mind” into seeing something better than what the original was.

[quote=264813:@Dave S]
I don’t understand why this concept is so hard to grasp. Subpixel like AntiAlias are mind tricks, they do not magically cause a monitor or iPhone to suddenly be able to show higher resolution images… they just make it SEEM like they do.[/quote]

Dave, the very fact that iOSGraphics is able to directly address pixels instead of points by merely using half points or third of point is in itself a great advantage over the scale used by classic HiDPI support.

Second, having the system automatically emulate subpixel lines with shades is very convenient. Of course this is smoke and mirrors that the brain gobbles as something that does not exist, like portions of pixels.

Ah. When I’m talking about floats I mean the coordinates being draw with. And when I say ‘pixel value’ I mean the rgba values.

But I would disagree that this is a trick of the mind. To me it’s accuracy in rendering pixel rgb values. Like how a 3D raytracer will subsample a pixel to determine 1/17 of the pixel is covered by one triangle and 1/3 is covered by another. The resulting pixel represents the measured ‘light’ at that pixel, it’s a better physical representation and that’s why it looks more accurate, because it is.

These pics show a line drawn and scaled up to see the pixels and the geometry of that line is drawn on top. The green dots are the received coordinates of the line segment and the red box is the extent of the lines geometric coverage. The top picture uses float coordinates, notice how the green dots are not in the very center of a pixel. In the bottom picture the coordinates have been shifted to the center of pixels (integer coordinate) so it’s a different geometry that gets very accurately rendered into pixels. All I’m talking about is how the coordinates get shifted so the geometry being rendered is different which produces different pixel rgba values. That shifted geometry is apparent in the curves posted above.

EXACTLY… that is what antialias (or whatever name you wish to give it) is all about… the SOFTWARE takes the “precise” colors specified, and determines an appropriate new color to provide [quote]a better physical representation and that’s why it looks more accurate[/quote] … Not because it “is”, but because it LOOKS like it is.

If one could draw at “subpixel” level, then I’d argue that the pixels were simply smaller … or the display was analog and not digital
Your own examples above prove what I have been attempting to say for days … neither line is a “straight line”… if they were, then you would see ONLY the part you outlined in red… what you have is subtle variations the TRICK THE MIND into thinking it is a straight line… The closer the eye gets the more the brain sees thru the “trick”, the farther away, the more the brain thinks it is a sold contigous one color line when in reality its dozens or hundreds of shades.

There is no reality, there is only perception