如何裁剪和调整JavaFX Image的大小? [英] How to crop and resize JavaFX Image?

查看:211
本文介绍了如何裁剪和调整JavaFX Image的大小?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to display very large images on JavaFX canvas. Resolution of a single image is 11980x8365. Each image has a corresponding world file and I can use it to position images correctly. My canvas size is 800x600. Sometimes I need to write whole image on the canvas, and sometimes just a part of it.

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

Here is what I've done so far:


  • 将完整大小的图像从文件加载到Image对象中。

  • 计算要显示和计算缩放
    参数的图像部分,以正确地将其放入800x600画布中。

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

So basically I wanted to use GraphicsContext.drawImage(...) - Draws the current source rectangle of the given image to the given destination rectangle of the Canvas.

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

For this method I calculated all parameters correctly. Problem is that sometimes Image is larger than 2048x2048, and for some reason JavaFX tries to draw this image directly to the canvas using GPU (if I understood that correctly). That's when I get exception:

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)

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

So what I want to try next is to crop and scale Image in some temporary object before sending it to canvas. I can't find anywhere example how to do that. Only example I found is how to crop Image using WritableImage, but I don't know how to scale it after cropping and convert it to Image.

推荐答案

有很多方法可以做到这一点,具体取决于你在这个过程的不同阶段可以得到什么信息。

There are a number of ways you can do this, depending on what information you have available at various points in the process.

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

If you know the size of the image on file before loading, and can thus compute the scale factor, you can actually scale it as you load it:

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

Image image = new Image(imageURL, requiredWidth, requiredHeight, false, true);

最后两个参数是 preserveRatio 平滑。后者将强制使用速度较慢但质量较好的重新缩放算法。

The last two parameters are preserveRatio and smooth. The latter will force a slower but better quality rescaling algorithm.

现在你可以将它裁剪为新的 WritableImage 发布链接

Now you can just crop it to a new WritableImage as in the post you linked:

double x = ... ;
double y = ... ;
double width = ...;
double height = ... ;
WritableImage croppedImage = new WritableImage(image.getPixelReader(), x, y, width, height);

其中 x y width height 定义了裁剪区域(在缩放坐标中) 。

where x, y, width, and height defined the cropped region (in the scaled coordinates).

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

And then you can just draw the cropped image into your canvas:

graphicsContent.drawImage(croppedImage, canvasX, canvasY);

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

Anther approach is to load the whole image, and then use an ImageView to create a cropped, scaled view of it:

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

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

Now you can create a new image with the cropped version of the original image by taking a snapshot of the ImageView. To do this, you need to place the ImageView into an off-screen scene:

Pane pane = new Pane(imageView);
Scene offScreenScene = new Scene(pane);
WritableImage croppedImage = imageView.snapshot(null, null);

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

and then you can draw the cropped image into the canvas as before.

这篇关于如何裁剪和调整JavaFX Image的大小?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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