如何裁剪和调整JavaFX Image的大小?

zor*_*ran 3 canvas image javafx-2 javafx-8

我试图在JavaFX画布上显示非常大的图像.单张图像的分辨率为11980x8365.每个图像都有一个相应的世界文件,我可以使用它来正确定位图像.我的画布大小是800x600.有时候我需要在画布上写整个图像,有时只是它的一部分.

这是我到目前为止所做的:

  • 将完整大小的图像从文件加载到Image对象中.
  • 计算要显示的图像部分并计算比例参数,以正确地将其放入800x600画布中.

所以基本上我想使用GraphicsContext.drawImage(...) - 将给定图像的当前源矩形绘制到Canvas的给定目标矩形.

对于这种方法,我正确计算了所有参数.问题是有时Image大于2048x2048,并且由于某种原因,JavaFX尝试使用GPU将此图像直接绘制到画布上(如果我理解正确的话).那是我得到例外的时候:

java.lang.NullPointerException
    at com.sun.prism.sw.SWGraphics.drawTexture(SWGraphics.java:686) at com.sun.prism.sw.SWGraphics.drawTexture(SWGraphics.java:686)
    at com.sun.prism.sw.SWGraphics.drawTexture(SWGraphics.java:665)
    at com.sun.prism.sw.SWGraphics.drawTexture(SWGraphics.java:648)
    at com.sun.javafx.sg.prism.NGCanvas.handleRenderOp(NGCanvas.java:1228)
    at com.sun.javafx.sg.prism.NGCanvas.renderStream(NGCanvas.java:997)
    at com.sun.javafx.sg.prism.NGCanvas.renderContent(NGCanvas.java:578)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2043)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1951)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:225)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:575)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2043)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1951)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:225)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:575)
    at com.sun.javafx.sg.prism.NGNode.renderForClip(NGNode.java:2282)
    at com.sun.javafx.sg.prism.NGNode.renderRectClip(NGNode.java:2176)
    at com.sun.javafx.sg.prism.NGNode.renderClip(NGNode.java:2202)
    at com.sun.javafx.sg.prism.CacheFilter.impl_renderNodeToCache(CacheFilter.java:655)
    at com.sun.javafx.sg.prism.CacheFilter.render(CacheFilter.java:561)
    at com.sun.javafx.sg.prism.NGNode.renderCached(NGNode.java:2346)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2034)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1951)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:225)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:575)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2043)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1951)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:225)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:575)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2043)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1951)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:225)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:575)
    at com.sun.javafx.sg.prism.NGNode.renderForClip(NGNode.java:2282)
    at com.sun.javafx.sg.prism.NGNode.renderRectClip(NGNode.java:2176)
    at com.sun.javafx.sg.prism.NGNode.renderClip(NGNode.java:2202)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2037)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1951)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:225)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:575)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2043)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1951)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:225)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:575)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2043)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1951)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:225)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:575)
    at com.sun.javafx.sg.prism.NGNode.renderForClip(NGNode.java:2282)
    at com.sun.javafx.sg.prism.NGNode.renderRectClip(NGNode.java:2176)
    at com.sun.javafx.sg.prism.NGNode.renderClip(NGNode.java:2202)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2037)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1951)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:225)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:575)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2043)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1951)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:225)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:575)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2043)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1951)
    at com.sun.javafx.tk.quantum.ViewPainter.doPaint(ViewPainter.java:469)
    at com.sun.javafx.tk.quantum.ViewPainter.paintImpl(ViewPainter.java:317)
    at com.sun.javafx.tk.quantum.UploadingPainter.run(UploadingPainter.java:132)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
    at com.sun.javafx.tk.RenderJob.run(RenderJob.java:58)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:129)
    at java.lang.Thread.run(Thread.java:744)
Run Code Online (Sandbox Code Playgroud)

所以我接下来要尝试的是在将一些临时对象中的Image裁剪并缩放之前将其发送到画布.我找不到任何示例如何做到这一点.我发现的唯一例子是如何使用WritableImage裁剪图像,但我不知道如何在裁剪后缩放它并将其转换为Image.

Jam*_*s_D 8

您可以通过多种方式执行此操作,具体取决于您在流程中的各个阶段提供的信息.

如果您在加载前知道文件中的图像大小,并因此可以计算比例因子,您可以在加载时实际缩放它:

double requiredWidth = ... ;
double requiredHeight = ... ;
String imageURL = ... ;

Image image = new Image(imageURL, requiredWidth, requiredHeight, false, true);
Run Code Online (Sandbox Code Playgroud)

最后两个参数是preserveRatiosmooth.后者将强制更慢但更好的质量重新缩放算法.

现在,您可以将其裁剪为新链接,WritableImage您链接帖子中所示:

double x = ... ;
double y = ... ;
double width = ...;
double height = ... ;
WritableImage croppedImage = new WritableImage(image.getPixelReader(), x, y, width, height);
Run Code Online (Sandbox Code Playgroud)

其中x,y,width,和height(在缩放坐标)限定的裁剪区域.

然后你可以将裁剪后的图像绘制到画布中:

graphicsContent.drawImage(croppedImage, canvasX, canvasY);
Run Code Online (Sandbox Code Playgroud)

另一种方法是加载整个图像,然后使用a ImageView创建一个裁剪的缩放视图:

Image fullImage = new Image(imageURL);

// define crop in image coordinates:
Rectangle2D croppedPortion = new Rectangle2D(x, y, width, height);

// target width and height:
double scaledWidth = ... ;
double scaledHeight = ... ;

ImageView imageView = new ImageView(fullImage);
imageView.setViewport(croppedPortion);
imageView.setFitWidth(scaledWidth);
imageView.setFitHeight(scaledHeight);
imageView.setSmooth(true);
Run Code Online (Sandbox Code Playgroud)

现在,您可以通过拍摄快照来创建具有原始图像的裁剪版本的新图像ImageView.为此,您需要将其ImageView置于屏幕外场景中:

Pane pane = new Pane(imageView);
Scene offScreenScene = new Scene(pane);
WritableImage croppedImage = imageView.snapshot(null, null);
Run Code Online (Sandbox Code Playgroud)

然后你可以像以前一样将裁剪后的图像绘制到画布中.