How to work around Unrecognized image loader: null in JavaFX?

匿名 (未验证) 提交于 2019-12-03 01:05:01

问题:

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


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!