如何不重复使用BitmapShader [英] How to use BitmapShader without repeat

查看:96
本文介绍了如何不重复使用BitmapShader的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个当用户触摸智能手机屏幕(在绘图Android应用程序上工作)时淹没在屏幕上的心形。因此,我将BitmapShader设置为以下代码

  ///通过从资源文件夹
中加载图像来初始化位图对象Bitmap fillBMP = BitmapFactory .decodeResource(context.getResources(),R.drawable.heart);
fillBMP = Bitmap.createScaledBitmap(fillBMP,20,20,false);
//使用Bitmap对象初始化BitmapShader并设置纹理图块模式
shader = new BitmapShader(fillBMP,Shader.TileMode.REPEAT,Shader.TileMode.REPEAT);

然后我指定着色器绘制对象。

  paint.setShader(preset.shader); 

我还设置了触摸监听器来跟踪用户手指。

  Path path = new Path(); 
path.moveTo(mid1.x,mid1.y);
path.quadTo(midmid.x,midmid.y,mid2.x,mid2.y);
canvas.drawPath(path,paint);

这给我这个



其中一些心形被剪裁并且也被重复。我不想要的东西是重复的,永远不会像这样裁剪。



感谢

解决方案

我在上传到Git时遇到了一些麻烦,所以现在将解决方案发布在这里。 / p>

这是我编写的帮助程序类,用于创建所需的效果。我在网上添加了注释,以解释我在做什么。

  import android.content.Context; 
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;

//将此类与Canvas结合使用以创建所需的效果。
公共类CurvedBitmapDrawer
{
private Context mContext;
私人Paint mPaint;
private int mResourceId;
私人位图mBitmap;
专用路径mPath;
private int mBitmapMargin;

//使用上下文创建,以便此类可以使用资源ID。
public CurvedBitmapDrawer(Context context){
mContext = context;
mPath = new Path();
}

//油漆的吸气剂。
//此绘制将用于绘制位图,绘制
//中的strokeWidth值将用于设置绘制的曲线/线的粗细。
public Paint getPaint(){
return mPaint;
}

public void setPaint(Paint paint){
mPaint = paint;
}

//获取器设置位图之间的空间。
public int getBitmapMargin()
{
return mBitmapMargin;
}

public void setBitmapMargin(int bitmapMargin)
{
mBitmapMargin = bitmapMargin;
}

//获取ID的获取方法setb
public int getResourceId(){
return mResourceId;
}

public void setResourceId(int resourceId)
{
mResourceId = resourceId;
mBitmap = null;
}

//位图的可选可选getter设置程序。
公共位图getBitmap()
{
return mBitmap;
}

public void setBitmap(Bitmap bitmap)
{
mBitmap =位图;
mResourceId = 0;
}

//我决定只在此处使用本地路径,但可以随意更改。
//调用getPath对此类绘制的路径执行操作。
public Path getPath()
{
return mPath;
}

//绘制方法。内联评论。
public void draw(Canvas canvas)
{
//获取所需大小的位图。
最终位图scaledBitmap = getScaledBitmap();

//找到位图的中心。
最终浮动中心X = scaledBitmap.getWidth()/ 2;
最终浮动中心Y = scaledBitmap.getHeight()/ 2;

//使用路径测量工具包装路径-PathMeasure
最终PathMeasure pathMeasure = new PathMeasure(mPath,false);

//初始化到位图中心的距离。
浮动距离= scaledBitmap.getWidth()/ 2;

//初始化位置和坡度缓冲区。
float [] position = new float [2];
float [] lope =新的float [2];

浮动坡度;

//绘制,只要路径上的行驶距离不超过
//路径的总距离即可。
while(distance< pathMeasure.getLength())
{
//获取位置&沿路径的特定距离上的斜率(切线)。
pathMeasure.getPosTan(距离,位置,坡度);

//将向量转换为度。
lopeDegree =(float)((Math.atan2(slope [1],slope [0])* 180f)/ Math.PI);

//保留画布的当前状态
canvas.save();

//将画布平移到路径上的位置。
canvas.translate(position [0]-centerX,position [1]-centerY);

//将画布围绕位图的中心旋转所需的度数。
canvas.rotate(slopeDegree,centerX,centerY);

//绘制位图
canvas.drawBitmap(scaledBitmap,0,0,mPaint);

//将位图还原为先前的状态
canvas.restore();

//将距离增加位图的宽度+所需的边距。
距离+ = scaledBitmap.getWidth()+ mBitmapMargin;
}

}

//从指定的资产返回缩放的位图。
私有位图getScaledBitmap()
{
//没有位图或resId,返回null(如果需要,无需特殊处理!添加)。
if(mBitmap == null&&mResourceId == 0)
返回null;

//如果未指定位图,请从资源ID中创建一个。
//优化:确保完成后清除位图。
if(mBitmap == null)
mBitmap = BitmapFactory.decodeResource(mContext.getResources(),mResourceId);

//位图的宽度/高度[
float width = mBitmap.getWidth();
float height = mBitmap.getHeight();

//位图的比率
浮动比率=宽度/高度;

//将位图的高度设置为路径的宽度(从绘制对象开始)。
float scaledHeight = mPaint.getStrokeWidth();

//要保持位图的高宽比,请使用height * ratio作为宽度。
float scaledWidth = scaledHeight *比率;

//返回生成的位图,缩放到正确的大小。
返回Bitmap.createScaledBitmap(mBitmap,(int)scaledWidth,(int)scaledHeight,true);
}
}

