Wenn Sie nur Bitmap-Verarbeitung tun wollen (im Gegensatz zu 3D oder Vektoren entgegengesetzt), Ihre beste Wette ist wahrscheinlich:
- Generieren Sie eine Schablonenmaske aus Ihrem Puzzlestück,
- Verwenden Difference of Gaussians zu verarbeiten (Ich habe in diesem Beispiel Kernel der Größe 12 und 2 Pixel verwendet), normalisieren und invertieren das Ergebnis,
- Alpha-Blend die Ausgabe von "2" in Originalbild mit der Maske (1.) als Schablone Kanal.
UPDATE: hier der Code kommt. Ich habe versucht, Ihre Variablennamen so zu verwenden, dass sie leichter zu verstehen sind. Der Code verwendet, wenn immer möglich, Renderscript-Intrinsics, um Dinge schneller und interessanter zu machen.
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);
Lassen Sie uns eine kurze Pause machen und sehen, was wir bisher haben. Beachten Sie, dass das gesamte Bild auf der linken Seite undurchsichtig ist (d. H. Einschließlich des Raums außerhalb des Puzzles), und wir müssen nun die Form ausschneiden und ihre Kante antialiasieren. Leider funktioniert die Verwendung der Originalform nicht, da sie zu groß ist und zu stark ausschneidet, was zu unangenehmen Artefakten nahe der Kante führt (Abbildung rechts).
Wir haben daher einen anderen Weg zeichnen, diesmal einen schmaleren Hub ... mit
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);
... für eine viel bessere aussehendes Ergebnis. Lassen Sie uns damit ein Hintergrundbild ausblenden.
mScriptBlend.forEachMultiply(mOutAllocation, mInAllocation);
mInAllocation.copyTo(mBitmapPuzzle);
}
dort Hallo! Jetzt nur den Setup-Code von 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));
}
Und schließlich onDraw()
:
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmapPuzzle, 0, 0, fillPaint);
super.onDraw(canvas);
}
ERLEDIGEN: überprüfen, ob anderer Schlaganfall Gehrungen angenehme Ecken geben würde.
Das ist genau das, was ich suchte. Kannst du bitte bitte mehr Informationen dazu geben? Wie kann man das in Java/Android effizient umsetzen? (Ich habe das Pfadobjekt auch.) – pats
Ich werde ein Codebeispiel in ein paar Stunden veröffentlichen. –
Hallo danke für deine tolle Antwort. Ich habe es ausprobiert, aber hatte keine Wirkung. Bitte beachten Sie die Ausgabe unten. – pats