Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Core Animation rendering engine to use device RGB color space #1801

Merged
merged 4 commits into from Nov 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -103,8 +103,8 @@ private class MaskLayer: CALayer {
addSublayer(maskLayer)
anchorPoint = .zero
maskLayer.fillColor = mask.mode == .add
? CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [1, 0, 0, 1])
: CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [0, 1, 0, 1])
? .rgb(1, 0, 0)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consolidated all CGColor.init call sites to the same helper

: .rgb(0, 1, 0)
maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
actions = [
"opacity" : NSNull(),
Expand Down
Expand Up @@ -48,9 +48,7 @@ final class InvertedMatteLayer: CALayer, CompositionLayerDelegate {

override func draw(in ctx: CGContext) {
guard let inputMatte = inputMatte else { return }
guard let fillColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [0, 0, 0, 1])
else { return }
ctx.setFillColor(fillColor)
ctx.setFillColor(.rgb(0, 0, 0))
ctx.fill(bounds)
ctx.setBlendMode(.destinationOut)
inputMatte.render(in: ctx)
Expand Down
Expand Up @@ -48,15 +48,11 @@ private final class GradientFillLayer: CALayer {

var gradientColors = [CGColor]()
var colorLocations = [CGFloat]()
let colorSpace = CGColorSpaceCreateDeviceRGB()
let maskColorSpace = CGColorSpaceCreateDeviceGray()
for i in 0..<numberOfColors {
let ix = i * 4
if
colors.count > ix, let color = CGColor(
colorSpace: colorSpace,
components: [colors[ix + 1], colors[ix + 2], colors[ix + 3], 1])
{
if colors.count > ix {
let color = CGColor.rgb(colors[ix + 1], colors[ix + 2], colors[ix + 3])
gradientColors.append(color)
colorLocations.append(colors[ix])
}
Expand All @@ -68,10 +64,8 @@ private final class GradientFillLayer: CALayer {
if alpha < 1 {
drawMask = true
}
if let color = CGColor(colorSpace: maskColorSpace, components: [alpha, 1]) {
alphaLocations.append(colors[i])
alphaColors.append(color)
}
alphaLocations.append(colors[i])
alphaColors.append(.gray(alpha))
}

/// First draw a mask is necessary.
Expand Down Expand Up @@ -114,8 +108,13 @@ private final class GradientFillLayer: CALayer {
}

/// Now draw the gradient
guard let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors as CFArray, locations: colorLocations)
guard
let gradient = CGGradient(
colorsSpace: CGColorSpaceCreateDeviceRGB(),
colors: gradientColors as CFArray,
locations: colorLocations)
else { return }

if type == .linear {
ctx.drawLinearGradient(gradient, start: start, end: end, options: [.drawsAfterEndLocation, .drawsBeforeStartLocation])
} else {
Expand All @@ -141,7 +140,7 @@ final class GradientFillRenderer: PassThroughOutputNode, Renderable {
override init(parent: NodeOutput?) {
super.init(parent: parent)

maskLayer.fillColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [1, 1, 1, 1])
maskLayer.fillColor = .rgb(1, 1, 1)
gradientLayer.mask = maskLayer

maskLayer.actions = [
Expand Down
Expand Up @@ -17,7 +17,7 @@ final class GradientStrokeRenderer: PassThroughOutputNode, Renderable {
override init(parent: NodeOutput?) {
strokeRender = StrokeRenderer(parent: nil)
gradientRender = LegacyGradientFillRenderer(parent: nil)
strokeRender.color = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [1, 1, 1, 1])
strokeRender.color = .rgb(1, 1, 1)
super.init(parent: parent)
}

Expand Down
Expand Up @@ -67,15 +67,11 @@ final class LegacyGradientFillRenderer: PassThroughOutputNode, Renderable {

var gradientColors = [CGColor]()
var colorLocations = [CGFloat]()
let colorSpace = CGColorSpaceCreateDeviceRGB()
let maskColorSpace = CGColorSpaceCreateDeviceGray()
for i in 0..<numberOfColors {
let ix = i * 4
if
colors.count > ix, let color = CGColor(
colorSpace: colorSpace,
components: [colors[ix + 1], colors[ix + 2], colors[ix + 3], 1])
{
if colors.count > ix {
let color = CGColor.rgb(colors[ix + 1], colors[ix + 2], colors[ix + 3])
gradientColors.append(color)
colorLocations.append(colors[ix])
}
Expand All @@ -87,10 +83,8 @@ final class LegacyGradientFillRenderer: PassThroughOutputNode, Renderable {
if alpha < 1 {
drawMask = true
}
if let color = CGColor(colorSpace: maskColorSpace, components: [alpha, 1]) {
alphaLocations.append(colors[i])
alphaColors.append(color)
}
alphaLocations.append(colors[i])
alphaColors.append(.gray(alpha))
}

inContext.setAlpha(opacity)
Expand Down Expand Up @@ -136,8 +130,13 @@ final class LegacyGradientFillRenderer: PassThroughOutputNode, Renderable {
}

/// Now draw the gradient
guard let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors as CFArray, locations: colorLocations)
guard
let gradient = CGGradient(
colorsSpace: CGColorSpaceCreateDeviceRGB(),
colors: gradientColors as CFArray,
locations: colorLocations)
else { return }

if type == .linear {
inContext.drawLinearGradient(gradient, start: start, end: end, options: [.drawsAfterEndLocation, .drawsBeforeStartLocation])
} else {
Expand Down
26 changes: 10 additions & 16 deletions Sources/Private/Utility/Debugging/LayerDebugging.swift
Expand Up @@ -150,10 +150,8 @@ extension ShapeRenderLayer: LayerDebugging {

extension LayerDebugStyle {
static func defaultStyle() -> LayerDebugStyle {
let colorSpace = CGColorSpaceCreateDeviceRGB()

let anchorColor = CGColor(colorSpace: colorSpace, components: [1, 0, 0, 1])!
let boundsColor = CGColor(colorSpace: colorSpace, components: [1, 1, 0, 1])!
let anchorColor = CGColor.rgb(1, 0, 0)
let boundsColor = CGColor.rgb(1, 1, 0)
return LayerDebugStyle(
anchorColor: anchorColor,
boundsColor: boundsColor,
Expand All @@ -162,9 +160,8 @@ extension LayerDebugStyle {
}

static func topLayerStyle() -> LayerDebugStyle {
let colorSpace = CGColorSpaceCreateDeviceRGB()
let anchorColor = CGColor(colorSpace: colorSpace, components: [1, 0.5, 0, 0])!
let boundsColor = CGColor(colorSpace: colorSpace, components: [0, 1, 0, 1])!
let anchorColor = CGColor.rgba(1, 0.5, 0, 0)
let boundsColor = CGColor.rgb(0, 1, 0)

return LayerDebugStyle(
anchorColor: anchorColor,
Expand All @@ -174,9 +171,8 @@ extension LayerDebugStyle {
}

static func nullLayerStyle() -> LayerDebugStyle {
let colorSpace = CGColorSpaceCreateDeviceRGB()
let anchorColor = CGColor(colorSpace: colorSpace, components: [0, 0, 1, 0])!
let boundsColor = CGColor(colorSpace: colorSpace, components: [0, 1, 0, 1])!
let anchorColor = CGColor.rgba(0, 0, 1, 0)
let boundsColor = CGColor.rgb(0, 1, 0)

return LayerDebugStyle(
anchorColor: anchorColor,
Expand All @@ -186,9 +182,8 @@ extension LayerDebugStyle {
}

static func shapeLayerStyle() -> LayerDebugStyle {
let colorSpace = CGColorSpaceCreateDeviceRGB()
let anchorColor = CGColor(colorSpace: colorSpace, components: [0, 1, 0, 0])!
let boundsColor = CGColor(colorSpace: colorSpace, components: [0, 1, 0, 1])!
let anchorColor = CGColor.rgba(0, 1, 0, 0)
let boundsColor = CGColor.rgb(0, 1, 0)

return LayerDebugStyle(
anchorColor: anchorColor,
Expand All @@ -198,9 +193,8 @@ extension LayerDebugStyle {
}

static func shapeRenderLayerStyle() -> LayerDebugStyle {
let colorSpace = CGColorSpaceCreateDeviceRGB()
let anchorColor = CGColor(colorSpace: colorSpace, components: [0, 1, 1, 0])!
let boundsColor = CGColor(colorSpace: colorSpace, components: [0, 1, 0, 1])!
let anchorColor = CGColor.rgba(0, 1, 1, 0)
let boundsColor = CGColor.rgb(0, 1, 0)

return LayerDebugStyle(
anchorColor: anchorColor,
Expand Down
19 changes: 12 additions & 7 deletions Sources/Private/Utility/Extensions/CGColor+RGB.swift
Expand Up @@ -6,13 +6,18 @@ import QuartzCore
extension CGColor {
/// Initializes a `CGColor` using the given `RGB` values
static func rgb(_ red: CGFloat, _ green: CGFloat, _ blue: CGFloat) -> CGColor {
if #available(iOS 13.0, tvOS 13.0, macOS 10.5, *) {
return CGColor(red: red, green: green, blue: blue, alpha: 1)
} else {
return CGColor(
colorSpace: CGColorSpaceCreateDeviceRGB(),
components: [red, green, blue])!
}
CGColor(
colorSpace: CGColorSpaceCreateDeviceRGB(),
components: [red, green, blue])!
.copy(alpha: 1)!
}

/// Initializes a `CGColor` using the given grayscale value
static func gray(_ gray: CGFloat) -> CGColor {
CGColor(
colorSpace: CGColorSpaceCreateDeviceGray(),
components: [gray])!
.copy(alpha: 1)!
}

/// Initializes a `CGColor` using the given `RGBA` values
Expand Down
7 changes: 2 additions & 5 deletions Sources/Private/Utility/Primitives/ColorExtension.swift
Expand Up @@ -95,14 +95,11 @@ extension LottieColor: AnyInitializable {
}

extension LottieColor {

static var clearColor: CGColor {
CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [0, 0, 0, 0])!
.rgba(0, 0, 0, 0)
}

var cgColorValue: CGColor {
// TODO: Fix color spaces
let colorspace = CGColorSpaceCreateDeviceRGB()
return CGColor(colorSpace: colorspace, components: [CGFloat(r), CGFloat(g), CGFloat(b), CGFloat(a)]) ?? LottieColor.clearColor
.rgba(CGFloat(r), CGFloat(g), CGFloat(b), CGFloat(a))
}
}
1 change: 1 addition & 0 deletions Tests/Samples/Issues/issue_1800.json

Large diffs are not rendered by default.

@@ -0,0 +1 @@
Supports Core Animation engine
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.