2011-01-05 15 views
13

Sto battendo la testa contro un muro qui, e sono abbastanza certo che sto facendo qualcosa di stupido, quindi è tempo di rendere pubblica la mia stupidità.Blending pixels from Two Bitmaps

Sto provando a scattare due immagini, unendole in una terza immagine utilizzando gli algoritmi di fusione standard (Hardlight, softlight, sovrapposizione, moltiplicazione, ecc.).

Poiché Android non dispone di tali proprietà di fusione incorporate, ho seguito il percorso di prendere ogni pixel e combinarli utilizzando un algoritmo. Tuttavia, i risultati sono spazzatura. Di seguito sono riportati i risultati di una semplice miscela multipla (immagini utilizzate e risultati attesi).

BASE: alt text

MISCELA: RESULT alt text

PREVISTO: alt text

IMMONDIZIA RESULT: alt text

Tutto l'aiuto sarebbe apprezzato. Di seguito è riportato il codice, che ho cercato di eliminare tutti i "junk", ma alcuni potrebbero averlo superato. Lo pulirò se qualcosa non è chiaro.

ImageView imageView = (ImageView) findViewById(R.id.ImageView01); 
    Bitmap base = BitmapFactory.decodeResource(getResources(), R.drawable.base); 
    Bitmap result = base.copy(Bitmap.Config.RGB_565, true); 
    Bitmap blend = BitmapFactory.decodeResource(getResources(), R.drawable.blend); 

    IntBuffer buffBase = IntBuffer.allocate(base.getWidth() * base.getHeight()); 
    base.copyPixelsToBuffer(buffBase); 
    buffBase.rewind(); 

    IntBuffer buffBlend = IntBuffer.allocate(blend.getWidth() * blend.getHeight()); 
    blend.copyPixelsToBuffer(buffBlend); 
    buffBlend.rewind(); 

    IntBuffer buffOut = IntBuffer.allocate(base.getWidth() * base.getHeight()); 
    buffOut.rewind(); 

    while (buffOut.position() < buffOut.limit()) { 
     int filterInt = buffBlend.get(); 
     int srcInt = buffBase.get(); 

     int redValueFilter = Color.red(filterInt); 
     int greenValueFilter = Color.green(filterInt); 
     int blueValueFilter = Color.blue(filterInt); 

     int redValueSrc = Color.red(srcInt); 
     int greenValueSrc = Color.green(srcInt); 
     int blueValueSrc = Color.blue(srcInt); 

     int redValueFinal = multiply(redValueFilter, redValueSrc); 
     int greenValueFinal = multiply(greenValueFilter, greenValueSrc); 
     int blueValueFinal = multiply(blueValueFilter, blueValueSrc); 

     int pixel = Color.argb(255, redValueFinal, greenValueFinal, blueValueFinal); 

     buffOut.put(pixel); 
    } 

    buffOut.rewind(); 

    result.copyPixelsFromBuffer(buffOut); 

    BitmapDrawable drawable = new BitmapDrawable(getResources(), result); 
    imageView.setImageDrawable(drawable); 
} 

int multiply(int in1, int in2) { 
    return in1 * in2/255; 
} 

risposta

13

Dopo la riproduzione, penso che il tuo problema abbia a che fare con la manipolazione delle immagini in modalità RGB565. Come discusso in this post, le bitmap apparentemente devono essere in modalità ARGB8888 per manipolare correttamente. Ho ottenuto il risultato atteso su una miscela moltiplicare effettuando le seguenti operazioni:

Resources res = getResources(); 
BitmapFactory.Options options = new BitmapFactory.Options(); 
options.inPreferredConfig = Bitmap.Config.ARGB_8888; 
Bitmap base = BitmapFactory.decodeResource(res, R.drawable.base, options); 
Bitmap blend = BitmapFactory.decodeResource(res, R.drawable.blend, options); 

// now base and blend are in ARGB8888 mode, which is what you want 

Bitmap result = base.copy(Config.ARGB_8888, true); 
// Continue with IntBuffers as before... 

La conversione delle bitmap in modalità ARGB8888 sembrava funzionare per me, almeno con i modelli di test gradiente. Tuttavia, avete solo bisogno di fare dello schermo o moltiplicare, si potrebbe provare anche questo:

// Same image creation/reading as above, then: 
Paint p = new Paint(); 
p.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY)); 
p.setShader(new BitmapShader(blend, TileMode.CLAMP, TileMode.CLAMP)); 

Canvas c = new Canvas(); 
c.setBitmap(result); 
c.drawBitmap(base, 0, 0, null); 
c.drawRect(0, 0, base.getWidth(), base.getHeight(), p); 

Con questo, non state facendo i calcoli per-pixel, ma si sono limitati alle preimpostate PorterDuff.Mode s. Nei miei test rapidi (e sporchi), questo era l'unico modo in cui ero in grado di far funzionare la fusione su immagini non sfumate.

+0

Kevin, grazie per l'aiuto. Non utilizzo le immagini con trasparenza, quindi l'opacità sarebbe sempre 1. Ho aggiornato l'esempio con le immagini e pulito il codice. – MarkPowell

+0

Hmm grazie per le immagini - questo è davvero un risultato spazzatura. Al momento non ho accesso ad un ambiente di sviluppo Android, ma mi chiedo se non abbia qualcosa a che fare con la densità dei pixel delle Bitmap: sembra che le tue Bitmap utilizzino lo spazio colore RGB565, ma sembra dai documenti che Color funziona nello spazio ARGB4444. Non ho analizzato tutti i documenti Bitmap/Color per vedere come/se questa conversione viene gestita automaticamente, ma potrebbe valere la pena di essere verificata fino a quando non posso eseguire alcuni test sulla piattaforma effettiva. –

+0

OK, ho giocato un po 'con esso e sono riuscito a riprodurre il risultato della "spazzatura", e l'ho fatto funzionare in due modi diversi.Cercherò di cambiare la mia risposta originale per riflettere questo. –

4

semplice sovrapposizione che si può fare in questo modo (per semplicità si suppone che bmp1 è uguale o superiore a BMP2):

private Bitmap bitmapOverlay(Bitmap bmp1, Bitmap bmp2) 
{ 
    Bitmap bmOverlay = Bitmap.createBitmap(bmp1.getWidth(), bmp1.getHeight(), bmp1.getConfig()); 
    Canvas canvas = new Canvas(bmOverlay); 
    canvas.drawBitmap(bmp1, 0, 0, null); 
    canvas.drawBitmap(bmp2, 0, 0, null); 
    return bmOverlay; 
} 

Per algoritmi di fusione più complessi, forse si può aiutare se stessi con un po ' disponibili funzioni Bitmap/Canvas.

+0

Grazie, ma stava seguendo questa strada e un'altra discussione che mi ha portato a credere che l'unione complessa non fosse possibile tramite la tela. Ciò ha comportato il tentativo di miscelazione per pixel. – MarkPowell