2017-07-27 72 views
1

動機: 我的目標是以最有效的方式將AWT BufferedImage轉換爲SWT ImageData。這個問題的典型答案是整個圖像的逐像素轉換,即O(n^2)複雜度。如果他們可以交換整個像素矩陣,效率會更高。 BufferedImage在詳細確定顏色和阿爾法編碼方面似乎非常靈活。爲了給您提供更廣泛的上下文,我使用Apache Batik編寫了一個SVG圖標按需光柵化器,但它是針對SWT(Eclipse)應用程序的。蠟染只渲染到java.awt.image.BufferedImage,但SWT組件需要org.eclipse.swt.graphics.Image如何使用單獨的alpha光柵創建BufferedImage

它們的支持柵格對象:java.awt.image.Rasterorg.eclipse.swt.graphics.ImageData表示完全相同的東西,它們只是包圍代表像素的二維字節值數組。如果我可以讓一個或另一個使用顏色編碼,瞧,我可以重新使用支持陣列。

我得到了很遠,這個工程:

// defined blank "canvas" for Batik Transcoder for SVG to be rasterized there 
public BufferedImage createCanvasForBatik(int w, int h) { 
    new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR); 
} 

// convert AWT's BufferedImage to SWT's ImageData to be made into SWT Image later 
public ImageData convertToSWT(BufferedImage bufferedImage) { 
    DataBuffer db = bufferedImage.getData().getDataBuffer(); 
    byte[] matrix = ((DataBufferByte) db).getData(); 

    PaletteData palette = 
      new PaletteData(0x0000FF, 0x00FF00, 0xFF0000); // BRG model 

    // the last argument contains the byte[] with the image data 
    int w = bufferedImage.getWidth(); 
    int h = bufferedImage.getHeight(); 

    ImageData swtimgdata = new ImageData(w, h, 32, palette); 
    swtimgdata.data = matrix; // ImageData has all field public!! 

    // ImageData swtimgdata = new ImageData(w, h, 32, palette, 4, matrix); ..also works 
    return swtimgdata; 
} 

它除了透明度:(

它看起來像ImageData需要(?總是)阿爾法所有的作品是一個單獨的柵格,看到ImageData.alphaData從顏色光柵,見ImageData.data;?都是byte[]類型

有沒有辦法如何使ImageData接受ARGB模型是阿爾法與其他顏色混合我?懷疑所以我走了另一條路。使BufferedImage對顏色和alpha使用單獨的陣列(又名柵格或「帶」)。 ComponentColorModelBandedRaster似乎準確地用於這些事情。

到目前爲止,我來到這裏:

public BufferedImage createCanvasForBatik(int w, int h) { 
    ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); 
    int[] nBits = {8, 8, 8, 8}; // ?? 
    ComponentColorModel colorModel = new ComponentColorModel(cs, nBits, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE); 
    WritableRaster raster = Raster.createBandedRaster(
     DataBuffer.TYPE_BYTE, w, h, 4, new Point(0,0)); 
    isPremultiplied = false; 
    properties = null; 
    return new BufferedImage(colorModel, raster, isPremultiplied, properties); 
} 

這創造了一個字母也爲單獨每種顏色獨立光柵(帶),所以我結束了4條帶(4個光柵),這是一次不可用SWT圖像。 是否可以使用2個波段創建帶狀光柵:一個用於RGB或BRG中的顏色,另一個用於alpha?

+0

進行了交錯的SWT BGR圖像數據嗎?從你的問題的第一部分看來是如此。如果是這樣,製作帶狀光柵沒有多大意義。您需要創建一些自定義的'DataBuffer'和/或'SampleModel',我認爲它們具有交錯BGR存儲和分立的alpha存儲。當然,如果SWT圖像數據允許交錯字母以及... – haraldK

+0

謝謝@haraldK,你很好地概括了我所要求的,也許用更好的詞彙:如何創建這樣的自定義DataBuffer和/或SampleModel ? – Espinosa

回答

1

我不知道詳細SWT,但根據我的理解API文檔的,下面應該工作:

訣竅是使用自定義DataBuffer實現,僞裝成「帶狀」緩衝,但內部使用交錯RGB和單獨的阿爾法陣列的組合進行存儲。這與標準BandedSampleModel很好地工作。您將失去使用此模型通常適用於BufferedImage的特殊(硬件)優化的任何機會,但這不應該因爲您正在使用SWT進行顯示。

我建議你先創建你的SWT圖像,然後在自定義數據緩衝區中將SWT圖像的顏色和alpha數組「包裝」出來。如果你這樣做,蠟染應該直接渲染到你的SWT圖像上,然後你可以扔掉BufferedImage(如果這不切實際,你當然也可以用相反的方法做,但你可能需要公開下面的自定義數據緩衝區類的內部數組,以創建SWT圖像)。

代碼(重要的部分是SWTDataBuffer類和createImage法):

