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"
|
||||
enableAddressSanitizer = "YES"
|
||||
enableASanStackUseAfterReturn = "YES"
|
||||
enableUBSanitizer = "YES"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Accelerate
|
||||
|
||||
private let deviceColorSpace: CGColorSpace = {
|
||||
if #available(iOSApplicationExtension 9.3, *) {
|
||||
@ -13,6 +14,8 @@ private let deviceColorSpace: CGColorSpace = {
|
||||
}
|
||||
}()
|
||||
|
||||
private let grayscaleColorSpace = CGColorSpaceCreateDeviceGray()
|
||||
|
||||
let deviceScale = UIScreen.main.scale
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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? {
|
||||
let selectedScale = scale ?? deviceScale
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
@ -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))
|
||||
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 alphaData = NSMutableData()
|
||||
|
||||
let alphaImage = generateImage(size, contextGenerator: { size, context in
|
||||
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) {
|
||||
if let colorDestination = CGImageDestinationCreateWithData(colorData as CFMutableData, kUTTypeJPEG, 1, nil), let alphaDestination = CGImageDestinationCreateWithData(alphaData as CFMutableData, kUTTypeJPEG, 1, nil) {
|
||||
CGImageDestinationSetProperties(colorDestination, [:] as CFDictionary)
|
||||
CGImageDestinationSetProperties(alphaDestination, [:] as CFDictionary)
|
||||
|
||||
@ -257,8 +259,12 @@ func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize,
|
||||
lhs = lhs.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
|
||||
|
||||
|
@ -271,7 +271,7 @@ final class CachedAnimatedStickerRepresentation: CachedMediaResourceRepresentati
|
||||
let height: Int32
|
||||
|
||||
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) {
|
||||
|
@ -551,9 +551,6 @@ public func chatMessageAnimatedSticker(postbox: Postbox, file: TelegramMediaFile
|
||||
}
|
||||
}
|
||||
|
||||
let img = context.generateImage()
|
||||
let cgImg = img?.cgImage
|
||||
|
||||
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);
|
||||
|
||||
uint8_t *buf = (uint8_t *)argb;
|
||||
uint8_t *alpha = yuva + (width * height * 1 + width * height * 1);
|
||||
for (int i = 0; i < width * height; i += 2) {
|
||||
uint8_t a0 = (buf[i * 4 + 0] >> 4) << 4;
|
||||
uint8_t a1 = (buf[(i + 1) * 4 + 0] >> 4) << 4;
|
||||
alpha[i / 2] = (a0 & (0xf0U)) | ((a1 & (0xf0U)) >> 4);
|
||||
uint8_t *alpha = yuva + width * height * 2;
|
||||
int i = 0;
|
||||
for (int y = 0; y < height; y += 1) {
|
||||
uint8_t const *argbRow = argb + y * bytesPerRow;
|
||||
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);
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
uint8_t const *alpha = yuva + (width * height * 1 + width * height * 1);
|
||||
for (int i = 0; i < width * height; i += 2) {
|
||||
uint8_t a = alpha[i / 2];
|
||||
uint8_t a1 = (a & (0xf0U));
|
||||
uint8_t a2 = ((a & (0x0fU)) << 4);
|
||||
argb[i * 4 + 0] = a1 | (a1 >> 4);
|
||||
argb[(i + 1) * 4 + 0] = a2 | (a2 >> 4);
|
||||
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 a1 = (a & (0xf0U));
|
||||
uint8_t a2 = ((a & (0x0fU)) << 4);
|
||||
argbRow[x * 4 + 0] = a1 | (a1 >> 4);
|
||||
argbRow[(x + 1) * 4 + 0] = a2 | (a2 >> 4);
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
error = vImagePremultiplyData_ARGB8888(&dest, &dest, kvImageDoNotTile);
|
||||
|
Loading…
x
Reference in New Issue
Block a user