浮雕边缘的图像形状显示在Android中的深度 [英] Emboss edges of a image shape showing the depth in android

查看:242
本文介绍了浮雕边缘的图像形状显示在Android中的深度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想显示3D浮雕的外观和感觉,如下图所示。我使用EmbossMaskFilter但不能得到它显示效果(见下面的代码)。有不同的方法来做到这一点?



a href =https://i.stack.imgur.com/beBNx.png =nofollow noreferrer>



我的输出

 路径path2 = new Path(); 
public Paint fillPaint = null;
//在构造函数中调用
public void createPath()
{
// path 2大一个
araay = new Point [] {new Point(144,320)新点(109,200),新点(171,308),新点(178,240),新点(171,172),新点(109,282)
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);
}

//向路径添加行
protected路径AddLine(路径路径,int endX,int endY){
//path.moveTo(startX, startY);

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

//向路径添加曲线
protected Path AddBeziers(路径路径,Point [] points,int lastX,int lastY){

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

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 + .X,points [index + 2] .Y);

返回路径;
}

//在画布上绘制
@Override
public void onDraw(Canvas canvas){

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


解决方案

如果你只想做位图处理(而不是3D或向量),你最好的打赌可能是:


  1. 从拼图中生成模板掩码, / li>
  2. 使用



    更新:这里是代码。我试图重用你的变量名,这样更容易理解。该代码尽可能使用Renderscript内在函数,以使事情更快更有趣。

      private Paint fillPaint = null; 
    private路径path2;
    private Bitmap mBitmapIn;
    private Bitmap mBitmapPuzzle;
    private RenderScript mRS;
    private分配mInAllocation;
    private分配mPuzzleAllocation;
    private分配mCutterAllocation;

    private分配mOutAllocation;
    private分配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); //创建笔触宽度为5像素的路径
    c.drawPath(path2,fillPaint); // draw it on canvas

    createScript(); // get renderscripts and Allocations ready

    //将半径为25的高斯模糊应用到我们的绘图
    mScriptBlur.setRadius(25);
    mScriptBlur.setInput(mPuzzleAllocation);
    mScriptBlur.forEach(mOutAllocation);

    //现在应用半径1的模糊化
    mScriptBlur.setRadius(1);
    mScriptBlur.setInput(mPuzzleAllocation);
    mScriptBlur.forEach(mOutAllocation2);

    //从另一个减去一个模糊结果
    mScriptBlend.forEachSubtract(mOutAllocation,mOutAllocation2);

    //现在我们要对结果进行归一化(例如使它使用完整的0-255范围)。
    //为此,我们首先计算我们的图像的直方图
    mScriptHistogram.setOutput(mAllocationHist);
    mScriptHistogram.forEach(mOutAllocation2);

    //将直方图复制到Java数组...
    int [] hist = new int [256 * 4];
    mAllocationHist.copyTo(hist);

    // ...并且从结尾处查找第一个非空bin
    int i; (hist [i * 4] | hist [i * 4 + 1] | hist [i * 4 + 2]),对于(i = 255; i> 1; i--)
    )!= 0)
    break;

    //现在设置LUT,将映射到新的更宽的范围。
    //我们还使用反转图像的机会(255 - )。
    for(int x = 0; x <= i; x ++)
    {
    int val = 255 - x * 255 / i;

    mScriptLUT.setAlpha(x,255); //注意我们总是让它完全不透明
    mScriptLUT.setRed(x,val);
    mScriptLUT.setGreen(x,val);
    mScriptLUT.setBlue(x,val);
    }


    //映射本身。
    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);

    //以下三个分配实际上可以使用createSized(),
    //但代码会更长。
    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:检查其他行程手柄是否会给更舒适的角。 >

    I want to show 3D embossed look and feel as shown in following image. I used EmbossMaskFilter but cannot get it to show the effect (see code below). Is there a different way to do this? or how can I use the EmbossMaskFilter for this.

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

    解决方案

    If you only want to do bitmap processing (as opposed to 3D or vectors), your best bet probably is to:

    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,
    3. Alpha-blend the output of "2" into original image using the mask (1.) as the stencil channel.

    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.

    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 (i.e. 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.

    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():

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

    这篇关于浮雕边缘的图像形状显示在Android中的深度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