我想显示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),172),282),new Point(144,160)}; AddBeziers(path2,araay,320,144); AddLine(path2,216,144 ); AddLine(path2,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 + 2].Y); return path; } //draw on canvas @Override public void onDraw(Canvas canvas) { canvas.drawPath(path2,fillPaint); super.onDraw(canvas); }
解决方法
如果您只想进行位图处理(而不是3D或矢量),最好的选择可能是:
>从你的拼图中生成一个模板面具,
>使用Difference of Gaussians来处理它(在本例中我使用了大小为12和2像素的内核),然后标准化并反转结果,
>使用掩模(1.)作为模板通道,将“2”的输出混合到原始图像中.
更新:这里是代码.我试图重用你的变量名,以便更容易理解.代码尽可能使用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,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,fillPaint); super.onDraw(canvas); }
TODO:检查其他中风杆是否会给出更舒适的角落.