代码之家  ›  专栏  ›  技术社区  ›  Skyler Lauren

保存动画Gif时iOS颜色不正确

  •  10
  • Skyler Lauren  · 技术社区  · 8 年前

    @1x 32 x 32

    enter image description here

    @10 x 320 x 320

    enter image description here

    @26 x 832 x 832

    enter image description here

    这是我用来创建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) 图像非常精细,颜色正确。我希望有人知道我错过了什么。

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

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

    2 回复  |  直到 8 年前
        1
  •  7
  •   Sulthan    8 年前

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

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

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

    似乎只需要关闭全局颜色贴图。还可以使用为每个帧显式设置颜色映射 kCGImagePropertyGIFImageColorMap

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

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

        2
  •  2
  •   Marc Palmer    8 年前

    接下来,这里有一些关于正在发生的量化失败的更多背景。如果通过运行GIF输出 imagemagick 要为具有全局颜色图和每帧颜色图的版本提取调色板,需要深入了解问题的根源:

    $ 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

    246 113

    多种颜色。这就指出了ImageIO中调色板量化的一个非常明显的错误。在有限的调色板中不应有重复条目。