Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pixel Gap Between CGRect

Tags:

ios

swift

I'm drawing bar graphs, and I have several stacked CGRects that are directly on top of each other (i.e. one rect's minY is the previous rect's maxY). However, there are still semi-transparent gaps between the rects. Is there any way to fix this? I've found that this also happens when drawing touching adjacent arcs.

Here's a screenshot of what I mean:

enter image description here

By zooming in, I've confirmed that this isn't just an optical illusion like one would find between adjacent red and blue rects. I would appreciate any input.

 var upToNowSegmentTotal: CGFloat = 0    
 for k in 0..<data[i].bars[j].segments.count {
      var segmentRect: CGRect = CGRect()

      if barDirection == "vertical" {
          let a: CGFloat = translateY(upToNowSegmentTotal)
          let b: CGFloat = translateY(upToNowSegmentTotal + data[i].bars[j].segments[k].length)
          upToNowSegmentTotal += data[i].bars[j].segments[k].length

          var rectY: CGFloat

          if a > b {
              rectY = b
          } else {
              rectY = a
          }

          segmentRect = CGRect(
              x: barWidthPosition,
              y: rectY,
              width: barWidthAbsolute,
              height: abs(a - b)
          )
      }
 }

Ignore the stuff about the width of the bars. Here's the translateY function. Basically, it translates coordinates from the graphing window into x/y stuff that's drawn. Remember that because the window/ graphing area does not change between drawn rects, the same y input will always produce the same result.

private func translateY(y: CGFloat) -> CGFloat {
    if barsAreReversed {
        return graphingArea.minY + graphingArea.height * (y - graphingWindow.startValue) / (graphingWindow.length)
    } else {
        return graphingArea.maxY - graphingArea.height * (y - graphingWindow.startValue) / (graphingWindow.length)
    }
}

EDIT 2:

Here's a simplified version of my code that shows the problem:

override func drawRect(rect: CGRect) {
    let rect1: CGRect = CGRect(
        x: 0,
        y: 0,
        width: 40,
        height: 33.7
    )
    let rect2: CGRect = CGRect(
        x: 0,
        y: rect1.height,
        width: 40,
        height: 33.5
    )

    let context: CGContextRef = UIGraphicsGetCurrentContext()

    CGContextSetFillColorWithColor(context, UIColor(red: 1 / 255, green: 29 / 255, blue: 29 / 255, alpha: 1).CGColor)
    CGContextAddRect(context, rect1)
    CGContextFillRect(context, rect1)

    CGContextSetFillColorWithColor(context, UIColor(red: 9 / 255, green: 47 / 255, blue: 46 / 255, alpha: 1).CGColor)
    CGContextAddRect(context, rect2)
    CGContextFillRect(context, rect2)
}

It produces this:

enter image description here

like image 440
DefinitelyNotAPlesiosaur Avatar asked Jun 23 '26 01:06

DefinitelyNotAPlesiosaur


2 Answers

I suspect that in this particular case, the rects you are filling are not integral, i.e they might have origins/heights that are by default rendered with slightly transparent pixels (anti-aliasing). You could avoid this by properly rounding your Y-axis translation

private func translateY(y: CGFloat) -> CGFloat {
    if barsAreReversed {
        return round(graphingArea.minY + graphingArea.height * (y - graphingWindow.startValue) / (graphingWindow.length))
    } else {
        return round(graphingArea.maxY - graphingArea.height * (y - graphingWindow.startValue) / (graphingWindow.length))
    }
}

With arcs and other shapes it is not as easy, however, you could try and get rid of it, by leaving a bit of overlap between shapes. Of course, as pointed out by matt, you could simply turn anti-aliasing off, in which case these transparent "half-pixels" will all be rendered as if they are actually fully-within the rect.

like image 191
Henri Normak Avatar answered Jun 25 '26 14:06

Henri Normak


This is likely happening because the rectangle coordinates you are using to draw shapes are fractional values. As a result Core Graphics performs antialiasing at the edges of those rectangles when your coordinates land between pixel boundaries.

You could solve this by simply rounding the coordinates of the rectangles before drawing. You can use the CGRectIntegral function which performs this kind of rounding, for example:

CGContextFillRect(context, CGRectIntegral(rect1))
like image 39
Clafou Avatar answered Jun 25 '26 15:06

Clafou



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!