mirror of
https://github.com/divkit/divkit.git
synced 2026-06-06 20:07:59 +00:00
139 lines
3.8 KiB
Swift
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)
|
|
}
|