【问题标题】:Emboss edges of a image shape showing the depth in android在android中显示深度的图像形状的浮雕边缘
【发布时间】:2015-10-12 07:28:37
【问题描述】:

我想展示如下图所示的 3D 浮雕外观。我使用了 EmbossMaskFilter 但无法让它显示效果(参见下面的代码)。有不同的方法可以做到这一点吗?或者我该如何使用 EmbossMaskFilter。

所需输出

我的输出

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);
}

【问题讨论】:

    标签: android canvas drawing emboss


    【解决方案1】:

    如果您只想进行位图处理(而不是 3D 或矢量),您最好的选择可能是:

    1. 从您的拼图中生成模板蒙版,
    2. 使用Difference of Gaussians 对其进行处理(在此示例中我使用大小为 12 和 2 像素的内核),然后对结果进行归一化和反转,
    3. 使用蒙版 (1.) 作为模板通道,将“2”的输出与原始图像进行 Alpha 混合。

    更新:代码来了。我试图重用你的变量名,以便更容易理解。代码尽可能使用 Renderscript 内部函数,以使事情变得更快、更有趣。

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

    让我们休息一下,看看到目前为止我们有什么。观察左边的整个图像是不透明的(即包括拼图外部的空间),我们现在必须正确地切出形状并消除其边缘。不幸的是,使用原始形状是行不通的,因为它太大并且切割太多,导致边缘附近出现令人不快的伪影(右图)。

    因此,我们绘制了另一条路径,这次使用的是更窄的笔划...

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

    ...为了更好看的结果。让我们用它来掩盖背景图像。

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

    你好!现在只是 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));
    }
    

    最后是onDraw():

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(mBitmapPuzzle, 0, 0, fillPaint);
        super.onDraw(canvas);
    }
    

    TODO:检查其他中风斜接是否会提供更舒适的角落。

    【讨论】:

    • 这正是我所追求的。您能否提供更多有关这方面的信息?如何在 java/android 中有效地实现这一点? (我也有路径对象。)
    • 您好,感谢您的出色回答。我试过了,但没有任何效果。请看下面的输出。
    • stackoverflow.com/questions/32679200/…我为此创建了一个新问题。
    【解决方案2】:

    如果你不怕涉足一些低级的图像处理,你可以使用convolution matrices 来实现浮雕效果。您可以使用图像的轮廓(即 alpha 通道)作为过滤器的输入。例如,如果你使用这个 5x5 矩阵:

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

    并将其应用于此类图像(表示 alpha 通道):

    你会得到这个效果:

    所有计算值都减少了 127,以确保它们在 0-255 的范围内。我在这个特定的例子中使用了n = 10。您可以通过使用不同大小的矩阵(不难推断)来控制半径,通过调整n 的值来控制深度(值越大,效果越微妙)。

    给定原始 Alpha 通道和计算出的蒙版,您可以确定偏移以应用于原始图像的各个像素,从而创建浮雕效果。

    希望对您有所帮助。

    【讨论】:

      【解决方案3】:

      我认为你需要更大的模糊半径和更小的环境光和镜面高光值。

      我通过创建一个 UI 来调整参数来解决这个问题。

      这是我使用的代码,你可以试试看。我使用了一个简单的蓝色矩形,但您应该能够轻松插入您想要绘制的任何图像。

      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>
      

      【讨论】:

      • 感谢 Kris 抽出宝贵时间,我认为所需的输出根本没有使用 emboss。过去两周我做了一些实验。浮雕滤镜会产生暗边和亮边,这对玩家来说是一个不公平的优势,因为他们可以根据光照确定位置。请参阅此链接code.msdn.microsoft.com/windowsdesktop/…。他们创造了一种叫做 alpha blend bevel 的东西,但该算法使用的是低级 API。如果有办法获得这种效果,我正在徘徊。有什么想法吗?
      • 我还有一个使用 PorterDuff 模式绘制到形状的位图。除了线条之外,浮雕过滤器根本不适用于位图。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-06
      • 1970-01-01
      相关资源
      最近更新 更多