保存animationGif时,iOS颜色不正确

我有这个很奇怪的问题。 我从UIImages创buildGIFanimation,大部分时间都是正确的。 但是,当我开始进入更大的图像,我的颜色开始消失。 例如,如果我做一个4帧32×32像素的图像不超过10个颜色没有问题。 如果我把相同的图像缩放到832 x 832,我会失去一个粉红色,棕色变成绿色。

@ 1x 32 x 32

在这里输入图像说明

@ 10x 320 x 320

在这里输入图像说明

@ 26x 832 x 832

在这里输入图像说明

这里是我用来创buildgif的代码…

var kFrameCount = 0 for smdLayer in drawingToUse!.layers{ if !smdLayer.hidden { kFrameCount += 1 } } let loopingProperty = [String(kCGImagePropertyGIFLoopCount): 0] let fileProperties: [String: AnyObject] = [String(kCGImagePropertyGIFDictionary): loopingProperty as AnyObject]; let frameProperty = [String(kCGImagePropertyGIFDelayTime): Float(speedLabel.text!)!] let frameProperties: [String: AnyObject] = [String(kCGImagePropertyGIFDictionary): frameProperty as AnyObject]; let documentsDirectoryPath = "file://\(NSTemporaryDirectory())" if let documentsDirectoryURL = URL(string: documentsDirectoryPath){ let fileURL = documentsDirectoryURL.appendingPathComponent("\(drawing.name)\(getScaleString()).gif") let destination = CGImageDestinationCreateWithURL(fileURL as CFURL, kUTTypeGIF, kFrameCount, nil)! CGImageDestinationSetProperties(destination, fileProperties as CFDictionary); for smdLayer in drawingToUse!.layers{ if !smdLayer.hidden{ let image = UIImage(smdLayer: smdLayer, alphaBlend: useAlphaLayers, backgroundColor: backgroundColorButton.backgroundColor!, scale: scale) CGImageDestinationAddImage(destination, image.cgImage!, frameProperties as CFDictionary) } } if (!CGImageDestinationFinalize(destination)) { print("failed to finalize image destination") } } 

在调用CGImageDestinationAddImage(destination, image.cgImage!, frameProperties as CFDictionary)之前,我已经放入了一个断点,图像与正确的颜色完美无缺。 我希望有人知道我错过了什么。

更新

这是一个示例项目。 请注意,虽然它在预览中没有animation,但它保存了一个GIFanimation,并在控制台中注销了图像的位置。

https://www.dropbox.com/s/pb52awaj8w3amyz/gifTest.zip?dl=0

看起来,closures全球色彩图解决了这个问题:

 let loopingProperty: [String: AnyObject] = [ kCGImagePropertyGIFLoopCount as String: 0 as NSNumber, kCGImagePropertyGIFHasGlobalColorMap as String: false as NSNumber ] 

请注意,与PNG不同,GIF只能使用256色的地图,而没有透明度。 对于animationGIF,可以是全局或每帧的颜色映射。

不幸的是,Core Graphics不允许我们直接使用颜色映射,因此GIF编码时会自动进行颜色转换。

看起来,closures全球色彩地图是所有需要的。 同样使用kCGImagePropertyGIFImageColorMap为每个帧显式设置颜色映射也可能工作。

由于这看起来不太可靠,所以让我们为每一帧创build自己的色彩映射:

 struct Color : Hashable { let red: UInt8 let green: UInt8 let blue: UInt8 var hashValue: Int { return Int(red) + Int(green) + Int(blue) } public static func ==(lhs: Color, rhs: Color) -> Bool { return [lhs.red, lhs.green, lhs.blue] == [rhs.red, rhs.green, rhs.blue] } } struct ColorMap { var colors = Set<Color>() var exported: Data { let data = Array(colors) .map { [$0.red, $0.green, $0.blue] } .joined() return Data(bytes: Array(data)) } } 

