当前位置: 首页 > 知识库问答 >
问题:

如何裁剪和调整JavaFX图像?

闻人鸿文
2023-03-14

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

以下是我到目前为止所做的:

  • 将文件中的全尺寸图像加载到Image对象中。
  • 计算要显示图像的哪一部分,并计算缩放参数以正确地将其放入800x600画布中。

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

对于这种方法,我正确计算了所有参数。问题是,有时图像大于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)

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

共有1个答案

魏宸
2023-03-14

有很多方法可以做到这一点,这取决于您在流程中的不同阶段可以获得哪些信息。

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

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

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

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

现在,您只需将其裁剪为新的可写图像,如您链接的帖子所示:

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

其中xy宽度高度定义了裁剪区域(在缩放坐标中)。

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

graphicsContent.drawImage(croppedImage, canvasX, canvasY);

另一种方法是加载整个图像,然后使用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);

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

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

然后你可以像以前一样把裁剪过的图像绘制到画布上。

 类似资料:
  • 我们现在知道调用drawImage方法的第一种方式,即将完整尺寸的图像绘制到画布上,但超过画布边界的部分被剪掉了。 为了解决这个问题,需要调整图像大小或者控制图像的裁剪。通过drawImage方法的最后两种调用方式都能够完成这两个任务。 第一种调用可以调整图像大小,第二种可以同时调整和裁剪图像。drawlmage的所有调用方式的唯一区别是所使用参数的个数和类型不同。 调整图像大小 调整图像大小与绘

  • 问题内容: 如何使用JavaFX 完成Android ? 我有图像视图: 问题答案: 感谢@TravisF的发布,我实现了最后一个解决方案,以使图像始终具有相同的高度,宽度和位置(中心)。

  • 我试图在从图库中选择图像后使用intent来裁剪图像。以下是我的代码片段 在这里,我使用PICK_IMAGE_REQUEST意图句柄调用上面的代码段 由于我在裁剪后使用了相同的意图,即PICK_IMAGE_REQUEST,可能会出现什么问题

  • 问题内容: 给定任意图像,我想从图像中心裁剪一个正方形并将其显示在给定的正方形内。 问题答案: 一种解决方案是使用以元素大小为裁剪尺寸的元素为中心的背景图像。 基本例子 标签示例 此版本保留了标签,因此我们不会丢失拖动或右键单击以保存图像的功能。信贷帕克贝内特不透明度伎俩。 / 请参阅支持的浏览器 。 的CSS3图像规范定义和属性,它们一起允许对规模更大的控制和一个的图像内容的位置元素。有了这些,

  • 问题内容: 我正在从磁盘读取图像,并将其显示在一行中。图像文件大于行中需要显示的图像文件。由于我需要将RAM 缓存在内存中以加快访问速度,因此我希望它们的大小只能与s 一样大(85x85 dip) 现在我正在读文件 位图= BitmapFactory.decodeFile(file); 而ImageView负责缩放和裁剪它 android:scaleType =“ centerCrop” AFAI

  • 问题内容: 我想显示具有特定宽度和高度的URL的图像,即使它 具有不同的尺寸比例。所以我想调整大小(保持比例), 然后将图像切成我想要的大小。 我可以使用html img属性调整大小,也可以使用剪切background-image。 我该怎么办? 例: 这个图片: 具有800x600像素大小,我想显示为200x100 像素图像 通过img我可以调整图像大小200x150px: 这给了我这个: 而且