这里是一个用法示例:

  ImageView image =(ImageView)findViewById(R.id.img); 

CurvedBitmapDrawer抽屉= new CurvedBitmapDrawer(this);

Paint paint = new Paint();

paint.setStrokeWidth(50);

cabinet.setPaint(paint);
抽屉.setResourceId(R.drawable.heart_icon);
抽屉.setBitmapMargin(10);

路径路径=抽屉.getPath();

path.moveTo(80,90);
path.cubicTo(160,470,750,290,440,880);

位图finalBitmap = Bitmap.createBitmap(800,1000,Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(finalBitmap);

cabinet.draw(画布);

image.setImageBitmap(finalBitmap);

我已经在这里对其进行了测试,并且看起来不错,也许减去了一些边缘情况。给出的用法示例如下所示:





请记住在您提供的绘画对象上设置笔划宽度,否则将不会绘制任何内容(可能会导致当前代码出现异常)。



希望这可以帮助您。


I want to create a heart shape drown on screen when user touches the smartphone screen(working on a drawing android app). So i setup the BitmapShader as below code

//Initialize the bitmap object by loading an image from the resources folder
Bitmap fillBMP = BitmapFactory.decodeResource(context.getResources(), R.drawable.heart);
fillBMP = Bitmap.createScaledBitmap(fillBMP, 20, 20, false);                
//Initialize the BitmapShader with the Bitmap object and set the texture tile mode
shader= new BitmapShader(fillBMP, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);

Then i assign the shader paint object.

paint.setShader(preset.shader);

I have also setup the touch listener to track the user finger. On user touch i draw this on canvas object as.

Path path = new Path();
path.moveTo(mid1.x, mid1.y);
path.quadTo(midmid.x, midmid.y, mid2.x, mid2.y);
canvas.drawPath(path, paint);

This give me this

Where some heart shape are croped and also these are repeated. What i want not these are repeated and never croped like this.

Thanks in advance.

解决方案

I had a bit of trouble uploading to Git, so for now, I'll post the solution here.

Here's a helper class I wrote to create the effect you want. I added comments in-line to explain what I'm doing.

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;

// Use this class with a Canvas to create the effect you want.
public class CurvedBitmapDrawer
{
    private Context mContext;
    private Paint mPaint;
    private int mResourceId;
    private Bitmap mBitmap;
    private Path mPath;
    private int mBitmapMargin;

    // Create with a context so that this class can use resource ids.
    public CurvedBitmapDrawer(Context context) {
        mContext = context;
        mPath = new Path();
    }

    // getters setters for paint.
    // this paint will be used to draw the bitmaps, and the strokeWidth value in the paint
    // will be used to set the thickness of the curve / line that is drawn.
    public Paint getPaint() {
        return mPaint;
    }

    public void setPaint(Paint paint) {
        mPaint = paint;
    }

    // getters setters for the space between the bitmaps.
    public int getBitmapMargin()
    {
        return mBitmapMargin;
    }

    public void setBitmapMargin(int bitmapMargin)
    {
        mBitmapMargin = bitmapMargin;
    }

    // getters setters for res id
    public int getResourceId() {
        return mResourceId;
    }

    public void setResourceId(int resourceId)
    {
        mResourceId = resourceId;
        mBitmap = null;
    }

    // alternative optional getters setters for bitmap.
    public Bitmap getBitmap()
    {
        return mBitmap;
    }

    public void setBitmap(Bitmap bitmap)
    {
        mBitmap = bitmap;
        mResourceId = 0;
    }

    // I decided to only use a local path here, but feel free to change it.
    // call getPath to perform actions on the path that is drawn by this class.
    public Path getPath()
    {
        return mPath;
    }

    // draw method. comments inline.
    public void draw(Canvas canvas)
    {
        // grab a bitmap in the desired size.
        final Bitmap scaledBitmap = getScaledBitmap();

        // find the center of the bitmap.
        final float centerX = scaledBitmap.getWidth() / 2;
        final float centerY = scaledBitmap.getHeight() / 2;

        // wrap the path with a measurement tool for paths - PathMeasure
        final PathMeasure pathMeasure = new PathMeasure(mPath, false);

        // initialize the distance to the center of the bitmap.
        float distance = scaledBitmap.getWidth() / 2;

        // initialize position and slope buffers.
        float[] position = new float[2];
        float[] slope = new float[2];

        float slopeDegree;

        // draw so long as the distance traveled on the path isn't longer than
        // the total distance of the path.
        while (distance < pathMeasure.getLength())
        {
            // grab the position & slope (tangent) on a particular distance along the path.
            pathMeasure.getPosTan(distance, position, slope);

            // convert the vector to a degree.
            slopeDegree = (float)((Math.atan2(slope[1], slope[0]) * 180f) / Math.PI);

            // preserve the current state of the canvas
            canvas.save();

            // translate the canvas to the position on the path.
            canvas.translate(position[0] - centerX, position[1] - centerY);

            // rotate the canvas around the center of the bitmap the amount of degrees needed.
            canvas.rotate(slopeDegree, centerX, centerY);

            // draw the bitmap
            canvas.drawBitmap(scaledBitmap, 0, 0, mPaint);

            // revert the bitmap to the previous state
            canvas.restore();

            // increase the distance by the bitmap's width + the desired margin.
            distance += scaledBitmap.getWidth() + mBitmapMargin;
        }

    }

    // returns a scaled bitmap from the asset specified.
    private Bitmap getScaledBitmap()
    {
        // no bitmap or resId, return null (no special handing of this! add if you like).
        if (mBitmap == null && mResourceId == 0)
            return null;

        // if no bitmap is specified, create one from the resource id. 
        // Optimization: be sure to clear the bitmap once done.
        if (mBitmap == null)
            mBitmap = BitmapFactory.decodeResource(mContext.getResources(), mResourceId);

        // width / height of the bitmap[
        float width = mBitmap.getWidth();
        float height = mBitmap.getHeight();

        // ratio of the bitmap
        float ratio = width / height;

        // set the height of the bitmap to the width of the path (from the paint object).
        float scaledHeight = mPaint.getStrokeWidth();

        // to maintain aspect ratio of the bitmap, use the height * ratio for the width.
        float scaledWidth = scaledHeight * ratio;

        // return the generated bitmap, scaled to the correct size.
        return Bitmap.createScaledBitmap(mBitmap, (int)scaledWidth, (int)scaledHeight, true);
    }
}

Here's a usage example:

        ImageView image = (ImageView)findViewById(R.id.img);

        CurvedBitmapDrawer drawer = new CurvedBitmapDrawer(this);

        Paint paint = new Paint();

        paint.setStrokeWidth(50);

        drawer.setPaint(paint);
        drawer.setResourceId(R.drawable.heart_icon);
        drawer.setBitmapMargin(10);

        Path path = drawer.getPath();

        path.moveTo(80, 90);
        path.cubicTo(160, 470, 750, 290, 440, 880);

        Bitmap finalBitmap = Bitmap.createBitmap(800, 1000, Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(finalBitmap);

        drawer.draw(canvas);

        image.setImageBitmap(finalBitmap);

I've tested it here and it seems to work well, minus some edge cases perhaps. Here's what it looks like with the given usage example:

Remember to set the stroke width on the paint object you supply, or else nothing will be drawn (and probably will cause an exception with the current code).

Hope this helps you.

这篇关于如何不重复使用BitmapShader的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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