batik是apache的一個開源項目,能夠實現svg的渲染,後端藉助它能夠比較簡單的實現圖片渲染,固然和java一向處理圖片不太方便同樣,使用起來也有很多坑java
下面記錄一個bug的修復過程apache
svg文件:後端
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <image y="0" width="100%" height="100%" x="0" xlink:href="http://image.uc.cn/o/wemedia/s/upload/2017/39c53604fe3587a4876396cf3785b801x200x200x13.png"/> <!--xlink:href="https://s17.mogucdn.com/mlcdn/c45406/180119_46ld8kkb54d3el06hela5d61e18f5_1024x966.png"/>--> <!--xlink:href="http://avatar.csdn.net/A/8/B/3_u010889145.jpg"/>--> </svg>
依次測試了三個圖片,兩個png,一個jpg,很不幸第一個png會拋異常app
輸出的堆棧信息如ide
The URI "http://image.uc.cn/o/wemedia/s/upload/2017/39c53604fe3587a4876396cf3785b801x200x200x13.png" on element <image> can't be opened because: PNG URL is corrupt or unsupported variant at org.apache.batik.bridge.UserAgentAdapter.getBrokenLinkDocument(UserAgentAdapter.java:448) at org.apache.batik.bridge.SVGImageElementBridge.createRasterImageNode(SVGImageElementBridge.java:642) at org.apache.batik.bridge.SVGImageElementBridge.createImageGraphicsNode(SVGImageElementBridge.java:340) at org.apache.batik.bridge.SVGImageElementBridge.buildImageGraphicsNode(SVGImageElementBridge.java:180) at org.apache.batik.bridge.SVGImageElementBridge.createGraphicsNode(SVGImageElementBridge.java:122) at org.apache.batik.bridge.GVTBuilder.buildGraphicsNode(GVTBuilder.java:213) at org.apache.batik.bridge.GVTBuilder.buildComposite(GVTBuilder.java:171) at org.apache.batik.bridge.GVTBuilder.build(GVTBuilder.java:82) at org.apache.batik.transcoder.SVGAbstractTranscoder.transcode(SVGAbstractTranscoder.java:208) at org.apache.batik.transcoder.image.ImageTranscoder.transcode(ImageTranscoder.java:92) at org.apache.batik.transcoder.XMLAbstractTranscoder.transcode(XMLAbstractTranscoder.java:142) at org.apache.batik.transcoder.SVGAbstractTranscoder.transcode(SVGAbstractTranscoder.java:156) ...
既然出現了這個問題,那麼就要去修復解決了,固然遇到這麼鬼畜的問題,最多見的幾個步驟:svg
經過上面的堆棧信息,能夠想見,debug的幾個地方也和明確了,首先定位到下面這一行測試
at org.apache.batik.bridge.UserAgentAdapter.getBrokenLinkDocument(UserAgentAdapter.java:448)
爲何這麼幹?由於首先得確認下這個異常是怎麼拋出來的,逆向推,直接看源碼,發現直接拋出異常ui
再往上走this
at org.apache.batik.bridge.SVGImageElementBridge.createRasterImageNode(SVGImageElementBridge.java:642)
因此說由於這個if條件判斷成立,致使進入了這個異常邏輯,判斷的邏輯也沒啥好說的,如今的關鍵是這個參數對象img是怎麼來的.net
at org.apache.batik.bridge.SVGImageElementBridge.createImageGraphicsNode(SVGImageElementBridge.java:340)
而後就稍微清晰一點了,直接將火力放在下面的方法中
org.apache.batik.ext.awt.image.spi.ImageTagRegistry#readURL(java.io.InputStream, org.apache.batik.util.ParsedURL, org.apache.xmlgraphics.java2d.color.ICCColorSpaceWithIntent, boolean, boolean)
在這個方法內部,也沒什麼好說的,單步多調幾回,就能發現異常的case是怎麼來的了,省略掉中間各類單步debug的過程,下面直接進入關鍵鏈路
org.apache.batik.ext.awt.image.codec.imageio.AbstractImageIORegistryEntry
經過上面的一路以後,發現最終的關鍵就是上面這個抽象類,順帶也能夠看下這個抽象類的幾個子類,有JPEGxxx, PNGxxx, TIFFxxx,而後問題來了,都已經有相關實現了,因此png講道理應該是會支持的纔對吧,但和實際的表現太不同了吧,因此有必要擼一把源碼了
public Filter handleStream(InputStream inIS, ParsedURL origURL, boolean needRawData) { final DeferRable dr = new DeferRable(); final InputStream is = inIS; final String errCode; final Object [] errParam; if (origURL != null) { errCode = ERR_URL_FORMAT_UNREADABLE; errParam = new Object[] {getFormatName(), origURL}; } else { errCode = ERR_STREAM_FORMAT_UNREADABLE; errParam = new Object[] {getFormatName()}; } Thread t = new Thread() { @Override public void run() { Filter filt; try{ Iterator<ImageReader> iter = ImageIO.getImageReadersByMIMEType( getMimeTypes().get(0).toString()); if (!iter.hasNext()) { throw new UnsupportedOperationException( "No image reader for " + getFormatName() + " available!"); } ImageReader reader = iter.next(); ImageInputStream imageIn = ImageIO.createImageInputStream(is); reader.setInput(imageIn, true); int imageIndex = 0; dr.setBounds(new Rectangle2D.Double (0, 0, reader.getWidth(imageIndex), reader.getHeight(imageIndex))); CachableRed cr; //Naive approach possibly wasting lots of memory //and ignoring the gamma correction done by PNGRed :-( //Matches the code used by the former JPEGRegistryEntry, though. BufferedImage bi = reader.read(imageIndex); cr = GraphicsUtil.wrap(bi); cr = new Any2sRGBRed(cr); cr = new FormatRed(cr, GraphicsUtil.sRGB_Unpre); WritableRaster wr = (WritableRaster)cr.getData(); ColorModel cm = cr.getColorModel(); BufferedImage image = new BufferedImage (cm, wr, cm.isAlphaPremultiplied(), null); cr = GraphicsUtil.wrap(image); filt = new RedRable(cr); } catch (IOException ioe) { // Something bad happened here... filt = ImageTagRegistry.getBrokenLinkImage (AbstractImageIORegistryEntry.this, errCode, errParam); } catch (ThreadDeath td) { filt = ImageTagRegistry.getBrokenLinkImage (AbstractImageIORegistryEntry.this, errCode, errParam); dr.setSource(filt); throw td; } catch (Throwable t) { filt = ImageTagRegistry.getBrokenLinkImage (AbstractImageIORegistryEntry.this, errCode, errParam); } dr.setSource(filt); } }; t.start(); return dr; }
看上面的實現是一個很是有意思的事情, 開了一個線程作事情,並且直接就返回了,至關於給了別人一個儲物箱的鑰匙,雖然如今儲物箱是空的,可是回頭我會填滿的
言歸正傳,主要的業務邏輯就在這個線程裏了,核心的幾行代碼就是
// 加載圖片,轉爲BufferedImage對象 BufferedImage bi = reader.read(imageIndex); cr = GraphicsUtil.wrap(bi); // 下面實現對圖片的ARGB進行修改 cr = new Any2sRGBRed(cr); cr = new FormatRed(cr, GraphicsUtil.sRGB_Unpre); WritableRaster wr = (WritableRaster)cr.getData(); ColorModel cm = cr.getColorModel(); BufferedImage image = new BufferedImage (cm, wr, cm.isAlphaPremultiplied(), null); cr = GraphicsUtil.wrap(image); filt = new RedRable(cr);
debug上面的幾行代碼,發現問題比較明顯了,就是這個圖片的轉換跪了,至於爲啥? java的圖片各類蛋疼至極,這裏面的邏輯,真心搞不進去,so深挖到此爲止
問題定位到了,固然就是想辦法來修復了,簡單來講,須要兼容的就是圖片的類型轉換上了,直接用原來的可能會拋異常,因此作了一個簡單的兼容邏輯
if(bi.getType() == BufferedImage.TYPE_BYTE_INDEXED) { BufferedImage image = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = image.createGraphics(); g2d.drawImage(bi, 0, 0, null); g2d.dispose(); cr = GraphicsUtil.wrap(image); } else { cr = GraphicsUtil.wrap(bi); cr = new Any2sRGBRed(cr); cr = new FormatRed(cr, GraphicsUtil.sRGB_Unpre); WritableRaster wr = (WritableRaster)cr.getData(); ColorModel cm = cr.getColorModel(); BufferedImage image = new BufferedImage (cm, wr, cm.isAlphaPremultiplied(), null); cr = GraphicsUtil.wrap(image); }
再次驗證,ok
注意:
一個問題來了,上面的兼容是須要修改源碼的,咱們能夠怎麼辦?有幾種解決方法
至於個人選擇,就是使用了猥瑣方法二
盡信書則不如,已上內容,純屬一家之言,因本人能力通常,看法不全,若有問題,歡迎批評指正