I'm working on Android project. and I'm using View class to drawing bitmap using canvas with matrix.
How can I skew matrix? to be like this:
I'm using this code, but it is not working:
float[] src=new float[]{0,0,0,1,1,1,1,0};
float[] dst=new float[]{0.2f,0,0,1,1,1,0.8f,0};
Paint paint = new Paint();
matrix.reset();
matrix.postTranslate(-width / 2.0f, -height / 2.0f);
matrix.postRotate(0/*(float)(angle*180/Math.PI) ,-width / 2.0f + 50, -height / 2.0f +50*/);
matrix.postScale(scale, scale);
matrix.setPolyToPoly(src, 0, dst, 0, 4);
matrix.postTranslate(position.getX(), position.getY());
canvas.drawBitmap(bitmap, matrix, paint);
canvas.concat(matrix);
use something like this:
class V extends View {
private Bitmap b;
private Matrix m;
public V(Context context) {
super(context);
b = BitmapFactory.decodeResource(getResources(), R.drawable.layer0);
m = new Matrix();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
int bw = b.getWidth();
int bh = b.getHeight();
float[] src = {0, 0, 0, bh, bw, bh, bw, 0};
int DX = 100;
float[] dst = {0 + DX, 0, 0, h, w, h, w - DX, 0};
m.setPolyToPoly(src, 0, dst, 0, 4);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(b, m, null);
}
}
UPDATE to keep the image aspect ratio:
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
int bw = b.getWidth();
int bh = b.getHeight();
RectF src = new RectF(0, 0, bw, bh);
RectF dst = new RectF(0, 0, w, h);
m.setRectToRect(src, dst, ScaleToFit.CENTER);
float[] pts = {0, 0, 0, bh, bw, bh, bw, 0, 0, 0, 0, 0, 0, 0, 0, 0};
m.mapPoints(pts, 8, pts, 0, 4);
int DX = 40;
pts[8] += DX;
pts[14] -= DX;
m.setPolyToPoly(pts, 0, pts, 8, 4);
}
Related
There is an objective to transform quadrilateral region (set by 4 points) of image into rectangle image. With Matrix.setPolyToPoly() and Bitmap.createBitmap() I can align whole image so points form a rectangle but now I dont know where is it's left-top corner so I can't cut it off.
For example let's say 4 points are corners of flag (teal color is just a background of ImageView):
Here is code with hardcoded values to fit flag to 72x48 Bitmap.
int w = 72;
int h = 48;
float src[] = {108, 201,
532, 26,
554, 301,
55, 372};
float dst[] = {0, 0,
w, 0,
w, h,
0, h};
Matrix m = new Matrix();
m.setPolyToPoly(src, 0, dst, 0, dst.length >> 1);
Bitmap b1 = BitmapFactory.decodeResource(getResources(), R.drawable.img);
ImageView iv1 = findViewById(R.id.testImg1);
iv1.setImageBitmap(b1);
Bitmap b2 = Bitmap.createBitmap(b1, 0, 0, b1.getWidth(), b1.getHeight(), m, true);
ImageView iv2 = findViewById(R.id.testImg2);
iv2.setImageBitmap(b2);
//these coordinates are hand-picked
Bitmap b3 = Bitmap.createBitmap(b2, 132, 208, w, h);
ImageView iv3 = findViewById(R.id.testImg3);
iv3.setImageBitmap(b3);
And original image (be sure you save it to res/drawable-nodpi):
As #pskink didn't post his answer, I do it.
To make new Bitmap and save original one - is exactly what I need, so here is solution for me:
int w = 72;
int h = 48;
float srcPoints[] = {108, 201,
532, 26,
554, 301,
55, 372};
float dstPoints[] = {0, 0,
w, 0,
w, h,
0, h};
Matrix m = new Matrix();
m.setPolyToPoly(srcPoints, 0, dstPoints, 0, dstPoints.length >> 1);
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.macedonia);
Bitmap dstBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(dstBitmap);
canvas.clipRect(0, 0, w, h);
canvas.drawBitmap(srcBitmap, m, null);
But if you don't want to use extra memory, use other approach:
class BD extends BitmapDrawable {
Matrix matrix = new Matrix();
int w = 72;
int h = 48;
RectF clip = new RectF(0, 0, w, h);
public BD(Resources res, int resId) {
super(res, BitmapFactory.decodeResource(res, resId));
float src[] = {108,201,532,26,554,301,55,372};
float dst[] = {0,0,w,0,w,h,0,h};
matrix.setPolyToPoly(src, 0, dst, 0, src.length / 2);
}
#Override public int getIntrinsicWidth() {return w;}
#Override public int getIntrinsicHeight() {return h;}
#Override
public void draw(Canvas canvas) {
canvas.clipRect(clip);
canvas.drawBitmap(getBitmap(), matrix, null);
}
}
I want to create app in which will be free-form progressbar like this:
Tell me please how can this be done? Perhaps some kind of example.
Thanks.
you can start from this simple custom view (the main idea is to use PathMeasure#getSegment() method):
class V extends View implements View.OnClickListener {
Path segment = new Path();
Paint paint = new Paint();
PathMeasure pm;
public V(Context context) {
super(context);
setOnClickListener(this);
paint.setColor(0xaa00ff00);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(48);
paint.setStrokeCap(Paint.Cap.ROUND);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
Path src = new Path();
src.moveTo(16.000000f, 1.630000f);
src.lineTo(22.030001f, 1.630000f);
src.cubicTo(23.420000f, 1.620000f, 24.770000f, 2.390000f, 25.469999f, 3.590000f);
src.lineTo(31.469999f, 14.030000f);
src.cubicTo(32.169998f, 15.240000f, 32.169998f, 16.790001f, 31.469999f, 18.000000f);
src.lineTo(25.440001f, 28.440001f);
src.cubicTo(24.740000f, 29.629999f, 23.379999f, 30.379999f, 22.000000f, 30.379999f);
src.lineTo(9.970000f, 30.379999f);
src.cubicTo(8.580000f, 30.379999f, 7.230000f, 29.610001f, 6.530000f, 28.410000f);
src.lineTo(0.530000f, 17.969999f);
src.cubicTo(-0.170000f, 16.760000f, -0.170000f, 15.210000f, 0.530000f, 14.000000f);
src.lineTo(6.560000f, 3.560000f);
src.cubicTo(7.260000f, 2.370000f, 8.620000f, 1.620000f, 10.000000f, 1.630000f);
src.close();
Matrix m = new Matrix();
RectF srcRect = new RectF(0, 0, 32, 32);
RectF dstRect = new RectF(0, 0, w, h);
dstRect.inset(paint.getStrokeWidth() / 2, paint.getStrokeWidth() / 2);
m.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.CENTER);
Path dst = new Path();
src.transform(m, dst);
pm = new PathMeasure(dst, true);
pm.getSegment(0, pm.getLength() / 2, segment, true);
segment.rLineTo(0, 0);
}
void setProgress(float progress) {
segment.reset();
float length = pm.getLength();
float start = progress % length;
float end = (progress + length / 2) % length;
if (start < end) {
pm.getSegment(start, end, segment, true);
} else {
pm.getSegment(start, length, segment, true);
pm.getSegment(0, end, segment, true);
}
segment.rLineTo(0, 0);
invalidate();
}
#Override
public void onClick(View v) {
ObjectAnimator.ofFloat(this, "progress", 0, 10 * pm.getLength()).setDuration(10 * 2500).start();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(segment, paint);
}
}
~
I need to create shadow effect for an image .
private static Bitmap getDropShadow3(Bitmap bitmap) {
if (bitmap==null) return null;
int think = 6;
int w = bitmap.getWidth();
int h = bitmap.getHeight();
int newW = w - (think);
int newH = h - (think);
Bitmap.Config conf = Bitmap.Config.ARGB_8888;
Bitmap bmp = Bitmap.createBitmap(w, h, conf);
Bitmap sbmp = Bitmap.createScaledBitmap(bitmap, newW, newH, false);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Canvas c = new Canvas(bmp);
// Right
Shader rshader = new LinearGradient(newW, 0, w, 0, Color.GRAY, Color.LTGRAY, Shader.TileMode.CLAMP);
paint.setShader(rshader);
c.drawRect(newW, think, w, newH, paint);
// Bottom
Shader bshader = new LinearGradient(0, newH, 0, h, Color.GRAY, Color.LTGRAY, Shader.TileMode.CLAMP);
paint.setShader(bshader);
c.drawRect(think, newH, newW , h, paint);
//Corner
Shader cchader = new LinearGradient(0, newH, 0, h, Color.LTGRAY, Color.LTGRAY, Shader.TileMode.CLAMP);
paint.setShader(cchader);
c.drawRect(newW, newH, w , h, paint);
c.drawBitmap(sbmp, 0, 0, null);
return bmp;
}
i used the above code, and i get two sides (Right, bottom) shadow effect . How can i make the effect in all sides including (top,Left)?
the method that you are using; may cause some problems with a certain type of images (irregular ones)
try this method instead (much much easier to understand and more flexible):
public Bitmap addShadowToBitmap(final Bitmap bm, final int dstHeight, final int dstWidth, int color, int size, float dx, float dy) {
final Bitmap mask = Bitmap.createBitmap(dstWidth, dstHeight, Config.ALPHA_8);
final Matrix scaleToFit = new Matrix();
final RectF src = new RectF(0, 0, bm.getWidth(), bm.getHeight());
final RectF dst = new RectF(0, 0, dstWidth - dx, dstHeight - dy);
scaleToFit.setRectToRect(src, dst, ScaleToFit.CENTER);
final Matrix dropShadow = new Matrix(scaleToFit);
dropShadow.postTranslate(dx, dy);
final Canvas maskCanvas = new Canvas(mask);
final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
maskCanvas.drawBitmap(bm, scaleToFit, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_OUT));
maskCanvas.drawBitmap(bm, dropShadow, paint);
final BlurMaskFilter filter = new BlurMaskFilter(size, Blur.NORMAL);
paint.reset();
paint.setAntiAlias(true);
paint.setColor(color);
paint.setMaskFilter(filter);
paint.setFilterBitmap(true);
final Bitmap ret = Bitmap.createBitmap(dstWidth, dstHeight, Config.ARGB_8888);
final Canvas retCanvas = new Canvas(ret);
retCanvas.drawBitmap(mask, 0, 0, paint);
retCanvas.drawBitmap(bm, scaleToFit, null);
mask.recycle();
return ret;
}
call it through:
addShadowToBitmap(arg0, arg1, arg2, arg3, arg4, arg5, arg6);
and it will return you a bitmap.
hope this will help.
Ahmad's code was not quite right for me. I augmented his code to determine w/h of returned bitmap based on the dx/dy and shadow size. This code worked for me with a bitmap that was a signature of a user.
public Bitmap addShadowToBitmap(final Bitmap bm, int color, int size, int dx, int dy) {
int dstWidth = bm.getWidth() + dx + size/2;
int dstHeight = bm.getHeight() + dy + size/2;
final Bitmap mask = Bitmap.createBitmap(dstWidth, dstHeight, Bitmap.Config.ALPHA_8);
final Canvas maskCanvas = new Canvas(mask);
final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
maskCanvas.drawBitmap(bm, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
maskCanvas.drawBitmap(bm, dx, dy, paint);
final BlurMaskFilter filter = new BlurMaskFilter(size, BlurMaskFilter.Blur.NORMAL);
paint.reset();
paint.setAntiAlias(true);
paint.setColor(color);
paint.setMaskFilter(filter);
paint.setFilterBitmap(true);
final Bitmap ret = Bitmap.createBitmap(dstWidth, dstHeight, Bitmap.Config.ARGB_8888);
final Canvas retCanvas = new Canvas(ret);
retCanvas.drawBitmap(mask, 0, 0, paint);
retCanvas.drawBitmap(bm, 0, 0, null);
mask.recycle();
return ret;
}
I want to scale a Bitmap using Canvas
I need to keep the Bitmap original width and height
and the scaled one aligns center-middle in bound
here is what I have tried:
public Bitmap scale(Bitmap src) {
int width = src.getWidth();
int height = src.getHeight();
float scaledWidth = width * .5f;
float scaledHeight = height * .5f;
Bitmap transBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(transBitmap);
Paint paint = new Paint();
float dx = (width - scaledWidth);
float dy = (height - scaledHeight);
canvas.drawBitmap(src, null, new RectF(dx, dy, scaledWidth, scaledHeight), paint);
Matrix m = new Matrix();
m.setRectToRect(new RectF(0, 0, src.getWidth(), src.getHeight()), new RectF(0, 0, scaledWidth, scaledHeight), Matrix.ScaleToFit.CENTER);
Bitmap output = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), m, true);
//return transBitmap;
return output;
}
I used this method in custom ImageView onTouch:
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setImageBitmap(scaledBmp);
break;
case MotionEvent.ACTION_UP:
setImageDrawable(originalDrawable);
break;
}
UPDATE
MyImageView.java
public class MyImageView extends ImageView {
private Drawable mDrawable;
private Drawable mScaled;
public MyImageView(Context context, Drawable drawable) {
super(context);
setImageDrawable(drawable);
Bitmap src = ((BitmapDrawable) drawable).getBitmap();
mDrawable = drawable;
mScaled = new BitmapDrawable(getResources(), makeScaled(src));
}
public Bitmap makeScaled(Bitmap src) {
int width = src.getWidth();
int height = src.getHeight();
float scaledWidth = width * .95f;
float scaledHeight = height * .95f;
Matrix m = new Matrix();
m.setRectToRect(new RectF(0, 0, src.getWidth(), src.getHeight()), new RectF(0, 0, scaledWidth, scaledHeight), Matrix.ScaleToFit.CENTER);
Bitmap output = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), m, true);
Canvas xfas = new Canvas(output);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
xfas.drawBitmap(output, 0, 0, paint);
return output;
}
#Override public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setImageDrawable(mScaled);
break;
case MotionEvent.ACTION_UP:
setImageDrawable(mDrawable);
break;
}
return super.dispatchTouchEvent(event);
}
}
If I have not misunderstood, one solution is to use a Matrix and its setRectToRect to translate the original values to the desired one. From the documentation
Set the matrix to the scale and translate values that map the source
rectangle to the destination rectangle, returning true if the the
result can be represented.
Matrix m = new Matrix();
m.setRectToRect(new RectF(0, 0, src.getWidth(), src.getHeight()), new RectF(0, 0, scaledWidth, scaledHeigth), Matrix.ScaleToFit.CENTER);
Bitmap output = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), m, true);
and then simply draw the scaled bitmap on your canvas
Solved
public static Bitmap scaleBitmap(Bitmap src, float factor) {
int width = src.getWidth();
int height = src.getHeight();
int scaledWidth = (int) (width * factor);
int scaledHeight = (int) (height * factor);
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Bitmap scaled = Bitmap.createScaledBitmap(src, scaledWidth, scaledHeight, false);
Canvas canvas = new Canvas(bmp);
Paint paint = new Paint();
int distX = (width - scaledWidth) / 2;
int distY = (height - scaledHeight) / 2;
canvas.drawBitmap(scaled, distX, distY, paint);
return bmp;
}
In getRoundedCornerBitmap(Context context, Bitmap input, int pixels , int w , int h , boolean squareTL, boolean squareTR, boolean squareBL, boolean squareBR ) method below i attempted to display rounded image but when i try to add a border around the circle nothing is displayed. How can i add border to this rounded method.
public static Bitmap getRoundedCornerBitmap(Context context, Bitmap input, int pixels , int w , int h , boolean squareTL, boolean squareTR, boolean squareBL, boolean squareBR ) {
Bitmap output = Bitmap.createBitmap(w, h, Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final float densityMultiplier = context.getResources().getDisplayMetrics().density;
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, w, h);
final RectF rectF = new RectF(rect);
//make sure that our rounded corner is scaled appropriately
final float roundPx = pixels*densityMultiplier;
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
//draw rectangles over the corners we want to be square
if (squareTL ){
canvas.drawRect(0, 0, w/2, h/2, paint);
}
if (squareTR ){
canvas.drawRect(w/2, 0, w, h/2, paint);
}
if (squareBL ){
canvas.drawRect(0, h/2, w/2, h, paint);
}
if (squareBR ){
canvas.drawRect(w/2, h/2, w, h, paint);
}
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(input, 0,0, paint);
return output;
}
Update now the circle is cut from left and bottom. How can i get a full rounded image
Try below code :-
int w = bitmap.getWidth();
int h = bitmap.getHeight();
int radius = Math.min(h / 2, w / 2);
Bitmap output = Bitmap.createBitmap(w + 8, h + 8, Config.ARGB_8888);
Paint p = new Paint();
p.setAntiAlias(true);
Canvas c = new Canvas(output);
c.drawARGB(0, 0, 0, 0);
p.setStyle(Style.FILL);
c.drawCircle((w / 2) + 4, (h / 2) + 4, radius, p);
p.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
c.drawBitmap(bitmap, 4, 4, p);
p.setXfermode(null);
p.setStyle(Style.STROKE);
p.setColor(Color.WHITE);
p.setStrokeWidth(3);
c.drawCircle((w / 2) + 4, (h / 2) + 4, radius, p);
return output;
see below link for more info:-
How to add a shadow and a border on circular imageView android?
Adding a round frame circle on rounded bitmap
or use below code :-
imageViewUser.setImageBitmap(new GraphicsUtil().getCircleBitmap(GraphicsUtil.decodeSampledBitmapFromResource(filePath,120, 120))); // filepath is your image path
GraphicsUtil.java
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
public class GraphicsUtil
{
/*
* Draw image in circular shape Note: change the pixel size if you want
* image small or large
*/
public Bitmap getCircleBitmap(Bitmap bitmap)
{
Bitmap output;
Canvas canvas = null;
final int color = 0xffff0000;
final Paint paint = new Paint();
Rect rect = null;
if (bitmap.getHeight() > 501)
{
output = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888);
canvas = new Canvas(output);
rect = new Rect(0, 0, 500, 500);
}
else
{
//System.out.println("output else =======");
bitmap = Bitmap.createScaledBitmap(bitmap, 500, 500, false);
output = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888);
canvas = new Canvas(output);
rect = new Rect(0, 0, 500, 500);
}
final RectF rectF = new RectF(rect);
paint.setAntiAlias(true);
paint.setDither(true);
paint.setFilterBitmap(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawOval(rectF, paint);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth((float) 1);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
{
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth)
{
// Calculate ratios of height and width to requested height and
// width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will
// guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(String path, int reqWidth, int reqHeight)
{
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path, options);
}
}