2015-07-22 17 views
6

Ich möchte 3D-geprägte aussehen und fühlen, wie in der folgenden Abbildung gezeigt. Ich habe EmbossMaskFilter verwendet, kann aber den Effekt nicht anzeigen (siehe Code unten). Gibt es einen anderen Weg, dies zu tun? oder wie kann ich den EmbossMaskFilter dafür verwenden?Prägen Kanten einer Bildform mit der Tiefe in Android

Erforderliche Ausgabe

enter image description here

Mein Ausgang

enter image description here

Path path2 = new Path(); 
public Paint fillPaint = null; 
// called in constructor 
public void createPath() 
{ 
    //path 2 Big one 
    araay = new Point[]{new Point(144,320),new Point(109,200), new Point(171,308),new Point(178,240),new Point(171,172),new Point(109,282),new Point(144,160)}; 
    AddBeziers(path2, araay, 320, 144); 
    AddLine(path2, 216, 144); 
    AddLine(path2, 216, 216); 
    AddLine(path2, 144, 320); 

    MaskFilter mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f); 
    fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
    fillPaint.setColor(Color.WHITE); 
    fillPaint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); 
    fillPaint.setAntiAlias(true); 
    fillPaint.setDither(true); 
    fillPaint.setStrokeJoin(Paint.Join.ROUND); 
    fillPaint.setStrokeCap(Paint.Cap.ROUND); 
    fillPaint.setStyle(Paint.Style.FILL); 
    paint.setMaskFilter(mEmboss); 
} 

// add lines to the path 
protected Path AddLine(Path path, int endX, int endY) { 
    //path.moveTo(startX, startY); 

    path.lineTo(endX, endY); 
    return path; 
} 

// add curves to the path 
protected Path AddBeziers(Path path, Point[] points, int lastX, int lastY) { 

    if (points[0].X != lastX && points[0].Y != lastY) 
     path.moveTo(points[0].X, points[0].Y); 

    int index = 1; 

    path.cubicTo(points[index].X, points[index].Y, points[index + 1].X, 
     points[index + 1].Y, points[index + 2].X, points[index + 2].Y); 
    index = index + 3; 
    path.cubicTo(points[index].X, points[index].Y, points[index + 1].X, 
     points[index + 1].Y, points[index + 2].X, points[index + 2].Y); 

    return path; 
} 

//draw on canvas 
@Override 
public void onDraw(Canvas canvas) { 

    canvas.drawPath(path2, fillPaint); 
    super.onDraw(canvas); 
} 

Antwort

4

Wenn Sie nur Bitmap-Verarbeitung tun wollen (im Gegensatz zu 3D oder Vektoren entgegengesetzt), Ihre beste Wette ist wahrscheinlich:

  1. Generieren Sie eine Schablonenmaske aus Ihrem Puzzlestück,
  2. 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,
  3. Alpha-Blend die Ausgabe von "2" in Originalbild mit der Maske (1.) als Schablone Kanal.

MaskDifference of GaussiansResult

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).

enter image description here enter image description here

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. enter image description here

mScriptBlend.forEachMultiply(mOutAllocation, mInAllocation); 
    mInAllocation.copyTo(mBitmapPuzzle); 
} 

enter image description here

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.

+0

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

+0

Ich werde ein Codebeispiel in ein paar Stunden veröffentlichen. –

+0

Hallo danke für deine tolle Antwort. Ich habe es ausprobiert, aber hatte keine Wirkung. Bitte beachten Sie die Ausgabe unten. – pats

5

Ich denke, Sie werden für Umgebungslicht und Spiegel einen größeren Unschärferadius und kleinere Werte brauchen Hallo ghlight.

Ich spielte damit herum, indem ich eine UI erstellte, um Parameter zu optimieren.

image 1

image 2

Hier ist der Code, den ich verwendet, so dass Sie es ausprobieren können. Ich habe ein einfaches blaues Rechteck verwendet, aber Sie sollten in der Lage sein, das Bild, das Sie zeichnen möchten, einfach zu verbinden.

MainActivity.java:

import android.graphics.EmbossMaskFilter; 
import android.os.Bundle; 
import android.support.v7.app.ActionBarActivity; 
import android.view.Menu; 
import android.view.MenuItem; 
import android.widget.SeekBar; 
import android.widget.TextView; 

public class MainActivity extends ActionBarActivity { 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     final DrawView drawView = (DrawView) findViewById(R.id.drawView); 

     final TextView ambientLightTextView = (TextView) findViewById(R.id.ambientLightTextView); 

