mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-08 08:31:13 +00:00
Fix animated stickers
This commit is contained in:
parent
dccb44dfdb
commit
23c76db8f4
@ -81,7 +81,6 @@
|
|||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
enableAddressSanitizer = "YES"
|
enableAddressSanitizer = "YES"
|
||||||
enableASanStackUseAfterReturn = "YES"
|
enableASanStackUseAfterReturn = "YES"
|
||||||
enableUBSanitizer = "YES"
|
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import Accelerate
|
||||||
|
|
||||||
private let deviceColorSpace: CGColorSpace = {
|
private let deviceColorSpace: CGColorSpace = {
|
||||||
if #available(iOSApplicationExtension 9.3, *) {
|
if #available(iOSApplicationExtension 9.3, *) {
|
||||||
@ -13,6 +14,8 @@ private let deviceColorSpace: CGColorSpace = {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private let grayscaleColorSpace = CGColorSpaceCreateDeviceGray()
|
||||||
|
|
||||||
let deviceScale = UIScreen.main.scale
|
let deviceScale = UIScreen.main.scale
|
||||||
|
|
||||||
public func generateImagePixel(_ size: CGSize, scale: CGFloat, pixelGenerator: (CGSize, UnsafeMutablePointer<UInt8>, Int) -> Void) -> UIImage? {
|
public func generateImagePixel(_ size: CGSize, scale: CGFloat, pixelGenerator: (CGSize, UnsafeMutablePointer<UInt8>, Int) -> Void) -> UIImage? {
|
||||||
@ -39,6 +42,74 @@ public func generateImagePixel(_ size: CGSize, scale: CGFloat, pixelGenerator: (
|
|||||||
return UIImage(cgImage: image, scale: scale, orientation: .up)
|
return UIImage(cgImage: image, scale: scale, orientation: .up)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func withImageBytes(image: UIImage, _ f: (UnsafePointer<UInt8>, Int, Int, Int) -> Void) {
|
||||||
|
let selectedScale = image.scale
|
||||||
|
let scaledSize = CGSize(width: image.size.width * selectedScale, height: image.size.height * selectedScale)
|
||||||
|
let bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15)
|
||||||
|
let length = bytesPerRow * Int(scaledSize.height)
|
||||||
|
let bytes = malloc(length)!.assumingMemoryBound(to: UInt8.self)
|
||||||
|
memset(bytes, 0, length)
|
||||||
|
|
||||||
|
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue)
|
||||||
|
|
||||||
|
guard let context = CGContext(data: bytes, width: Int(scaledSize.width), height: Int(scaledSize.height), bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: deviceColorSpace, bitmapInfo: bitmapInfo.rawValue) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
context.scaleBy(x: selectedScale, y: selectedScale)
|
||||||
|
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: image.size))
|
||||||
|
|
||||||
|
f(bytes, Int(scaledSize.width), Int(scaledSize.height), bytesPerRow)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func generateGrayscaleAlphaMaskImage(image: UIImage) -> UIImage? {
|
||||||
|
let selectedScale = image.scale
|
||||||
|
let scaledSize = CGSize(width: image.size.width * selectedScale, height: image.size.height * selectedScale)
|
||||||
|
let bytesPerRow = (1 * Int(scaledSize.width) + 15) & (~15)
|
||||||
|
let length = bytesPerRow * Int(scaledSize.height)
|
||||||
|
let bytes = malloc(length)!.assumingMemoryBound(to: UInt8.self)
|
||||||
|
memset(bytes, 0, length)
|
||||||
|
|
||||||
|
guard let provider = CGDataProvider(dataInfo: bytes, data: bytes, size: length, releaseData: { bytes, _, _ in
|
||||||
|
free(bytes)
|
||||||
|
})
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)
|
||||||
|
|
||||||
|
guard let context = CGContext(data: bytes, width: Int(scaledSize.width), height: Int(scaledSize.height), bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: grayscaleColorSpace, bitmapInfo: bitmapInfo.rawValue) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
context.scaleBy(x: selectedScale, y: selectedScale)
|
||||||
|
|
||||||
|
withImageBytes(image: image, { pixels, width, height, imageBytesPerRow in
|
||||||
|
var src = vImage_Buffer(data: UnsafeMutableRawPointer(mutating: pixels), height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: imageBytesPerRow)
|
||||||
|
|
||||||
|
let permuteMap: [UInt8] = [3, 2, 1, 0]
|
||||||
|
vImagePermuteChannels_ARGB8888(&src, &src, permuteMap, vImage_Flags(kvImageDoNotTile))
|
||||||
|
vImageUnpremultiplyData_ARGB8888(&src, &src, vImage_Flags(kvImageDoNotTile))
|
||||||
|
|
||||||
|
for y in 0 ..< Int(scaledSize.height) {
|
||||||
|
let srcRowBytes = pixels.advanced(by: y * imageBytesPerRow)
|
||||||
|
let dstRowBytes = bytes.advanced(by: y * bytesPerRow)
|
||||||
|
for x in 0 ..< Int(scaledSize.width) {
|
||||||
|
let a = srcRowBytes.advanced(by: x * 4 + 0).pointee
|
||||||
|
dstRowBytes.advanced(by: x).pointee = 0xff &- a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
guard let image = CGImage(width: Int(scaledSize.width), height: Int(scaledSize.height), bitsPerComponent: 8, bitsPerPixel: 8, bytesPerRow: bytesPerRow, space: grayscaleColorSpace, bitmapInfo: bitmapInfo, provider: provider, decode: nil, shouldInterpolate: false, intent: .defaultIntent)
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return UIImage(cgImage: image, scale: selectedScale, orientation: .up)
|
||||||
|
}
|
||||||
|
|
||||||
public func generateImage(_ size: CGSize, contextGenerator: (CGSize, CGContext) -> Void, opaque: Bool = false, scale: CGFloat? = nil) -> UIImage? {
|
public func generateImage(_ size: CGSize, contextGenerator: (CGSize, CGContext) -> Void, opaque: Bool = false, scale: CGFloat? = nil) -> UIImage? {
|
||||||
let selectedScale = scale ?? deviceScale
|
let selectedScale = scale ?? deviceScale
|
||||||
let scaledSize = CGSize(width: size.width * selectedScale, height: size.height * selectedScale)
|
let scaledSize = CGSize(width: size.width * selectedScale, height: size.height * selectedScale)
|
||||||
|
@ -195,6 +195,13 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
|
|||||||
lhs = lhs.advanced(by: 1)
|
lhs = lhs.advanced(by: 1)
|
||||||
rhs = rhs.advanced(by: 1)
|
rhs = rhs.advanced(by: 1)
|
||||||
}
|
}
|
||||||
|
var lhsRest = UnsafeMutableRawPointer(frameBytes).assumingMemoryBound(to: UInt8.self).advanced(by: (decodeBufferLength / 8) * 8)
|
||||||
|
var rhsRest = UnsafeMutableRawPointer(decodeBytes).assumingMemoryBound(to: UInt8.self).advanced(by: (decodeBufferLength / 8) * 8)
|
||||||
|
for _ in (decodeBufferLength / 8) * 8 ..< decodeBufferLength {
|
||||||
|
lhsRest.pointee = rhsRest.pointee ^ lhsRest.pointee
|
||||||
|
lhsRest = lhsRest.advanced(by: 1)
|
||||||
|
rhsRest = rhsRest.advanced(by: 1)
|
||||||
|
}
|
||||||
|
|
||||||
frameData = Data(bytes: frameBytes, count: decodeBufferLength)
|
frameData = Data(bytes: frameBytes, count: decodeBufferLength)
|
||||||
}
|
}
|
||||||
|
@ -99,19 +99,21 @@ func fetchCompressedLottieFirstFrameAJpeg(data: Data, size: CGSize, cacheKey: St
|
|||||||
encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(context.bytesPerRow))
|
encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(context.bytesPerRow))
|
||||||
decodeYUVAToRGBA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(context.bytesPerRow))
|
decodeYUVAToRGBA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(context.bytesPerRow))
|
||||||
|
|
||||||
if let colorImage = context.generateImage() {
|
if let colorSourceImage = context.generateImage(), let alphaImage = generateGrayscaleAlphaMaskImage(image: colorSourceImage) {
|
||||||
|
let colorContext = DrawingContext(size: size, scale: 1.0, clear: false)
|
||||||
|
colorContext.withFlippedContext { c in
|
||||||
|
c.setFillColor(UIColor.black.cgColor)
|
||||||
|
c.fill(CGRect(origin: CGPoint(), size: size))
|
||||||
|
c.draw(colorSourceImage.cgImage!, in: CGRect(origin: CGPoint(), size: size))
|
||||||
|
}
|
||||||
|
guard let colorImage = colorContext.generateImage() else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let colorData = NSMutableData()
|
let colorData = NSMutableData()
|
||||||
let alphaData = NSMutableData()
|
let alphaData = NSMutableData()
|
||||||
|
|
||||||
let alphaImage = generateImage(size, contextGenerator: { size, context in
|
if let colorDestination = CGImageDestinationCreateWithData(colorData as CFMutableData, kUTTypeJPEG, 1, nil), let alphaDestination = CGImageDestinationCreateWithData(alphaData as CFMutableData, kUTTypeJPEG, 1, nil) {
|
||||||
context.setFillColor(UIColor.white.cgColor)
|
|
||||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
|
||||||
context.clip(to: CGRect(origin: CGPoint(), size: size), mask: colorImage.cgImage!)
|
|
||||||
context.setFillColor(UIColor.black.cgColor)
|
|
||||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
|
||||||
}, scale: 1.0)
|
|
||||||
|
|
||||||
if let alphaImage = alphaImage, let colorDestination = CGImageDestinationCreateWithData(colorData as CFMutableData, kUTTypeJPEG, 1, nil), let alphaDestination = CGImageDestinationCreateWithData(alphaData as CFMutableData, kUTTypeJPEG, 1, nil) {
|
|
||||||
CGImageDestinationSetProperties(colorDestination, [:] as CFDictionary)
|
CGImageDestinationSetProperties(colorDestination, [:] as CFDictionary)
|
||||||
CGImageDestinationSetProperties(alphaDestination, [:] as CFDictionary)
|
CGImageDestinationSetProperties(alphaDestination, [:] as CFDictionary)
|
||||||
|
|
||||||
@ -257,8 +259,12 @@ func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize,
|
|||||||
lhs = lhs.advanced(by: 1)
|
lhs = lhs.advanced(by: 1)
|
||||||
rhs = rhs.advanced(by: 1)
|
rhs = rhs.advanced(by: 1)
|
||||||
}
|
}
|
||||||
for i in (yuvaLength / 8) * 8 ..< yuvaLength {
|
var lhsRest = previousYuvaFrameData.assumingMemoryBound(to: UInt8.self).advanced(by: (yuvaLength / 8) * 8)
|
||||||
|
var rhsRest = yuvaFrameData.assumingMemoryBound(to: UInt8.self).advanced(by: (yuvaLength / 8) * 8)
|
||||||
|
for _ in (yuvaLength / 8) * 8 ..< yuvaLength {
|
||||||
|
lhsRest.pointee = rhsRest.pointee ^ lhsRest.pointee
|
||||||
|
lhsRest = lhsRest.advanced(by: 1)
|
||||||
|
rhsRest = rhsRest.advanced(by: 1)
|
||||||
}
|
}
|
||||||
deltaTime += CACurrentMediaTime() - deltaStartTime
|
deltaTime += CACurrentMediaTime() - deltaStartTime
|
||||||
|
|
||||||
|
@ -271,7 +271,7 @@ final class CachedAnimatedStickerRepresentation: CachedMediaResourceRepresentati
|
|||||||
let height: Int32
|
let height: Int32
|
||||||
|
|
||||||
var uniqueId: String {
|
var uniqueId: String {
|
||||||
return "animated-sticker-\(self.width)x\(self.height)-v6"
|
return "animated-sticker-\(self.width)x\(self.height)-v7"
|
||||||
}
|
}
|
||||||
|
|
||||||
init(width: Int32, height: Int32) {
|
init(width: Int32, height: Int32) {
|
||||||
|
@ -551,9 +551,6 @@ public func chatMessageAnimatedSticker(postbox: Postbox, file: TelegramMediaFile
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let img = context.generateImage()
|
|
||||||
let cgImg = img?.cgImage
|
|
||||||
|
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,16 @@ void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height,
|
|||||||
|
|
||||||
error = vImageUnpremultiplyData_ARGB8888(&src, &src, kvImageDoNotTile);
|
error = vImageUnpremultiplyData_ARGB8888(&src, &src, kvImageDoNotTile);
|
||||||
|
|
||||||
uint8_t *buf = (uint8_t *)argb;
|
uint8_t *alpha = yuva + width * height * 2;
|
||||||
uint8_t *alpha = yuva + (width * height * 1 + width * height * 1);
|
int i = 0;
|
||||||
for (int i = 0; i < width * height; i += 2) {
|
for (int y = 0; y < height; y += 1) {
|
||||||
uint8_t a0 = (buf[i * 4 + 0] >> 4) << 4;
|
uint8_t const *argbRow = argb + y * bytesPerRow;
|
||||||
uint8_t a1 = (buf[(i + 1) * 4 + 0] >> 4) << 4;
|
for (int x = 0; x < width; x += 2) {
|
||||||
|
uint8_t a0 = (argbRow[x * 4 + 0] >> 4) << 4;
|
||||||
|
uint8_t a1 = (argbRow[(x + 1) * 4 + 0] >> 4) << 4;
|
||||||
alpha[i / 2] = (a0 & (0xf0U)) | ((a1 & (0xf0U)) >> 4);
|
alpha[i / 2] = (a0 & (0xf0U)) | ((a1 & (0xf0U)) >> 4);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vImage_Buffer destYp;
|
vImage_Buffer destYp;
|
||||||
@ -79,12 +83,17 @@ void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height,
|
|||||||
error = vImageConvert_420Yp8_CbCr8ToARGB8888(&srcYp, &srcCbCr, &dest, &info, NULL, 0xff, kvImageDoNotTile);
|
error = vImageConvert_420Yp8_CbCr8ToARGB8888(&srcYp, &srcCbCr, &dest, &info, NULL, 0xff, kvImageDoNotTile);
|
||||||
|
|
||||||
uint8_t const *alpha = yuva + (width * height * 1 + width * height * 1);
|
uint8_t const *alpha = yuva + (width * height * 1 + width * height * 1);
|
||||||
for (int i = 0; i < width * height; i += 2) {
|
int i = 0;
|
||||||
|
for (int y = 0; y < height; y += 1) {
|
||||||
|
uint8_t *argbRow = argb + y * bytesPerRow;
|
||||||
|
for (int x = 0; x < width; x += 2) {
|
||||||
uint8_t a = alpha[i / 2];
|
uint8_t a = alpha[i / 2];
|
||||||
uint8_t a1 = (a & (0xf0U));
|
uint8_t a1 = (a & (0xf0U));
|
||||||
uint8_t a2 = ((a & (0x0fU)) << 4);
|
uint8_t a2 = ((a & (0x0fU)) << 4);
|
||||||
argb[i * 4 + 0] = a1 | (a1 >> 4);
|
argbRow[x * 4 + 0] = a1 | (a1 >> 4);
|
||||||
argb[(i + 1) * 4 + 0] = a2 | (a2 >> 4);
|
argbRow[(x + 1) * 4 + 0] = a2 | (a2 >> 4);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
error = vImagePremultiplyData_ARGB8888(&dest, &dest, kvImageDoNotTile);
|
error = vImagePremultiplyData_ARGB8888(&dest, &dest, kvImageDoNotTile);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user