简体   繁体   English

浮雕图像形状的边缘显示android中的深度

[英]Emboss edges of a image shape showing the depth in android

I want to show 3D embossed look and feel as shown in following image. 我想显示3D浮雕外观和感觉,如下图所示。 I used EmbossMaskFilter but cannot get it to show the effect (see code below). 我使用EmbossMaskFilter但无法让它显示效果(请参阅下面的代码)。 Is there a different way to do this? 有没有不同的方法来做到这一点? or how can I use the EmbossMaskFilter for this. 或者我如何使用EmbossMaskFilter。

Required output 要求的输出

在此输入图像描述

My output 我的输出

在此输入图像描述

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

I think you will need a larger blur radius and smaller values for ambient light and specular highlight. 我认为你需要更大的模糊半径和更小的环境光和镜面高光值。

I played around with this by creating a UI to tweak parameters. 我通过创建一个用于调整参数的UI来解决这个问题。

图片1

图片2

Here is the code I used, so you can try it out. 这是我使用的代码,所以你可以尝试一下。 I used a simple blue rectangle, but you should be able to easily plug in whatever image you want to draw. 我使用了一个简单的蓝色矩形,但你应该能够轻松插入你想要绘制的任何图像。

MainActivity.java: 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: 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: 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>

If you only want to do bitmap processing (as opposed to 3D or vectors), your best bet probably is to: 如果您只想进行位图处理(而不是3D或矢量),最好的选择可能是:

  1. Generate a stencil mask from your puzzle piece, 从拼图中生成模板蒙版,
  2. Use Difference of Gaussians to process it (I used kernels of size 12 and 2 pixels in this example), then normalize and invert the result, 使用高斯差分来处理它(在本例中我使用了大小为12和2像素的内核),然后对结果进行归一化和反演,
  3. Alpha-blend the output of "2" into original image using the mask (1.) as the stencil channel. 使用掩模(1.)作为模板通道,将“2”的输出混合成原始图像。

面具 高斯差异 结果

UPDATE: here comes the code. 更新:这里是代码。 I tried to reuse your variable names so that it's easier to understand. 我试图重用你的变量名,以便更容易理解。 The code uses Renderscript intrinsics whenever possible in order to make things faster and more interesting. 代码尽可能使用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);

Let's make a short break and see what we have so far. 让我们休息一下,看看到目前为止我们有什么。 Observe that the entire image on the left is opaque (ie including the space outside the puzzle), and we now have to cut out the shape and antialias its edge properly. 观察到左边的整个图像是不透明的(即包括拼图外面的空间),我们现在必须正确地剪切其边缘的形状和抗锯齿。 Unfortunately, using original shape won't work, as it is too large and cuts out too much, leading to unpleasant artifacts near the edge (figure on the right). 不幸的是,使用原始形状是行不通的,因为它太大而且切得太多,导致边缘附近出现令人不快的伪影(右图)。

在此输入图像描述 在此输入图像描述

We therefore draw another path, this time using a narrower stroke... 因此,我们绘制了另一条路径,这次是使用更窄的行程......

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

...for a much better looking result. ...为了更好看的结果。 Let's use it to mask out a background image. 我们用它来掩盖背景图像。 在此输入图像描述

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

在此输入图像描述

Hello there! 你好! Now just the Renderscript setup code. 现在只是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));
}

And finally onDraw() : 最后onDraw()

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

TODO: check if other stroke miters would give more pleasant corners. TODO:检查其他中风杆是否会给出更舒适的角落。

If you're not afraid of dabbling into some low-level image processing, you can use convolution matrices to achieve an emboss effect. 如果你不害怕涉及一些低级图像处理,你可以使用卷积矩阵来实现浮雕效果。 You could use your image's silhouette (ie the alpha channel) as an input to the filter. 您可以使用图像的轮廓(即Alpha通道)作为滤镜的输入。 For example, if you use this 5x5 matrix: 例如,如果您使用此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 |

and apply it to such image (representing an alpha channel): 并将其应用于此类图像(表示Alpha通道):

未经处理的alpha通道

you'll get this effect: 你会得到这个效果:

浮雕alpha通道

All of the computed values have been decreased by 127 to ensure that they will be in range of 0-255. 所有计算值都减少了127,以确保它们在0-255的范围内。 I used n = 10 in this particular example. 我在这个特定的例子中使用了n = 10 You can manipulate the radius by using a matrix of different size (it's not hard to extrapolate) and the depth by adjusting the value of n (the bigger the value, the subtler the effect). 您可以通过使用不同大小的矩阵(不难推断)来操纵半径,并通过调整n的值来调整深度(值越大,效果越微妙)。

Given the original alpha channel, and the computed mask you can determine offsets to apply to the respective pixels of the original image, thus creating an emboss effect. 给定原始Alpha通道和计算的掩模,您可以确定要应用于原始图像的各个像素的偏移,从而创建浮雕效果。

Hope that helps. 希望有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM