2010-05-13 11 views
17

Voglio fare una copia (di un'area rettangolare) dei valori ARGB da una fonte BufferedImage in una destinazione BufferedImage. Nessun compositing dovrebbe essere fatto: se copio un pixel con un valore ARGB di 0x8000BE50 (valore alfa a 128), il pixel di destinazione deve essere esattamente 0x8000BE50, ignorando totalmente il pixel di destinazione.Java: come eseguire una copia veloce dei pixel di BufferedImage? (unit test incluso)

Ho una domanda molto precisa e ho fatto un test unitario per mostrare ciò di cui ho bisogno. Il test unitario è completamente funzionale e autonomo e sta passando bene e sta facendo esattamente quello che voglio.

Tuttavia, voglio un efficiente metodo di memoria più veloce e più per sostituire copySrcIntoDstAt (...).

Questo è il punto della mia domanda: non sto cercando di "riempire" l'immagine in un modo più veloce (quello che ho fatto è solo un esempio per fare un test unitario). Tutto quello che voglio è sapere quale sarebbe il modo veloce ed efficiente per farlo (cioè veloce e non creare oggetti inutili).

L'implementazione di proof-of-concept che ho realizzato è ovviamente molto efficiente in termini di memoria, ma è lenta (eseguendo uno getRGB e uno setRGB per ogni pixel).

Schematicamente, ho questo: (dove A indica pixel corrispondenti dal dell'immagine di destinazione prima della copia)

AAAAAAAAAAAAAAAAAAAA 
AAAAAAAAAAAAAAAAAAAA 
AAAAAAAAAAAAAAAAAAAA 
AAAAAAAAAAAAAAAAAAAA 
AAAAAAAAAAAAAAAAAAAA 
AAAAAAAAAAAAAAAAAAAA 

E voglio avere questo:

AAAAAAAAAAAAAAAAAAAA 
AAAAAAAAAAAAAAAAAAAA 
AAAAAAAAAAAAAAAAAAAA 
AAAAAAAAAAAAABBBBAAA 
AAAAAAAAAAAAABBBBAAA 
AAAAAAAAAAAAAAAAAAAA 

dove 'B 'rappresenta i pixel dell'immagine src.

Nota che sto cercando una sostituzione esatta del metodo copySrcIntoDstAt (...), non per un collegamento/preventivo API.

import org.junit.Test; 

import java.awt.image.BufferedImage; 

import static org.junit.Assert.*; 

public class TestCopy { 

    private static final int COL1 = 0x8000BE50; // alpha at 128 
    private static final int COL2 = 0x1732FE87; // alpha at 23 

    @Test 
    public void testPixelsCopy() { 
     final BufferedImage src = new BufferedImage( 5, 5, BufferedImage.TYPE_INT_ARGB); 
     final BufferedImage dst = new BufferedImage(20, 20, BufferedImage.TYPE_INT_ARGB); 
     convenienceFill(src, COL1); 
     convenienceFill(dst, COL2); 
     copySrcIntoDstAt(src, dst, 3, 4); 
     for (int x = 0; x < dst.getWidth(); x++) { 
      for (int y = 0; y < dst.getHeight(); y++) { 
       if (x >= 3 && x <= 7 && y >= 4 && y <= 8) { 
        assertEquals(COL1, dst.getRGB(x,y)); 
       } else { 
        assertEquals(COL2, dst.getRGB(x,y)); 
       } 
      } 
     } 
    } 

    // clipping is unnecessary 
    private static void copySrcIntoDstAt(
      final BufferedImage src, 
      final BufferedImage dst, 
      final int dx, 
      final int dy 
    ) { 
     // TODO: replace this by a much more efficient method 
     for (int x = 0; x < src.getWidth(); x++) { 
      for (int y = 0; y < src.getHeight(); y++) { 
       dst.setRGB(dx + x, dy + y, src.getRGB(x,y)); 
      } 
     } 
    } 

    // This method is just a convenience method, there's 
    // no point in optimizing this method, this is not what 
    // this question is about 
    private static void convenienceFill(
      final BufferedImage bi, 
      final int color 
    ) { 
     for (int x = 0; x < bi.getWidth(); x++) { 
      for (int y = 0; y < bi.getHeight(); y++) { 
       bi.setRGB(x, y, color); 
      } 
     } 
    } 

} 

risposta

20
private static void copySrcIntoDstAt(final BufferedImage src, 
     final BufferedImage dst, final int dx, final int dy) { 
    int[] srcbuf = ((DataBufferInt) src.getRaster().getDataBuffer()).getData(); 
    int[] dstbuf = ((DataBufferInt) dst.getRaster().getDataBuffer()).getData(); 
    int width = src.getWidth(); 
    int height = src.getHeight(); 
    int dstoffs = dx + dy * dst.getWidth(); 
    int srcoffs = 0; 
    for (int y = 0 ; y < height ; y++ , dstoffs+= dst.getWidth(), srcoffs += width) { 
     System.arraycopy(srcbuf, srcoffs , dstbuf, dstoffs, width); 
    } 
} 
+1

+1 molto bello ... Non ho mai pensato utilizzando diversi * * System.arraycopy sarebbe la strada da percorrere. Suppongo che ogni BufferedImage abbia sempre un raster e che non ci sia creazione di oggetti in corso qui a destra !? È davvero bello ma allo stesso tempo sembra un po 'strano: mi sarei aspettato qualcosa di preesistente nel fare il lavoro senza dover fare manualmente un ciclo * for * e System.arraycopy. Ma, sì, davvero bello (l'ho appena provato e mi sembra bello :) – SyntaxT3rr0r

+0

@WizardOfOds Grazie, hai sottolineato "più veloce e più efficiente in termini di memoria" è sempre necessario un qualche tipo di ciclo per spostare gli offset. Manipolando le strutture interne senza fare copie e creando oggetti, dubito che ci sarebbe un metodo non JNI che funzioni meglio. – stacker