Skip to content

Commit

Permalink
Fix pixel alignment math (#312)
Browse files Browse the repository at this point in the history
  • Loading branch information
bryankeller authored Jul 31, 2024
1 parent 87b81e2 commit e19a229
Show file tree
Hide file tree
Showing 6 changed files with 497 additions and 502 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed
- Fixed an issue that could cause accessibility focus to shift unexpectedly
- Fixed a screen-pixel alignment issue

### Changed
- Rewrote accessibility code to avoid posting notifications, which causes poor Voice Over performance and odd focus bugs
Expand Down
4 changes: 2 additions & 2 deletions Sources/Internal/FrameProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ final class FrameProvider {
let origin: CGPoint
if distanceFromAdjacentDay < 0 {
let proposedX = adjacentDayFrame.minX - content.horizontalDayMargin - daySize.width
if proposedX > minX || proposedX.isEqual(to: minX, threshold: 1 / scale) {
if proposedX > minX || proposedX.isEqual(to: minX, screenScale: scale) {
origin = CGPoint(x: proposedX, y: adjacentDayFrame.minY)
} else {
origin = CGPoint(
Expand All @@ -239,7 +239,7 @@ final class FrameProvider {
}
} else {
let proposedX = adjacentDayFrame.maxX + content.horizontalDayMargin
if proposedX < maxX || proposedX.isEqual(to: maxX, threshold: 1 / scale) {
if proposedX < maxX || proposedX.isEqual(to: maxX, screenScale: scale) {
origin = CGPoint(x: proposedX, y: adjacentDayFrame.minY)
} else {
origin = CGPoint(x: minX, y: adjacentDayFrame.maxY + content.verticalDayMargin)
Expand Down
22 changes: 11 additions & 11 deletions Sources/Internal/ScreenPixelAlignment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ extension CGFloat {
(self * scale).rounded() / scale
}

/// Tests `self` for approximate equality using the threshold value. For example, 1.48 equals 1.52 if the threshold is 0.05.
/// `threshold` will be treated as a positive value by taking its absolute value.
func isEqual(to rhs: CGFloat, threshold: CGFloat) -> Bool {
abs(self - rhs) <= abs(threshold)
/// Tests `self` for approximate equality, first rounding the operands to be pixel-aligned for a screen with the given
/// `screenScale`. For example, 1.48 equals 1.52 if the `screenScale` is `2`.
func isEqual(to rhs: CGFloat, screenScale: CGFloat) -> Bool {
let lhs = alignedToPixel(forScreenWithScale: screenScale)
let rhs = rhs.alignedToPixel(forScreenWithScale: screenScale)
return lhs == rhs
}

}
Expand All @@ -35,13 +37,11 @@ extension CGRect {
/// Rounds a `CGRect`'s `origin` and `size` values so that they're aligned on pixel boundaries for a screen with the provided
/// scale.
func alignedToPixels(forScreenWithScale scale: CGFloat) -> CGRect {
let alignedX = minX.alignedToPixel(forScreenWithScale: scale)
let alignedY = minY.alignedToPixel(forScreenWithScale: scale)
return CGRect(
x: alignedX,
y: alignedY,
width: maxX.alignedToPixel(forScreenWithScale: scale) - alignedX,
height: maxY.alignedToPixel(forScreenWithScale: scale) - alignedY)
CGRect(
x: minX.alignedToPixel(forScreenWithScale: scale),
y: minY.alignedToPixel(forScreenWithScale: scale),
width: width.alignedToPixel(forScreenWithScale: scale),
height: height.alignedToPixel(forScreenWithScale: scale))
}

}
Expand Down
6 changes: 3 additions & 3 deletions Tests/FrameProviderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -528,10 +528,10 @@ final class FrameProviderTests: XCTestCase {
height: 34.857142857142854)
.alignedToPixels(forScreenWithScale: 3)
let expectedFrameAfterMiddleDay = CGRect(
x: 187.33333333333334,
x: 187.66666666666666,
y: 374.3333333333333,
width: 34.857142857142854,
height: 34.857142857142854)
width: 35,
height: 35)
.alignedToPixels(forScreenWithScale: 3)
XCTAssert(frameBeforeMiddleDay == expectedFrameBeforeMiddleDay, "Incorrect frame for day.")
XCTAssert(frameAfterMiddleDay == expectedFrameAfterMiddleDay, "Incorrect frame for day.")
Expand Down
20 changes: 7 additions & 13 deletions Tests/ScreenPixelAlignmentTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,15 @@ final class ScreenPixelAlignmentTests: XCTestCase {

func test2xScaleRectAlignment() {
let rect = CGRect(x: 5.299999, y: -19.1994, width: 20.25, height: 0.76)
let expectedRect = CGRect(x: 5.5, y: -19, width: 20, height: 0.5)
let expectedRect = CGRect(x: 5.5, y: -19, width: 20.5, height: 1)
XCTAssert(
rect.alignedToPixels(forScreenWithScale: 2) == expectedRect,
"Incorrect screen pixel alignment")
}

func test3xScaleRectAlignment() {
let rect = CGRect(x: 71.13, y: 71.19, width: 20.25, height: 2)
let expectedRect = CGRect(x: 71, y: 71.33333333333333, width: 20.33333333333333, height: 2)
let expectedRect = CGRect(x: 71, y: 71.33333333333333, width: 20.333333333333332, height: 2)
XCTAssert(
rect.alignedToPixels(forScreenWithScale: 3) == expectedRect,
"Incorrect screen pixel alignment")
Expand All @@ -125,17 +125,11 @@ final class ScreenPixelAlignmentTests: XCTestCase {
// MARK: CGFloat Approximate Comparison Tests

func testApproximateEquality() {
XCTAssert(CGFloat(1.48).isEqual(to: 1.52, threshold: 0.05))
XCTAssert(!CGFloat(1.48).isEqual(to: 1.53, threshold: 0.05))

XCTAssert(CGFloat(1).isEqual(to: 10, threshold: 9))
XCTAssert(!CGFloat(1).isEqual(to: 11, threshold: 9))

XCTAssert(CGFloat(1).isEqual(to: 10, threshold: 9))
XCTAssert(!CGFloat(1).isEqual(to: 11, threshold: 9))

XCTAssert(CGFloat(1.333).isEqual(to: 1.666, threshold: 1 / 3))
XCTAssert(!CGFloat(1.332).isEqual(to: 1.666, threshold: 1 / 3))
XCTAssert(CGFloat(1.48).isEqual(to: 1.52, screenScale: 2))
XCTAssert(!CGFloat(1).isEqual(to: 10, screenScale: 9))
XCTAssert(!CGFloat(1).isEqual(to: 10, screenScale: 9))
XCTAssert(!CGFloat(1).isEqual(to: 9, screenScale: 9))
XCTAssert(!CGFloat(1.333).isEqual(to: 1.666, screenScale: 3))
}

}
Loading

0 comments on commit e19a229

Please sign in to comment.