可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
My application, written in Java 8 using JavaFX, takes screenshots, or rather, snapshots, and twice it generated this error:
java.lang.IllegalArgumentException: Unrecognized image loader: null at javafx.scene.image.WritableImage.loadTkImage(WritableImage.java:240) at javafx.scene.image.WritableImage.access$000(WritableImage.java:46) at javafx.scene.image.WritableImage$1.loadTkImage(WritableImage.java:51) at javafx.scene.Scene.doSnapshot(Scene.java:1236) at javafx.scene.Node.doSnapshot(Node.java:1864) at javafx.scene.Node.snapshot(Node.java:1942)
So far, in my application, I wasn't able to reproduce it. Searching for this issue I found this:
https://bugs.openjdk.java.net/browse/JDK-8116783
that points to:
https://bugs.openjdk.java.net/browse/JDK-8088198
It seems to be an open bug in JavaFX: "Exception thrown from snapshot if dimensions are larger than max texture size".
The description of the bug says that it happens occasionally. Does anybody know if catching the exception and retrying would be a good way or workaround it?
Update: when I tried to reproduce this error by making the screenshot very big, I got a completely different error:
java.lang.NullPointerException at com.sun.prism.impl.ps.BaseShaderContext.initLCDBuffer(BaseShaderContext.java:703) at com.sun.prism.impl.ps.BaseShaderContext.validateLCDBuffer(BaseShaderContext.java:725) at com.sun.prism.impl.ps.BaseShaderGraphics.initLCDSampleRT(BaseShaderGraphics.java:1925) at com.sun.prism.impl.ps.BaseShaderGraphics.drawString(BaseShaderGraphics.java:2059) at com.sun.javafx.webkit.prism.WCGraphicsPrismContext$10.doPaint(WCGraphicsPrismContext.java:936) at com.sun.javafx.webkit.prism.WCGraphicsPrismContext$Composite.paint(WCGraphicsPrismContext.java:1500) at com.sun.javafx.webkit.prism.WCGraphicsPrismContext$Composite.paint(WCGraphicsPrismContext.java:1485) at com.sun.javafx.webkit.prism.WCGraphicsPrismContext.drawString(WCGraphicsPrismContext.java:948) at com.sun.webkit.graphics.GraphicsDecoder.decode(GraphicsDecoder.java:299) at com.sun.webkit.graphics.WCRenderQueue.decode(WCRenderQueue.java:92) at com.sun.webkit.WebPage.paint2GC(WebPage.java:734) at com.sun.webkit.WebPage.paint(WebPage.java:701) at com.sun.javafx.sg.prism.web.NGWebView.renderContent(NGWebView.java:96) at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053) at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945) at com.sun.javafx.tk.quantum.QuantumToolkit$5.draw(QuantumToolkit.java:1393) at com.sun.javafx.tk.quantum.QuantumToolkit$5.run(QuantumToolkit.java:1429) 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:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125) at java.lang.Thread.run(Thread.java:748)
It similarly is a thread with no call touching my code at all.
回答1:
It happened to me too...
This happens if the snapshot ends up being to big.
I was able to fix this by adding a scale in SnapshotParameters
to say, half. Once you lay your hand on the image, you can do just anything you want, including getting it back to size.
回答2:
TL;DR:
- even if using Oracle JVM, see: https://wiki.openjdk.java.net/pages/viewpage.action?pageId=20415996
- use
-Dprism.poolstats=true -Dprism.verbose=true
to check your maxvram limit and to see how much vram you actually consume - setting
-Dprism.maxvram=xxxxM
to lower value might help to reproduce the problem - setting
-Dprism.maxvram=xxxxM
to higher value might help to solve the problem
my case (for anyone interested)
As suggested in the link above I used -Dprism.poolstats=true -Dprism.verbose=true
and found that maxvram is high enough. Anyway, to be really sure, I added -Dprism.maxvram=1024M -Dprism.targetvram=512M
but it didn't help. There was enough memory but the allocation kept failing with the following errors:
D3D Vram Pool: 66.929.834 used (12,5%), 81.273.002 target (15,1%), 536.870.912 max 36 total resources being managed average resource age is 12,6 frames 0 resources at maximum supported age (0,0%) 5 resources marked permanent (13,9%) 0 resources have had mismatched locks (0,0%) 0 resources locked (0,0%) 31 resources contain interesting data (86,1%) 0 resources disappeared (0,0%) Growing pool D3D Vram Pool target to 116.658.814 Growing pool D3D Vram Pool target to 131.338.878 Growing pool D3D Vram Pool target to 159.043.730 D3D hresult failed :D3D_ERROR ffffffff8007000e java.lang.Exception: Stack trace at com.sun.prism.d3d.D3DContext.validate(D3DContext.java:133) at com.sun.prism.d3d.D3DContext.validatePresent(D3DContext.java:183) at com.sun.prism.d3d.D3DRTTexture.readPixels(D3DRTTexture.java:116) at com.sun.prism.d3d.D3DRTTexture.readPixels(D3DRTTexture.java:90) at com.sun.javafx.tk.quantum.QuantumToolkit$5.run(QuantumToolkit.java:1437) at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) at java.util.concurrent.FutureTask.runAndReset(Unknown Source) at com.sun.javafx.tk.RenderJob.run(RenderJob.java:58) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125) at java.lang.Thread.run(Unknown Source) D3D Vram Pool: 109.314.750 used (20,4%), 159.043.730 target (29,6%), 536.870.912 max 37 total resources being managed average resource age is 13,1 frames 0 resources at maximum supported age (0,0%) 5 resources marked permanent (13,5%) 0 resources have had mismatched locks (0,0%) 0 resources locked (0,0%) 32 resources contain interesting data (86,5%) 0 resources disappeared (0,0%) 2018-07-17 18:05:09 [JavaFX Application Thread] de.apris3.client.ErrorHandler.handle() ERROR: : java.lang.IllegalArgumentException: Unrecognized image loader: null at javafx.scene.image.WritableImage.loadTkImage(WritableImage.java:240) at javafx.scene.image.WritableImage.access$000(WritableImage.java:46) at javafx.scene.image.WritableImage$1.loadTkImage(WritableImage.java:51) at javafx.scene.Scene.doSnapshot(Scene.java:1236) at javafx.scene.Node.doSnapshot(Node.java:1864) at javafx.scene.Node.snapshot(Node.java:1942) ... D3D Vram Pool: 112.230.142 used (20,9%), 159.043.730 target (29,6%), 536.870.912 max 40 total resources being managed average resource age is 13,0 frames 0 resources at maximum supported age (0,0%) 5 resources marked permanent (12,5%) 0 resources have had mismatched locks (0,0%) 0 resources locked (0,0%) 35 resources contain interesting data (87,5%) 0 resources disappeared (0,0%)
Obviously, JVM couldn't allocate enough memory even though the limit was high enough. Searching for D3D hresult failed :D3D_ERROR ffffffff8007000e
revealed it's actually a Windows error (https://docs.microsoft.com/en-us/windows/desktop/seccrypto/common-hresult-values):
E_OUTOFMEMORY Failed to allocate necessary memory 0x8007000E
Finally, after checking the device manager, it turned out that the client had installed a new graphic card and Windows Updated screwed the drivers up.
回答3:
I encountered the same problem when trying to make a snapshot from a large node. In my case scaling was not a possibility since I needed a high resolution image. To solve the problem I made multiple snapshots from subsections of the node and stiched them together afterwards.
Here is the function I wrote:
private void exportPng(final Node node, final String filePath) { final int w = (int) node.getLayoutBounds().getWidth(); final int h = (int) node.getLayoutBounds().getHeight(); final WritableImage full = new WritableImage(w, h); // defines the number of tiles to export (use higher value for bigger resolution) final int size = 2; final int tileWidth = w / size; final int tileHeight = h / size; System.out.println("Exporting node (building " + (size * size) + " tiles)"); try { for (int col = 0; col < size; ++col) { for (int row = 0; row < size; ++row) { final int x = row * tileWidth; final int y = col * tileHeight; final SnapshotParameters params = new SnapshotParameters(); params.setViewport(new Rectangle2D(x, y, tileWidth, tileHeight)); final CompletableFuture<Image> future = new CompletableFuture<>(); // keeps fx application thread unblocked Platform.runLater(() -> future.complete(node.snapshot(params, null))); full.getPixelWriter().setPixels(x, y, tileWidth, tileHeight, future.get().getPixelReader(), 0, 0); } } System.out.println("Exporting node (saving to file)"); ImageIO.write(SwingFXUtils.fromFXImage(full, null), "png", new File(filePath)); System.out.println("Exporting node (finished)"); } catch (Exception e) { e.printStackTrace(); } }