     SeekBar ambientLightSeekBar = (SeekBar) findViewById(R.id.ambientLightSeekBar); 
     ambientLightSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 
      @Override 
      public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 
       float value = (float) progress/1000F; 
       ambientLightTextView.setText(Float.toString(value)); 
       drawView.setAmbientLight(value); 
      } 

      @Override 
      public void onStartTrackingTouch(SeekBar seekBar) {} 
      @Override 
      public void onStopTrackingTouch(SeekBar seekBar) {} 
     }); 

     final TextView specularHighlightTextView = (TextView) findViewById(R.id.specularHighlightTextView); 

     SeekBar specularHighlightSeekBar = (SeekBar) findViewById(R.id.specularHighlightSeekBar); 
     specularHighlightSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 
      @Override 
      public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 
       float value = (float) progress/100f; 
       specularHighlightTextView.setText(Float.toString(value)); 
       drawView.setSpecularHighlight(value); 
      } 

      @Override 
      public void onStartTrackingTouch(SeekBar seekBar) {} 
      @Override 
      public void onStopTrackingTouch(SeekBar seekBar) {} 
     }); 

     final TextView blurRadiusTextView = (TextView) findViewById(R.id.blurRadiusTextView); 

     SeekBar blurRadiusSeekBar = (SeekBar) findViewById(R.id.blurRadiusSeekBar); 
     blurRadiusSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 
      @Override 
      public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 
       float value = (float) progress/5F; 
       blurRadiusTextView.setText(Float.toString(value)); 
       drawView.setBlurRadius(value); 
      } 

      @Override 
      public void onStartTrackingTouch(SeekBar seekBar) {} 
      @Override 
      public void onStopTrackingTouch(SeekBar seekBar) {} 
     }); 
    } 

} 

DrawView.java:

import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.EmbossMaskFilter; 
import android.graphics.Paint; 
import android.graphics.Rect; 
import android.util.AttributeSet; 
import android.view.View; 

public class DrawView extends View { 

    private Rect rect = new Rect(); 

    private Paint paint; 

    private EmbossMaskFilter filter; 

    private float[] mLightSource = new float[] {2, 5, 5}; 

    private float mAmbientLight = 0.5F; 

    private float mSpecularHighlight = 8F; 

    private float mBlurRadius = 15f; 

    public DrawView(Context context) { 
     super(context); 
     init(); 
    } 

    public DrawView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     init(); 
    } 

    public DrawView(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
     init(); 
    } 

    private void init() { 

     setLayerType(LAYER_TYPE_SOFTWARE, null); 

     filter = new EmbossMaskFilter(mLightSource, mAmbientLight, mSpecularHighlight, mBlurRadius); 

     paint = new Paint(); 
     paint.setColor(0xFF0000FF); 
     paint.setStyle(Paint.Style.FILL); 
     paint.setAntiAlias(true); 
     paint.setDither(true); 
     paint.setMaskFilter(filter); 
    } 

    @Override 
    protected void onDraw(Canvas canvas) { 
     super.onDraw(canvas); 

     rect.left = getWidth()/3; 
     rect.right = rect.left * 2; 
     rect.top = getHeight()/3; 
     rect.bottom = rect.top * 2; 

     canvas.drawRect(rect, paint); 
    } 

    public void setAmbientLight(float value) { 
     if (value > 1.0F) value = 1.0F; 
     if (value < 0) value = 0; 
     mAmbientLight = value; 
     filter = new EmbossMaskFilter(mLightSource, mAmbientLight, mSpecularHighlight, mBlurRadius); 
     paint.setMaskFilter(filter); 
     invalidate(); 
    } 

    public void setSpecularHighlight(float value) { 
     mSpecularHighlight = value; 
     filter = new EmbossMaskFilter(mLightSource, mAmbientLight, mSpecularHighlight, mBlurRadius); 
     paint.setMaskFilter(filter); 
     invalidate(); 
    } 

    public void setBlurRadius(float value) { 
     mBlurRadius = value; 
     filter = new EmbossMaskFilter(mLightSource, mAmbientLight, mSpecularHighlight, mBlurRadius); 
     paint.setMaskFilter(filter); 
     invalidate(); 
    } 
} 

activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:paddingBottom="@dimen/activity_vertical_margin" 
    android:paddingLeft="@dimen/activity_horizontal_margin" 
    android:paddingRight="@dimen/activity_horizontal_margin" 
    android:paddingTop="@dimen/activity_vertical_margin" 
    tools:context=".MainActivity"> 

    <!-- make sure you match this up with the actual package name in your code --> 
    <com.example.embossmaskfilter.DrawView 
     android:id="@+id/drawView" 
     android:layout_width="200dp" 
     android:layout_height="200dp" 
     android:layout_alignParentTop="true" 
     android:layout_centerHorizontal="true" 
     android:layerType="software" /> 

    <TextView 
     android:id="@+id/textView2" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentStart="true" 
     android:layout_below="@id/drawView" 
     android:layout_centerVertical="true" 
     android:text="Ambient Light" 
     android:textAppearance="?android:attr/textAppearanceSmall" /> 

    <TextView 
     android:id="@+id/ambientLightTextView" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentEnd="true" 
     android:layout_alignTop="@id/textView2" 
     android:text="0.5" 
     android:textAppearance="?android:attr/textAppearanceSmall" /> 

    <SeekBar 
     android:id="@+id/ambientLightSeekBar" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentEnd="true" 
     android:layout_alignParentStart="true" 
     android:layout_below="@+id/textView2" 
     android:max="1000" /> 

    <TextView 
     android:id="@+id/textView3" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentStart="true" 
     android:layout_below="@id/ambientLightSeekBar" 
     android:layout_centerVertical="true" 
     android:text="Specular Highlight" 
     android:textAppearance="?android:attr/textAppearanceSmall" /> 

    <TextView 
     android:id="@+id/specularHighlightTextView" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentEnd="true" 
     android:layout_alignTop="@id/textView3" 
     android:text="0.5" 
     android:textAppearance="?android:attr/textAppearanceSmall" /> 

    <SeekBar 
     android:id="@+id/specularHighlightSeekBar" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentEnd="true" 
     android:layout_alignParentStart="true" 
     android:layout_below="@+id/textView3" 
     android:max="1000" /> 

    <TextView 
     android:id="@+id/textView4" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentStart="true" 
     android:layout_below="@id/specularHighlightSeekBar" 
     android:layout_centerVertical="true" 
     android:text="Blur Radius" 
     android:textAppearance="?android:attr/textAppearanceSmall" /> 

    <TextView 
     android:id="@+id/blurRadiusTextView" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentEnd="true" 
     android:layout_alignTop="@id/textView4" 
     android:text="0.5" 
     android:textAppearance="?android:attr/textAppearanceSmall" /> 

    <SeekBar 
     android:id="@+id/blurRadiusSeekBar" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentEnd="true" 
     android:layout_alignParentStart="true" 
     android:layout_below="@+id/textView4" 
     android:max="1000" /> 

</RelativeLayout> 
+0

Danke Kris für Ihre Zeit, ich glaube nicht, dass die erforderliche Ausgabe überhaupt Prägung verwendet. Ich habe letzte zwei Wochen Experimente gemacht. Der Relieffilter erzeugt dunkle und helle Ränder. Dies ist ein unfairer Vorteil des Spielers, da er die Orte basierend auf der Beleuchtung herausfinden kann. Siehe diesen Link https://code.msdn.microsoft.com/windowsdesktop/C-Jigsaw-Puzzle-Game-5f0b7a75. Sie haben etwas erstellt, das als Alpha-Blendfase bezeichnet wird, aber der Algorithmus verwendet Low-Level-APIs. Ich war am Wandern, wenn es einen Weg gibt, diesen Effekt zu bekommen. Irgendeine Idee? – pats

+0

Weiter Was ich habe, ist eine BItmap, die im PorterDuff-Modus zu einer Form gezeichnet wird. Der Prägungsfilter wird nicht auf eine Bitmap abgesehen von den Linien angewendet. – pats

2

Wenn Sie Dilettantismus in eine Low-Level nicht Angst haben, Bildverarbeitung können Sie convolution matrices verwenden, um einen Relief-Effekt zu erzielen. Sie können die Silhouette Ihres Bildes (d. H. Den Alpha-Kanal) als Eingabe für den Filter verwenden. Zum Beispiel, wenn Sie diese 5x5-Matrix verwenden:

| -2 0 0 0 0 | 
| 0 -1 0 0 0 | 
| 0 0 n 0 0 | * (1/n) 
| 0 0 0 1 0 | 
| 0 0 0 0 2 | 

und es auf solche Bild (die einen Alphakanal):

unprocessed alpha channel

Sie diesen Effekt erhalten:

embossed alpha channel

Alle berechneten Werte wurden um 127 verringert, um sicherzustellen, dass sie im Bereich von 0-255 liegen. Ich habe n = 10 in diesem speziellen Beispiel verwendet. Sie können den Radius manipulieren, indem Sie eine Matrix unterschiedlicher Größe (es ist nicht schwer zu extrapolieren) und die Tiefe verwenden, indem Sie den Wert n anpassen (je größer der Wert, desto feiner der Effekt).

Anhand des ursprünglichen Alphakanals und der berechneten Maske können Sie Offsets festlegen, die auf die jeweiligen Pixel des Originalbilds angewendet werden, wodurch ein Prägeeffekt erzeugt wird.

Hoffe, dass hilft.