Se si desidera solo fare l'elaborazione bitmap (al contrario di 3D o vettori), la cosa migliore è probabilmente quello di:
- generare una maschera di stencil dal vostro pezzo di puzzle,
- Usa Difference of Gaussians per elaborare (Ho usato kernel di dimensioni 12 e 2 pixel in questo esempio), quindi normalizzare e invertire il risultato,
- Alpha-blend l'output di "2" nell'immagine originale utilizzando la maschera (1.) come canale stencil.
UPDATE: ecco che arriva il codice. Ho provato a riutilizzare i nomi delle variabili in modo che sia più facile da capire. Il codice utilizza gli elementi intrinseci di Renderscript ogni volta che è possibile per rendere le cose più veloci e più interessanti.
private Paint fillPaint = null;
private Path path2;
private Bitmap mBitmapIn;
private Bitmap mBitmapPuzzle;
private RenderScript mRS;
private Allocation mInAllocation;
private Allocation mPuzzleAllocation;
private Allocation mCutterAllocation;
private Allocation mOutAllocation;
private Allocation mOutAllocation2;
private Allocation mAllocationHist;
private ScriptIntrinsicBlur mScriptBlur;
private ScriptIntrinsicBlend mScriptBlend;
private ScriptIntrinsicHistogram mScriptHistogram;
private ScriptIntrinsicLUT mScriptLUT;
private Context ctx;
private int bw = 780;
private int bh = 780;
private void init()
{
mBitmapIn = loadBitmap(R.drawable.cat7); // background image
mBitmapPuzzle = Bitmap.createBitmap(bw, bh, Bitmap.Config.ARGB_8888); // this will hold the puzzle
Canvas c = new Canvas(mBitmapPuzzle);
path2 = new Path();
createPath(5); // create the path with stroke width of 5 pixels
c.drawPath(path2, fillPaint); // draw it on canvas
createScript(); // get renderscripts and Allocations ready
// Apply gaussian blur of radius 25 to our drawing
mScriptBlur.setRadius(25);
mScriptBlur.setInput(mPuzzleAllocation);
mScriptBlur.forEach(mOutAllocation);
// Now apply the blur of radius 1
mScriptBlur.setRadius(1);
mScriptBlur.setInput(mPuzzleAllocation);
mScriptBlur.forEach(mOutAllocation2);
// Subtract one blur result from another
mScriptBlend.forEachSubtract(mOutAllocation, mOutAllocation2);
// We now want to normalize the result (e.g. make it use full 0-255 range).
// To do that, we will first compute the histogram of our image
mScriptHistogram.setOutput(mAllocationHist);
mScriptHistogram.forEach(mOutAllocation2);
// copy the histogram to Java array...
int []hist = new int[256 * 4];
mAllocationHist.copyTo(hist);
// ...and walk it from the end looking for the first non empty bin
int i;
for(i = 255; i > 1; i--)
if((hist[i * 4] | hist[i * 4 + 1] | hist[i * 4 + 2]) != 0)
break;
// Now setup the LUTs that will map the image to the new, wider range.
// We also use the opportunity to inverse the image ("255 -").
for(int x = 0; x <= i; x++)
{
int val = 255 - x * 255/i;
mScriptLUT.setAlpha(x, 255); // note we always make it fully opaque
mScriptLUT.setRed(x, val);
mScriptLUT.setGreen(x, val);
mScriptLUT.setBlue(x, val);
}
// the mapping itself.
mScriptLUT.forEach(mOutAllocation2, mOutAllocation);
Facciamo una breve pausa e vediamo cosa abbiamo finora. Osserva che l'intera immagine a sinistra è opaca (cioè includendo lo spazio al di fuori del puzzle), e ora dobbiamo ritagliare la forma e antialias correttamente il suo bordo. Sfortunatamente, l'uso della forma originale non funzionerà, poiché è troppo grande e taglia troppo, causando spiacevoli artefatti vicino al bordo (figura a destra).
Abbiamo quindi disegnare un altro percorso, questa volta usando un colpo stretto ...
Bitmap mBitmapCutter = Bitmap.createBitmap(bw, bh, Bitmap.Config.ARGB_8888);
c = new Canvas(mBitmapCutter);
path2 = new Path();
createPath(1); // stroke width 1
c.drawPath(path2, fillPaint);
mCutterAllocation = Allocation.createFromBitmap(mRS, mBitmapCutter);
// cookie cutter now
mScriptBlend.forEachDstIn(mCutterAllocation, mOutAllocation);
... per un risultato molto più bello. Usiamolo per mascherare un'immagine di sfondo.
mScriptBlend.forEachMultiply(mOutAllocation, mInAllocation);
mInAllocation.copyTo(mBitmapPuzzle);
}
Ciao a tutti! Ora solo il codice di installazione di Renderscript.
private void createScript() {
mRS = RenderScript.create(ctx);
mPuzzleAllocation = Allocation.createFromBitmap(mRS, mBitmapPuzzle);
// three following allocations could actually use createSized(),
// but the code would be longer.
mInAllocation = Allocation.createFromBitmap(mRS, mBitmapIn);
mOutAllocation = Allocation.createFromBitmap(mRS, mBitmapPuzzle);
mOutAllocation2 = Allocation.createFromBitmap(mRS, mBitmapPuzzle);
mAllocationHist = Allocation.createSized(mRS, Element.I32_3(mRS), 256);
mScriptBlur = ScriptIntrinsicBlur.create(mRS, Element.U8_4(mRS));
mScriptBlend = ScriptIntrinsicBlend.create(mRS, Element.U8_4(mRS));
mScriptHistogram = ScriptIntrinsicHistogram.create(mRS, Element.U8_4(mRS));
mScriptLUT = ScriptIntrinsicLUT.create(mRS, Element.U8_4(mRS));
}
E infine onDraw()
:
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmapPuzzle, 0, 0, fillPaint);
super.onDraw(canvas);
}
TODO: controllare se altri mitre ictus darebbe più piacevoli angoli.
Questo è esattamente quello che cercavo. Potete per favore fornire maggiori informazioni su questo? Come implementarlo in modo efficiente in java/android? (Ho anche il path object.) – pats
Inserirò un esempio di codice in poche ore. –
Ciao grazie per la tua grande risposta. L'ho provato, ma non ha avuto alcun effetto. Si prega di vedere l'output di seguito. – pats