Files
divkit/client/ios/Core/CommonCorePublic/ImageLayerLayout.swift
T
2023-03-28 12:17:21 +03:00

139 lines
3.8 KiB
Swift

// Copyright 2018 Yandex LLC. All rights reserved.
import CoreGraphics
public struct ImageLayerLayout {
public let frame: CGRect
public let contentRect: CGRect
public let contentCenter: CGRect
public init(
contentMode: ImageContentMode,
contentSize: CGSize,
boundsSize: CGSize,
capInsets: EdgeInsets
) {
switch contentMode.scale {
case .noScale:
// layout will be done by system due to contentsGravity
frame = CGRect(origin: .zero, size: boundsSize)
contentRect = defaultContentRect
contentCenter = defaultContentCenter
case .aspectFill:
frame = makeAspectFillFrame(
contentMode: contentMode,
contentSize: contentSize,
bounds: boundsSize
)
contentRect = defaultContentRect
contentCenter = makeContentCenter(for: contentSize, capInsets: capInsets)
case .aspectFit:
frame = makeAspectFitFrame(
contentMode: contentMode,
contentSize: contentSize,
bounds: boundsSize
)
contentRect = defaultContentRect
contentCenter = makeContentCenter(for: contentSize, capInsets: capInsets)
case .aspectWidth:
assert(
contentMode.verticalAlignment == .top,
"Aspect width scale supports only top alignment"
)
frame = CGRect(origin: .zero, size: boundsSize)
contentRect = makeTopContentRect(for: contentSize, bounds: boundsSize)
contentCenter = makeContentCenter(for: contentSize, capInsets: capInsets)
case .resize:
frame = CGRect(origin: .zero, size: boundsSize)
contentRect = defaultContentRect
contentCenter = makeContentCenter(for: contentSize, capInsets: capInsets)
}
}
}
private let defaultContentRect = CGRect(x: 0, y: 0, width: 1, height: 1)
private func makeTopContentRect(for contentSize: CGSize, bounds: CGSize) -> CGRect {
guard contentSize.height > 0, contentSize.width > 0, bounds.width > 0 else { return .zero }
let scaleByWidth = bounds.width / contentSize.width
let scaledContentHeight = contentSize.height * scaleByWidth
let normalizedVisibleContentHeight = bounds.height / scaledContentHeight
return CGRect(x: 0, y: 0, width: 1, height: normalizedVisibleContentHeight)
}
private let defaultContentCenter = CGRect(x: 0, y: 0, width: 1, height: 1)
private func makeContentCenter(for contentSize: CGSize, capInsets: EdgeInsets) -> CGRect {
guard contentSize.width > 0, contentSize.height > 0 else {
return CGRect(x: 0, y: 0, width: 1, height: 1)
}
return CGRect(origin: .zero, size: contentSize)
.inset(by: capInsets)
.scaled(x: 1 / contentSize.width, y: 1 / contentSize.height)
}
private func makeAspectFillFrame(
contentMode: ImageContentMode,
contentSize: CGSize,
bounds: CGSize
) -> CGRect {
guard let size = contentSize.sizeToFill(size: bounds) else {
return .zero
}
let origin = makeOrigin(
contentMode: contentMode,
contentSize: size,
bounds: bounds
)
return CGRect(origin: origin, size: size)
}
private func makeAspectFitFrame(
contentMode: ImageContentMode,
contentSize: CGSize,
bounds: CGSize
) -> CGRect {
guard let size = contentSize.sizeToFit(size: bounds) else {
return .zero
}
let origin = makeOrigin(
contentMode: contentMode,
contentSize: size,
bounds: bounds
)
return CGRect(origin: origin, size: size)
}
private func makeOrigin(
contentMode: ImageContentMode,
contentSize: CGSize,
bounds: CGSize
) -> CGPoint {
let x: CGFloat
switch contentMode.horizontalAlignment {
case .left:
x = 0
case .center:
x = (bounds.width - contentSize.width) / 2
case .right:
x = bounds.width - contentSize.width
}
let y: CGFloat
switch contentMode.verticalAlignment {
case .top:
y = 0
case .center:
y = (bounds.height - contentSize.height) / 2
case .bottom:
y = bounds.height - contentSize.height
}
return CGPoint(x: x, y: y)
}