我想显示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);
}
答案 0 :(得分:5)
我认为你需要更大的模糊半径和更小的环境光和镜面高光值。
我通过创建用于调整参数的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>
答案 1 :(得分:4)
如果您只想进行位图处理(而不是3D或矢量),最好的选择可能是:
更新:这里是代码。我尝试重用您的变量名称,以便更容易理解。该代码尽可能使用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:检查是否有其他的中风杆可以给出更舒适的角落。
答案 2 :(得分: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通道和计算出的遮罩,您可以确定要应用于原始图像的各个像素的偏移,从而创建浮雕效果。
希望有所帮助。