现在让我们更新我们的方法:

 func getScaledImages(_ scale: Int) -> [(CGImage, ColorMap)] { var sourceImages = [UIImage]() var result: [(CGImage, ColorMap)] = [] ... var colorMap = ColorMap() let pixelData = imageRef.dataProvider!.data let rawData: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData) for y in 0 ..< imageRef.height{ for _ in 0 ..< scale { for x in 0 ..< imageRef.width{ let offset = y * imageRef.width * 4 + x * 4 let color = Color(red: rawData[offset], green: rawData[offset + 1], blue: rawData[offset + 2]) colorMap.colors.insert(color) for _ in 0 ..< scale { pixelPointer[byteIndex] = rawData[offset] pixelPointer[byteIndex+1] = rawData[offset+1] pixelPointer[byteIndex+2] = rawData[offset+2] pixelPointer[byteIndex+3] = rawData[offset+3] byteIndex += 4 } } } } let cgImage = context.makeImage()! result.append((cgImage, colorMap)) 

 func createAnimatedGifFromImages(_ images: [(CGImage, ColorMap)]) -> URL { ... for (image, colorMap) in images { let frameProperties: [String: AnyObject] = [ String(kCGImagePropertyGIFDelayTime): 0.2 as NSNumber, String(kCGImagePropertyGIFImageColorMap): colorMap.exported as NSData ] let properties: [String: AnyObject] = [ String(kCGImagePropertyGIFDictionary): frameProperties as AnyObject ]; CGImageDestinationAddImage(destination, image, properties as CFDictionary); } 

当然,这只有在颜色数量less于256的情况下才有效。我真的推荐一个自定义的GIF库,可以正确处理颜色转换。

以下是关于量化失败的更多背景信息。 如果通过imagemagick运行GIF输出以提取具有全局颜色映射的版本的颜色调色板与每帧颜色映射,则可以深入了解问题的根源:

具有GLOBAL颜色映射的版本: $ convert test.gif -format %c -depth 8 histogram:info:- 28392: ( 0, 0, 0,255) #000000FF black 240656: ( 71,162, 58,255) #47A23AFF srgba(71,162,58,1) 422500: (147,221,253,255) #93DDFDFF srgba(147,221,253,1) 676: (255,255,255,255) #FFFFFFFF white 2704: ( 71,162, 58,255) #47A23AFF srgba(71,162,58,1) 676: (147,221,253,255) #93DDFDFF srgba(147,221,253,1) 2704: ( 71,162, 58,255) #47A23AFF srgba(71,162,58,1) 676: (147,221,253,255) #93DDFDFF srgba(147,221,253,1) 2704: ( 71,162, 58,255) #47A23AFF srgba(71,162,58,1) 676: (147,221,253,255) #93DDFDFF srgba(147,221,253,1)

带有每帧颜色映射的版本: $ convert test.gif -format %c -depth 8 histogram:info:- 28392: ( 0, 0, 0,255) #000000FF black 237952: ( 71,163, 59,255) #47A33BFF srgba(71,163,59,1) 2704: (113, 78, 0,255) #714E00FF srgba(113,78,0,1) 421824: (147,221,253,255) #93DDFDFF srgba(147,221,253,1) 676: (246, 81,249,255) #F651F9FF srgba(246,81,249,1) 676: (255,255,255,255) #FFFFFFFF white 28392: ( 0, 0, 0,255) #000000FF black 237952: ( 71,163, 59,255) #47A33BFF srgba(71,163,59,1) 2704: (113, 78, 0,255) #714E00FF srgba(113,78,0,1) 421824: (147,221,253,255) #93DDFDFF srgba(147,221,253,1) 676: (246, 81,249,255) #F651F9FF srgba(246,81,249,1) 676: (255,255,255,255) #FFFFFFFF white 28392: ( 0, 0, 0,255) #000000FF black 237952: ( 71,163, 59,255) #47A33BFF srgba(71,163,59,1) 2704: (113, 78, 0,255) #714E00FF srgba(113,78,0,1) 421824: (147,221,253,255) #93DDFDFF srgba(147,221,253,1) 676: (246, 81,249,255) #F651F9FF srgba(246,81,249,1) 676: (255,255,255,255) #FFFFFFFF white 28392: ( 0, 0, 0,255) #000000FF black 237952: ( 71,163, 59,255) #47A33BFF srgba(71,163,59,1) 2704: (113, 78, 0,255) #714E00FF srgba(113,78,0,1) 421824: (147,221,253,255) #93DDFDFF srgba(147,221,253,1) 676: (246, 81,249,255) #F651F9FF srgba(246,81,249,1) 676: (255,255,255,255) #FFFFFFFF white $ convert test.gif -format %c -depth 8 histogram:info:- 28392: ( 0, 0, 0,255) #000000FF black 237952: ( 71,163, 59,255) #47A33BFF srgba(71,163,59,1) 2704: (113, 78, 0,255) #714E00FF srgba(113,78,0,1) 421824: (147,221,253,255) #93DDFDFF srgba(147,221,253,1) 676: (246, 81,249,255) #F651F9FF srgba(246,81,249,1) 676: (255,255,255,255) #FFFFFFFF white 28392: ( 0, 0, 0,255) #000000FF black 237952: ( 71,163, 59,255) #47A33BFF srgba(71,163,59,1) 2704: (113, 78, 0,255) #714E00FF srgba(113,78,0,1) 421824: (147,221,253,255) #93DDFDFF srgba(147,221,253,1) 676: (246, 81,249,255) #F651F9FF srgba(246,81,249,1) 676: (255,255,255,255) #FFFFFFFF white 28392: ( 0, 0, 0,255) #000000FF black 237952: ( 71,163, 59,255) #47A33BFF srgba(71,163,59,1) 2704: (113, 78, 0,255) #714E00FF srgba(113,78,0,1) 421824: (147,221,253,255) #93DDFDFF srgba(147,221,253,1) 676: (246, 81,249,255) #F651F9FF srgba(246,81,249,1) 676: (255,255,255,255) #FFFFFFFF white 28392: ( 0, 0, 0,255) #000000FF black 237952: ( 71,163, 59,255) #47A33BFF srgba(71,163,59,1) 2704: (113, 78, 0,255) #714E00FF srgba(113,78,0,1) 421824: (147,221,253,255) #93DDFDFF srgba(147,221,253,1) 676: (246, 81,249,255) #F651F9FF srgba(246,81,249,1) 676: (255,255,255,255) #FFFFFFFF white

所以第一个是缺less棕色和粉红色的,红色通道中的246113的颜色根本没有列出,并且这些在直方图中被正确列出(对于在较长输出中的每一帧可能重复)框架颜色映射版本。

这是GIF中调色板生成不正确的证据,这是我们用眼睛轻松看到的。 但是,我想知道的是,全球色彩映射版本有几个颜色重复的条目 。 这指出了ImageIO中调色板量化的相当明显的错误。 在有限的调色板中不应该有重复的条目。

简而言之:不要依赖Core Graphics来定量你的24位RGB图像。 在将它们发送到ImageIO并closures全局颜色映射之前,预先对它们进行量化。 如果问题仍然存在,那么ImageIO调色板写入已被破坏,您应该使用不同的GIF输出库