Skip to content

Commit

Permalink
Merge branch 'master' into scroll-indicators-update
Browse files Browse the repository at this point in the history
  • Loading branch information
rikner committed Mar 4, 2019
2 parents 75553c8 + 5aacce1 commit 147831f
Show file tree
Hide file tree
Showing 65 changed files with 1,081 additions and 563 deletions.
6 changes: 3 additions & 3 deletions Sources/AVPlayer+Android.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,18 @@ public class AVPlayer: JNIObject {

private weak var globalAVPlayer: AVPlayer?

@_silgen_name("Java_org_uikit_AVPlayer_nativeOnVideoReady")
@_cdecl("Java_org_uikit_AVPlayer_nativeOnVideoReady")
public func nativeOnVideoReady(env: UnsafeMutablePointer<JNIEnv>, cls: JavaObject) {
globalAVPlayer?.onLoaded?(nil)
globalAVPlayer?.onLoaded = nil
}

@_silgen_name("Java_org_uikit_AVPlayer_nativeOnVideoEnded")
@_cdecl("Java_org_uikit_AVPlayer_nativeOnVideoEnded")
public func nativeOnVideoEnded(env: UnsafeMutablePointer<JNIEnv>, cls: JavaObject) {
globalAVPlayer?.onVideoEnded?()
}

@_silgen_name("Java_org_uikit_AVPlayer_nativeOnVideoSourceError")
@_cdecl("Java_org_uikit_AVPlayer_nativeOnVideoSourceError")
public func nativeOnVideoSourceError(env: UnsafeMutablePointer<JNIEnv>, cls: JavaObject) {
globalAVPlayer?.onLoaded?(AVPlayer.DataSourceError())
globalAVPlayer?.onLoaded = nil
Expand Down
8 changes: 4 additions & 4 deletions Sources/AVPlayerLayer+Android.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ public class AVPlayerLayer: JNIObject {
set {
let scaledFrame = (newValue * UIScreen.main.scale)
try! call(methodName: "setFrame", arguments: [
Int(round(scaledFrame.origin.x)),
Int(round(scaledFrame.origin.y)),
Int(round(scaledFrame.size.width)),
Int(round(scaledFrame.size.height))
JavaInt(round(scaledFrame.origin.x)),
JavaInt(round(scaledFrame.origin.y)),
JavaInt(round(scaledFrame.size.width)),
JavaInt(round(scaledFrame.size.height))
])
}
}
Expand Down
8 changes: 4 additions & 4 deletions Sources/AVPlayerLayer+Mac.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ public final class AVPlayerLayer: CALayer {

private func updateContentsGravityFromVideoGravity() {
switch videoGravity {
case .resize: contentsGravityEnum = .resize
case .resizeAspect: contentsGravityEnum = .resizeAspectFit
case .resizeAspectFill: contentsGravityEnum = .resizeAspectFill
case .resize: contentsGravity = .resize
case .resizeAspect: contentsGravity = .resizeAspect
case .resizeAspectFill: contentsGravity = .resizeAspectFill
}
}

Expand All @@ -47,7 +47,7 @@ public final class AVPlayerLayer: CALayer {

private var playerOutput = AVPlayerItemVideoOutput()

open override var frame: CGRect {
public override var frame: CGRect {
didSet {
if frame.size == .zero { return }
updatePlayerOutput(size: frame.size)
Expand Down
2 changes: 1 addition & 1 deletion Sources/Button.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ open class Button: UIControl {
didSet { self.setNeedsLayout() }
}

public override var tintColor: UIColor! {
open override var tintColor: UIColor! {
didSet {
if tintColor != nil { setTitleColor(tintColor, for: .normal) }
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/CABasicAnimationPrototype.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class CABasicAnimationPrototype {
}

extension CAMediaTimingFunction {
static func timingFunction(from options: UIViewAnimationOptions) -> CAMediaTimingFunction? {
static func timingFunction(from options: UIView.AnimationOptions) -> CAMediaTimingFunction? {
if options.contains(.curveEaseIn) {
return .init(name: kCAMediaTimingFunctionEaseIn)
} else if options.contains(.curveEaseOut) {
Expand Down
35 changes: 19 additions & 16 deletions Sources/CALayer+ContentsGravity.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
//
// CALayer+ContentsGravity.swift
// UIKit
//
// Created by Geordie Jay on 18.02.18.
// Copyright © 2018 flowkey. All rights reserved.
//


extension CALayer {
internal enum ContentsGravity: String {
case left, center, right, top, bottom, topLeft, topRight
case resize, resizeAspectFill, resizeAspectFit = "resizeAspect"
}
/*
* Apples CALayerContentsGravity implementation is based on a struct
* with a raw representable because of backwards compatibility
* We implemented it with an enum, which can be used the
* same way as Apples CALayerContentsGravity
*/
public enum CALayerContentsGravity: String {
case bottom, bottomLeft, bottomRight
case center, left, right
case top, topLeft, topRight
case resize, resizeAspect, resizeAspectFill
}

struct ContentsGravityTransformation {
Expand Down Expand Up @@ -42,15 +39,15 @@ struct ContentsGravityTransformation {
return (bounds.height - scaledContents.height) * (1 - layer.anchorPoint.y)
}

switch layer.contentsGravityEnum {
switch layer.contentsGravity {
case .resize:
offset = .zero
scale = CGSize(width: bounds.width / scaledContents.width, height: bounds.height / scaledContents.height)
case .resizeAspectFill:
offset = .zero
let maxScale = max(bounds.width / scaledContents.width, bounds.height / scaledContents.height)
scale = CGSize(width: maxScale, height: maxScale)
case .resizeAspectFit:
case .resizeAspect:
offset = .zero
let minScale = min(bounds.width / scaledContents.width, bounds.height / scaledContents.height)
scale = CGSize(width: minScale, height: minScale)
Expand All @@ -75,6 +72,12 @@ struct ContentsGravityTransformation {
case .topRight:
offset = CGPoint(x: distanceToMaxX, y: distanceToMinY)
scale = .defaultScale
case .bottomLeft:
offset = CGPoint(x: distanceToMinX, y: distanceToMaxY)
scale = .defaultScale
case .bottomRight:
offset = CGPoint(x: distanceToMaxX, y: distanceToMaxY)
scale = .defaultScale
}
}
}
Expand Down
27 changes: 18 additions & 9 deletions Sources/CALayer+SDL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import SDL_gpu

extension CALayer {
final func sdlRender(parentAbsoluteOpacity: Float = 1) {
guard let renderer = UIApplication.shared?.glRenderer else { return }
guard let renderer = UIScreen.main else { return }
let opacity = self.opacity * parentAbsoluteOpacity
if isHidden || opacity < 0.01 { return }

Expand Down Expand Up @@ -103,13 +103,22 @@ extension CALayer {
}

if let contents = contents {
renderer.blit(
contents,
anchorPoint: anchorPoint,
contentsScale: contentsScale,
contentsGravity: ContentsGravityTransformation(for: self),
opacity: opacity
)
do {
try renderer.blit(
contents,
anchorPoint: anchorPoint,
contentsScale: contentsScale,
contentsGravity: ContentsGravityTransformation(for: self),
opacity: opacity
)
} catch {
// Try to recreate contents from source data if it exists
if contents.reloadFromSourceData() == false {
// That failed, rely on the layer to re-render itself:
self.contents = nil
self.setNeedsDisplay()
}
}
}

if mask != nil {
Expand All @@ -125,7 +134,7 @@ extension CALayer {
let translationFromAnchorPointToOrigin = CATransform3DMakeTranslation(
deltaFromAnchorPointToOrigin.x - bounds.origin.x,
deltaFromAnchorPointToOrigin.y - bounds.origin.y,
0 // If we moved (e.g.) forward to render `self`, all sublayers should start at the same zIndex
0 // If we moved (e.g.) forward to render `self`, all sublayers should start at that same zIndex
)

// This transform is referred to as the `parentOriginTransform` in our sublayers (see above):
Expand Down
20 changes: 12 additions & 8 deletions Sources/CALayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@ open class CALayer {
/// the view sets this value to match the screen.
open var contentsScale: CGFloat = 1.0

internal var contentsGravityEnum: ContentsGravity = .resize
open var contentsGravity: String {
get { return contentsGravityEnum.rawValue }
set { contentsGravityEnum = ContentsGravity(rawValue: newValue) ?? .center } // matches iOS
}
open var contentsGravity: CALayerContentsGravity = .resize

internal (set) public weak var superlayer: CALayer?
internal (set) public var sublayers: [CALayer]? {
Expand Down Expand Up @@ -216,7 +212,7 @@ open class CALayer {
contentsScale = layer.contentsScale
superlayer = layer.superlayer
sublayers = layer.sublayers
contentsGravityEnum = layer.contentsGravityEnum
contentsGravity = layer.contentsGravity
}

open func copy() -> Any {
Expand All @@ -241,16 +237,23 @@ open class CALayer {
internal var _presentation: CALayer?
open func presentation() -> CALayer? { return _presentation }

var disableAnimations = false
internal var disableAnimations = false

var animations = [String: CABasicAnimation]() {
internal var animations = [String: CABasicAnimation]() {
didSet { onDidSetAnimations(wasEmpty: oldValue.isEmpty) }
}

/// We disable animation on parameters of views / layers that haven't been rendered yet.
/// This is both a performance optimization (avoids lots of animations at the start)
/// as well as a correctness fix (matches iOS behaviour). Maybe there's a better way though?
internal var hasBeenRenderedInThisPartOfOverallLayerHierarchy = false

internal var _needsDisplay = true
open func needsDisplay() -> Bool { return _needsDisplay }
open func setNeedsDisplay() { _needsDisplay = true }
open func display() {
delegate?.display(self)
}
}

private extension CGPoint {
Expand Down Expand Up @@ -299,4 +302,5 @@ extension CALayer: Hashable {

public protocol CALayerDelegate: class {
func action(forKey event: String) -> CABasicAnimation?
func display(_ layer: CALayer)
}
70 changes: 65 additions & 5 deletions Sources/CGImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,33 @@ import SDL_gpu
import Foundation

public class CGImage {
let rawPointer: UnsafeMutablePointer<GPU_Image>
/// Be careful using this pointer e.g. for another CGImage instance.
/// You will have to manually adjust its pointee's reference count.
var rawPointer: UnsafeMutablePointer<GPU_Image> {
didSet { CALayer.layerTreeIsDirty = true }
}

/// Stores the compressed image `Data` this `CGImage` was inited with (if any).
/// This allows us to recreate the image if our OpenGL Context gets killed (esp. relevant for Android)
private let sourceData: Data?

public let width: Int
public let height: Int

/**
Initialize a `CGImage` by passing a reference to a `GPU_Image`, which is usually the result of SDL_gpu's `GPU_*Image*` creation functions. May be null.
The second parameter provides the compressed image source data (in PNG, JPG etc. format). The source data will be used to recreate the `GPU_Image` if the GLContext has been invalidated (which happens quite commonly on Android). Not providing source data means you will have to recreate the `CGImage` yourself if it fails to render, usually by overriding your `CALayer`'s display() method.
*/
internal init?(_ pointer: UnsafeMutablePointer<GPU_Image>?) {
internal init?(_ pointer: UnsafeMutablePointer<GPU_Image>?, sourceData: Data?) {
guard let pointer = pointer else {
// We check for GPU errors on render, so clear any error that may have caused GPU_Image to be nil.
// It's possible there are unrelated errors on the stack at this point, but we immediately catch and
// handle any errors that interest us *when they occur*, so it's fine to clear unrelated ones here.
UIApplication.shared?.glRenderer.clearErrors()
UIScreen.main?.clearErrors()
return nil
}

self.sourceData = sourceData
rawPointer = pointer

GPU_SetSnapMode(rawPointer, GPU_SNAP_POSITION_AND_DIMENSIONS)
Expand All @@ -38,16 +48,66 @@ public class CGImage {
height = Int(rawPointer.pointee.h)
}

internal convenience init?(_ sourceData: Data) {
var data = sourceData
let dataCount = Int32(data.count)

guard let gpuImagePtr = data.withUnsafeMutableBytes({ (ptr: UnsafeMutablePointer<Int8>) -> UnsafeMutablePointer<GPU_Image>? in
let rw = SDL_RWFromMem(ptr, dataCount)
return GPU_LoadImage_RW(rw, true)
}) else { return nil }

self.init(gpuImagePtr, sourceData: data)
}

convenience init?(surface: UnsafeMutablePointer<SDLSurface>) {
self.init(GPU_CopyImageFromSurface(surface))
guard let pointer = GPU_CopyImageFromSurface(surface) else { return nil }
self.init(pointer, sourceData: nil)
}

func replacePixels(with bytes: UnsafePointer<UInt8>, bytesPerPixel: Int) {
internal func replacePixels(with bytes: UnsafePointer<UInt8>, bytesPerPixel: Int) {
var rect = GPU_Rect(x: 0, y: 0, w: Float(rawPointer.pointee.w), h: Float(rawPointer.pointee.h))
GPU_UpdateImageBytes(rawPointer, &rect, bytes, Int32(rawPointer.pointee.w) * Int32(bytesPerPixel))
}

/// Recreate the underlying `GPU_Image` (`self.rawPointer`) from this `CGImage`'s source data if possible.
/// - Returns: `true`, if it was possible to recreate the image. Or `false`, if there was no underlying source data, or when SDL_gpu could not decode that data.
internal func reloadFromSourceData() -> Bool {
guard let sourceData = sourceData, let newImage = CGImage(sourceData) else {
return false
}

// Free the old GPU_Image before replacing it (this may be our last chance)
GPU_FreeImage(rawPointer)

// If we don't increase the new image's refcount it will be deinited along
// with the CGImageRef that goes out of scope at the end of this function.
newImage.rawPointer.pointee.refcount += 1

self.rawPointer = newImage.rawPointer

return true
}

deinit {
GPU_FreeImage(rawPointer)
}

public func pngData() -> Data? {
guard let surface = GPU_CopySurfaceFromImage(rawPointer) else { return nil }
defer { SDL_FreeSurface(surface) }

let pngWritingFunc: @convention(c) (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?, Int32) -> Void = { (outData, pngData, dataSize) in
guard let pngData = pngData, dataSize > 0 else { return }
outData?.assumingMemoryBound(to: Data.self)
.pointee
.append(pngData.assumingMemoryBound(to: UInt8.self), count: Int(dataSize))
}

return withoutActuallyEscaping(pngWritingFunc) { (closure) -> Data? in
var data = Data()
stbi_write_png_to_func(closure, &data, surface.pointee.w, surface.pointee.h, Int32(surface.pointee.format.pointee.BytesPerPixel), surface.pointee.pixels, surface.pointee.pitch)
return data.count > 0 ? data : nil
}
}
}
38 changes: 38 additions & 0 deletions Sources/Data+fromRelativePathCrossPlatform.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// Data+fromRelativePathCrossPlatform.swift
// UIKit
//
// Created by Geordie Jay on 01.11.18.
// Copyright © 2018 flowkey. All rights reserved.
//

import Foundation

extension Data {
static func fromPathCrossPlatform(_ path: String) -> Data? {
#if !os(Android)
// At time of writing the SDL code below worked on all supported SDL platforms.
// But, because of some crashes in `Data` we're doing an unneccessary copy there
// which makes that version less efficient than Foundation's, so use it here instead:
return try? Data(contentsOf: URL(fileURLWithPath: path))
#else
guard let fileReader = SDL_RWFromFile(path, "r") else {
return nil
}

defer { _ = fileReader.pointee.close(fileReader) }

let fileSize = Int(fileReader.pointee.size(fileReader))

let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: fileSize)
defer { buffer.deallocate() }

let bytesRead = fileReader.pointee.read(fileReader, buffer, 1, fileSize)
if bytesRead == fileSize {
return Data(bytes: buffer, count: fileSize)
} else {
return nil
}
#endif
}
}
2 changes: 1 addition & 1 deletion Sources/FontRenderer+renderAttributedString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ extension FontRenderer {

var xOffset: Int32 = 0

// Adding bound checking to avoid all kinds of memory corruption errors
// Bounds checking to avoid memory corruption errors
let surfaceEndIndex = surface.pointee.pixels.assumingMemoryBound(to: UInt32.self)
+ Int(surface.pointee.pitch / 4 * surface.pointee.h)

Expand Down
Loading

0 comments on commit 147831f

Please sign in to comment.