保存动画 Gif 时 iOS 颜色不正确 [英] iOS Colors Incorrect When Saving Animated Gif

查看:31
本文介绍了保存动画 Gif 时 iOS 颜色不正确的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了这个非常奇怪的问题.我正在从 UIImages 创建动画 gif,而且大部分时间它们都是正确的.然而,当我开始进入更大尺寸的图像时,我的颜色开始消失.例如,如果我制作不超过 10 种颜色的 4 帧 32 x 32 像素图像没有问题.如果我将相同的图像放大到 832 x 832,我会失去粉红色,而我的棕色会变成绿色.

I am having this very strange issue. I am creating animated gifs from UIImages and most of the time they come out correct. However when I start to get into larger size images my colors start to disappear. For example if I do a 4 frame 32 x 32 pixel image with no more than 10 colors no issue. If I scale the same image up to 832 x 832 I lose a pink color and my brown turns green.

@1x 32 x 32

@1x 32 x 32

@10x 320 x 320

@10x 320 x 320

@26x 832 x 832

@26x 832 x 832

这是我用来创建 gif 的代码...

Here is the code I use to create the gif...

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) 之前设置了一个断点,并且图像非常好,颜色正确.我希望有人知道我错过了什么.

I have put in a break point right before I call CGImageDestinationAddImage(destination, image.cgImage!, frameProperties as CFDictionary) and the image is perfectly fine with the correct colors. I hope someone out there knows what I am missing.

更新

这是一个示例项目.请注意,虽然它在预览中没有动画,但它保存了一个动画 gif,我在控制台中注销了图像的位置.

Here is a sample project. Note that although it isn't animated in the preview it is saving an animated gif and I log out the location of the image in the console.

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

推荐答案

似乎关闭全局颜色映射可以解决问题:

It seems that turning off the global color map fixes the problem:

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

请注意,与 PNG 不同,GIF 只能使用 256 色图,没有透明度.对于动画 GIF,可以使用全局或每帧颜色映射.

Note that unlike PNGs, GIFs can use only a 256 color map, without transparency. For animated GIFs there can be either a global or a per-frame color map.

遗憾的是,Core Graphics 不允许我们直接使用颜色映射,因此在对 GIF 进行编码时会进行一些自动颜色转换.

Unfortunately, Core Graphics does not allow us to work with color maps directly, therefore there is some automatic color conversion when the GIF is encoded.

看来,关闭全局颜色映射是完全需要的.使用 kCGImagePropertyGIFImageColorMap 为每一帧显式设置颜色映射也可能会起作用.

It seems that turning off the global color map is all what is needed. Also setting up color map explicitly for every frame using kCGImagePropertyGIFImageColorMap would probably work too.

由于这似乎不能可靠地工作,让我们为每一帧创建我们自己的颜色图:

Since this seems not to work reliably, let's create our own color map for every frame:

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))
    }
}

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

Now let's update our methods:

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);
    }

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

Of course, this will work only if the number of colors is less than 256. I would really recommend a custom GIF library that can handle the color conversion correctly.

这篇关于保存动画 Gif 时 iOS 颜色不正确的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