简体   繁体   English

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

[英]How to create an image from canvas data?

In my application I am trying to save an arbitrary part of a rendered HTML canvas to an image file. 在我的应用程序中,我试图将呈现的HTML画布的任意部分保存到图像文件中。 In my Javascript I call ctx. getImageData (x, y, w, h) 在我的Javascript中,我称之为ctx. getImageData (x, y, w, h) ctx. getImageData (x, y, w, h) and pass the resulting object to my macruby code (though if you know a solution in objc I am also very interested). ctx. getImageData (x, y, w, h)并将生成的对象传递给我的macruby代码(尽管如果你知道objc中的解决方案我也很感兴趣)。

There I'm trying to create a NSBitmapImageRep object so that I can then save to an image format the user desires. 在那里,我正在尝试创建一个NSBitmapImageRep对象,以便我可以保存为用户所需的图像格式。

This is my code so far (the function gets a WebScriptObject as it's argument): 到目前为止这是我的代码(该函数获取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

The code doesn't seem to get through the initWithBitmapDataPlanes function but gives no error. 代码似乎没有通过initWithBitmapDataPlanes函数,但没有给出错误。

My question is: what am I doing wrong? 我的问题是:我做错了什么? Is this approach reasonable (if not, what would be better?). 这种方法是否合理(如果没有,哪种更好?)。

Edit 编辑

Using Phrogz' answer below I got an intermediate solution: I use another canvas, getImageData, putImageData and toDataURL to get a data url of the region needed. 使用下面的Phrogz答案,我得到了一个中间解决方案:我使用另一个画布,getImageData,putImageData和toDataURL来获取所需区域的数据网址。 In my setimagedata I simply save the data url and my dataOfType: error: method looks like this: 在我的setimagedata我只需保存数据url和我的dataOfType: error:方法如下所示:

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

The missing secrete sauce it this ugly hax: 这个丑陋的hax丢失的秘密酱:

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

It leverages duck typing in Cocoa and defines a selector normally on NSData to write itself to a file. 它利用Cocoa中的duck类型,并在NSData上定义一个选择器,将自己写入文件。

This seems to work so far and I'm happy I reached a solution. 这似乎到目前为止工作,我很高兴我达成了解决方案。 However I would still like to see a solution using NSBitmapImageRep. 但是我仍然希望看到使用NSBitmapImageRep的解决方案。 The next feature I'm implementing is exporting to video and I believe I will need the finer control provided by this class. 我正在实现的下一个功能是导出到视频,我相信我需要这个类提供更好的控制。

Instead of using getImageData , I would suggest using Canvas.toDataURL . 我建议使用Canvas.toDataURL ,而不是使用getImageData This will give you a binary PNG (or JPEG) that happens to be base64 encoded. 这将为您提供恰好是base64编码的二进制PNG(或JPEG)。 Decode the base64 and you'll have a file you can save for the user, or process to transcode to another format. 对base64进行解码,您将拥有一个可以为用户保存的文件,或者可以转换为另一种格式的进程。

Edit : I originally deleted my answer because I realized that you wanted to serialize a sub-region of the canvas. 编辑 :我最初删除了我的答案,因为我意识到你想要序列化画布的子区域。 Then I realized that if this helps you could instead do this: 然后我意识到,如果这有助于你可以改为:

  1. Create a new canvas of the size of the subregion. 创建一个子区域大小的新画布。
  2. Use context.drawImage() to copy the sub-region from the original to the new canvas. 使用context.drawImage()将子区域从原始画布复制到新画布。
  3. Call Canvas.toDataURL on the new canvas to get the base64-serialized data. 在新画布上调用Canvas.toDataURL以获取base64序列化数据。

To decode the base64 data, you can use the base64 ruby library's decode64 method. 要解码base64数据,可以使用base64 ruby库的decode64方法。 Note that the implementation of this method is exceedingly simple, however, and you could just inline it: 请注意,此方法的实现非常简单,您可以将其内联:

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

Edit 2 : For example, given the results of a toDataURL call placed in a Ruby string, simply: 编辑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 ) }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM