如何从画布数据创建图像?

时间:2011-01-09 11:59:32

标签: javascript objective-c cocoa canvas macruby

在我的应用程序中,我试图将呈现的HTML画布的任意部分保存到图像文件中。在我的Javascript中,我调用ctx.getImageData(x, y, w, h)并将生成的对象传递给我的macruby代码(尽管如果你知道objc中的解决方案我也很感兴趣)。

我正在尝试创建一个NSBitmapImageRep对象,以便我可以保存为用户所需的图像格式。

到目前为止这是我的代码(函数获取WebScriptObject作为参数):

def setimagedata(d)
    w =  d.valueForKey("width").to_i
    h =  d.valueForKey("height").to_i
    data = Pointer.new(:char, d.valueForKey("data").valueForKey("length").to_i)
    d.valueForKey("data").valueForKey("length").to_i.times do |i|
        data[i] = d.valueForKey("data").webScriptValueAtIndex(i).to_i
    end
    puts "data complete" # get's called
    @exported_image = NSBitmapImageRep.alloc.initWithBitmapDataPlanes(data,
        pixelsWide: w, pixelsHigh:h, bitsPerSample: 32,
        samplesPerPixel: 4, hasAlpha: true, isPlanar: false, 
        colorSpaceName: NSCalibratedRGBColorSpace, 
        bitmapFormat: NSAlphaNonpremultipliedBitmapFormat, 
        bytesPerRow: 0, bitsPerPixel: 0)
    puts "done" # doesn't get called
end

代码似乎没有通过initWithBitmapDataPlanes函数但没有错误。

我的问题是:我做错了什么?这种方法是否合理(如果没有,哪种更好?)。

修改

使用Phrogz的答案我得到了一个中间解决方案:我使用另一个画布,getImageData,putImageData和toDataURL来获取所需区域的数据网址。在我的setimagedata中,我只是保存数据网址,我的dataOfType: error:方法如下所示:

def dataOfType(type, error:outError)
    workspace = NSWorkspace.sharedWorkspace
    if workspace.type(type, conformsToType: "public.image")
        @data_url[ /(?<=,).+/ ].unpack("m").first
    end
end

这个丑陋的哈克斯失踪的秘密酱:

class NSString
    def writeToURL(url, options: opts, error: error)
        File.open(url.path, "w") {|f| f << self }
    end
end

它利用Cocoa中的duck类型,并在NSData上定义一个选择器,将自己写入文件。

这似乎到目前为止工作,我很高兴我达成了解决方案。但是我仍然希望看到使用NSBitmapImageRep的解决方案。我正在实现的下一个功能是导出到视频,我相信我需要这个类提供的更好的控制。

1 个答案:

答案 0 :(得分:3)

我建议使用Canvas.toDataURL而不是getImageData。这将为您提供恰好是base64编码的二进制PNG(或JPEG)。对base64进行解码,您将拥有一个可以为用户保存的文件,或者处理转码为其他格式的文件。

编辑:我最初删除了我的答案,因为我意识到您要序列化画布的子区域。然后我意识到,如果这有助于你做到这一点:

  1. 创建一个子区域大小的新画布。
  2. 使用context.drawImage()将子区域从原始图片复制到新画布。
  3. 在新画布上调用Canvas.toDataURL以获取base64序列化数据。
  4. 要解码base64数据,您可以使用base64 ruby​​库的decode64方法。请注意,此方法的实现非常简单,您可以将其内联:

    def decode64(str)
      str.unpack("m").first
    end
    

    编辑2 :例如,给定放置在Ruby字符串中的toDataURL调用的结果,只需:

    require 'base64'
    data_only = data_url[ /(?<=,).+/ ] # Find everything after the first comma
    File.open( 'foo.png', 'w' ){ |f| f << Base64.decode64( data_only ) }