public class SplitDataBufferTest { 
    /** Custom DataBuffer implementation using separate arrays for RGB and alpha.*/ 
    public static class SWTDataBuffer extends DataBuffer { 
     private final byte[] rgb; // RGB or BGR interleaved 
     private final byte[] alpha; 

     public SWTDataBuffer(byte[] rgb, byte[] alpha) { 
      super(DataBuffer.TYPE_BYTE, alpha.length, 4); // Masquerade as banded data buffer 
      if (alpha.length * 3 != rgb.length) { 
       throw new IllegalArgumentException("Bad RGB/alpha array lengths"); 
      } 
      this.rgb = rgb; 
      this.alpha = alpha; 
     } 

     @Override 
     public int getElem(int bank, int i) { 
      switch (bank) { 
       case 0: 
       case 1: 
       case 2: 
        return rgb[i * 3 + bank]; 
       case 3: 
        return alpha[i]; 
      } 
      throw new IndexOutOfBoundsException(String.format("bank %d >= number of banks, %d", bank, getNumBanks())); 
     } 

     @Override 
     public void setElem(int bank, int i, int val) { 
      switch (bank) { 
       case 0: 
       case 1: 
       case 2: 
        rgb[i * 3 + bank] = (byte) val; 
        return; 
       case 3: 
        alpha[i] = (byte) val; 
        return; 
      } 

      throw new IndexOutOfBoundsException(String.format("bank %d >= number of banks, %d", bank, getNumBanks())); 
     } 
    } 

    public static void main(String[] args) { 
     // These are given from your SWT image 
     int w = 300; 
     int h = 200; 
     byte[] rgb = new byte[w * h * 3]; 
     byte[] alpha = new byte[w * h]; 

     // Create an empty BufferedImage around the SWT image arrays 
     BufferedImage image = createImage(w, h, rgb, alpha); 

     // Just to demonstrate that it works 
     System.out.println("image: " + image); 
     paintSomething(image); 
     showIt(image); 
    } 

    private static BufferedImage createImage(int w, int h, byte[] rgb, byte[] alpha) { 
     DataBuffer buffer = new SWTDataBuffer(rgb, alpha); 
     // SampleModel sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, w, h, 4); // If SWT data is RGB, you can use simpler constructor 
     SampleModel sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, w, h, w, 
       new int[] {2, 1, 0, 3}, // Band indices for BGRA 
       new int[] {0, 0, 0, 0}); 

     WritableRaster raster = Raster.createWritableRaster(sampleModel, buffer, null); 
     ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE); 
     return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null); 
    } 

    private static void showIt(final BufferedImage image) { 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       JFrame frame = new JFrame("Test"); 
       frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 

       JLabel label = new JLabel(new ImageIcon(image)); 
       label.setOpaque(true); 
       label.setBackground(Color.GRAY); 
       frame.add(label); 

       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    private static void paintSomething(BufferedImage image) { 
     int w = image.getWidth(); 
     int h = image.getHeight(); 
     int qw = w/4; 
     int qh = h/4; 

     Graphics2D g = image.createGraphics(); 
     g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 

     g.setColor(Color.ORANGE); 
     g.fillOval(0, 0, w, h); 

     g.setColor(Color.RED); 
     g.fillRect(5, 5, qw, qh); 
     g.setColor(Color.WHITE); 
     g.drawString("R", 5, 30); 

     g.setColor(Color.GREEN); 
     g.fillRect(5 + 5 + qw, 5, qw, qh); 
     g.setColor(Color.BLACK); 
     g.drawString("G", 5 + 5 + qw, 30); 

     g.setColor(Color.BLUE); 
     g.fillRect(5 + (5 + qw) * 2, 5, qw, qh); 
     g.setColor(Color.WHITE); 
     g.drawString("B", 5 + (5 + qw) * 2, 30); 

     g.dispose(); 
    } 
} 
+0

Woohaa!有用。我甚至可以使用Apache Batik工作。謝謝!幾乎沒有什麼變化,我不得不公開'rgb'和'alpha'數組,在SWT方面,我必須使用'bufferedImage.getRaster()'而不是'getData()';只有'getRaster()'返回'SWTDataBuffer','getRgb()'和'getAlpha()'返回數組。 – Espinosa

+0

對於任何感興趣的人來說,這裏有工作來源:https://bitbucket.org/espinosa/org.bitbucket.espinosa.svg.icon/src/59f50b81a0ac3521d997e93a752ad351192397a3/src/org/bitbucket/espinosa/svg/icon/swt/BufferedImageToSwtImageDataConverter2。 java?at = alternative-buffimg2swt and https://bitbucket.org/espinosa/org.bitbucket.espinosa.svg.icon/src/59f50b81a0ac3521d997e93a752ad351192397a3/src/org/bitbucket/espinosa/svg/icon/swt/BufferedImageToSwtImageConverter2。 java?at = alternative-buffimg2swt – Espinosa

+0

@Espinosa是的,'getData()'在光柵中創建一個*拷貝數據,而getRaster()返回當前光柵。所以你明確地想在這裏使用'getRaster()'。 – haraldK