I want to know if it's possible to modify bitmap pixels by pixels. I'm not asking for changing bitmap size/scale. What I'm looking for is that I want to increase size of all or some specific pixels of a bitmap.
I tried this
Thread(Runnable {
val newBitmap = Bitmap.createScaledBitmap(bitmap, bitmap.width * 4,
bitmap.height * 4, true);
for (x in 0 until bitmap.getWidth()) {
for (y in 0 until bitmap.getHeight()) {
val pixel = bitmap.getPixel(x, y);
val redValue = Color.red(pixel)
val blueValue = Color.blue(pixel)
val greenValue = Color.green(pixel)
newBitmap.setPixel(x * 4, y * 4, Color.rgb(redValue, greenValue, blueValue))
}
}
runOnUiThread({ imageView.setImageBitmap(newBitmap) })
}).start()
But it has no effect on the bitmap. Any kind of help is highly appreciated.
If you want to pixelate just a part of your bitmap, then you could create a Bitmap of the part which you wish to edit, perform changes on the new bitmap and then overlay the changed bitmap on top of the original bitmap.
So for example, you could pixelate a portion of a Bitmap by doing something along the following lines,
#Override
protected void onCreate(Bundle savedInstanceState) {
...
Bitmap originalBitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.drawabledroid)).getBitmap();
// Pixelate just the top half
int x = 0, y = 0, width = originalBitmap.getWidth(), height = originalBitmap.getHeight()/2;
//Or pixelate a rectangle in the middle
//int x = originalBitmap.getWidth()/2 - 50, y = originalBitmap.getHeight()/2 - 50, width = 100, height = 100;
//Get bitmap with region you want to pixelate
Bitmap original = Bitmap.createBitmap(originalBitmap, x,y,width,height);
int normalWidth = original.getWidth(), normalHeight = original.getHeight();
int smallWidth = normalWidth/15, smallHeight = normalHeight/15;
Bitmap small = getResizedBitmap(original, smallWidth, smallHeight);
Bitmap pixelated = getResizedBitmap(small, normalWidth, normalHeight);
//Overlay the pixelated bitmap on top of the original
Bitmap overlayed = overlayBitmaps(originalBitmap, pixelated,x,y, width, height);
}
public Bitmap getResizedBitmap(Bitmap bm, int newWidth, int newHeight) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
return Bitmap.createBitmap(
bm, 0, 0, width, height, matrix, false);
}
public static Bitmap overlayBitmaps(Bitmap bmp1, Bitmap bmp2, int x, int y, int width, int height) {
Bitmap bmOverlay = Bitmap.createBitmap(bmp1.getWidth(), bmp1.getHeight(), bmp1.getConfig());
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(bmp1, new Matrix(), null);
//If you know the background in transparent or any specific color
//Then you can color the region before overlaying the pixelated bitmap
Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.TRANSPARENT);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
mPaint.setAntiAlias(true);
canvas.drawRect(x,y, x+width,y+height, mPaint);
//Overlay the pixelated bitmap
canvas.drawBitmap(bmp2, x, y, null);
return bmOverlay;
}
Related
I need to change an image, from circle to normal square.
I'm a beginner a few weeks in programming,
I'm 2 days stopped in this code, does anyone know how to change?
I already tried everything, but I could not solve
I do not understand anything about canvas, I've already studied to solve this problem, but I'm still not able to
public static Bitmap getCircleBitmap(Bitmap bitmap) {
final int scaledWidth = 100;
final int scaledHeight = 100;
bitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true);
Bitmap output;
Rect srcRect, dstRect;
float r;
final int width = bitmap.getWidth();
final int height = bitmap.getHeight();
if (width > height) {
output = Bitmap.createBitmap(height, height, Bitmap.Config.ARGB_8888);
int left = (width - height) / 2;
int right = left + height;
srcRect = new Rect(left, 0, right, height);
dstRect = new Rect(0, 0, height, height);
r = height / 2;
} else {
output = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
int top = (height - width) / 2;
int bottom = top + width;
srcRect = new Rect(0, top, width, bottom);
dstRect = new Rect(0, 0, width, width);
r = width / 2;
}
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawCircle(r, r, r, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, srcRect, dstRect, paint);
bitmap.recycle();
return output;
}
public static Bitmap getBitmapFromVectorDrawable(Context context, int drawableId) {
Drawable drawable = AppCompatResources.getDrawable(context, drawableId);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
drawable = (DrawableCompat.wrap(drawable)).mutate();
}
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
}
If you look at your code, towards the end of method getCircleBitmap() you have the below line of code:
canvas.drawCircle(r, r, r, paint);
That's the one drawing the circle image for you using DrawCircle.
What you can do is, replace it with DrawRect. That should do that trick for you.
However, if you need to just draw a square image from a vector then you can use a standard ImageView straightaway and use the vector as image resource.
I'm using the following method to add a watermark logo on top of an existing bitmap:
private Bitmap addWaterMark(Bitmap src) {
int w = src.getWidth();
int h = src.getHeight();
Bitmap result = Bitmap.createBitmap(w, h, src.getConfig());
Canvas canvas = new Canvas(result);
canvas.drawBitmap(src, 0, 0, null);
Bitmap waterMark = BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.menu_logo);
canvas.drawBitmap(waterMark, 0, 0, null);
return result;
}
How can I modify the above code to position the watermark in the bottom right of the bitmap, and to also make its width 33% of the bitmap, while maintaining its aspect ratio?
Ended up getting it to work how I wanted like this:
private Bitmap addWaterMark(Bitmap src) {
int w = src.getWidth();
int h = src.getHeight();
Bitmap result = Bitmap.createBitmap(w, h, src.getConfig());
Canvas canvas = new Canvas(result);
canvas.drawBitmap(src, 0, 0, null);
int watermarkPadding = 50;
Bitmap waterMark = BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.menu_logo);
int newWatermarkWidth = w / 3;
int newWatermarkHeight = (waterMark.getHeight() * newWatermarkWidth) / waterMark.getWidth();
Bitmap newWatermark = Bitmap.createScaledBitmap(waterMark, newWatermarkWidth, newWatermarkHeight, true);
canvas.drawBitmap(newWatermark, w - newWatermarkWidth - watermarkPadding, h - newWatermarkHeight - watermarkPadding, null);
return result;
}
Hope this helps.
private Bitmap addWaterMark(Bitmap src) {
int w = src.getWidth();
int h = src.getHeight();
Bitmap result = Bitmap.createBitmap(w, h, src.getConfig());
Canvas canvas = new Canvas(result);
canvas.drawBitmap(src, 0, 0, null);
float scale = 0.33f;
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
matrix.postTranslate(w - w * scale, h - h * scale);
Bitmap waterMark = BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.menu_logo);
canvas.drawBitmap(waterMark, matrix, null);
return result;
}
You can use this library, you can draw you watermark in XML (view) and convert to Bitmap and add on your original.
private Bitmap generateWaterMark(Bitmap src) {
//src is your original image
WaterMark waterMark = new WaterMark(mContext);
//return the your original image with watermark added
return waterMark.getImageWaterMarkFromView(src, R.layout.watermark_all);
}
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;
}
I've got two ImageViews within a RelativeLayout. One ImageView is draggable and scalable upon the other ImageView. This all works fine except when I try to export those ImageViews to a bitmap and add it to a canvas.
I can't figure out how to calculate the scale and dragged position of view/bitmap B.
ImageView A functions as a background.
ImageView B is draggable and scalable on top of ImageView A.
Here is a code example
public static Bitmap MergeBitmaps(View v, View ViewA, Bitmap bitmapA, View viewB, Bitmap bitmapB) {
Bitmap b = Bitmap.createBitmap(
bitmapA.getWidth(), bitmapA.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
float scaleFactor = (float) ViewA.getHeight() / (float) bitmapA.getHeight();
float scaleFactorWidth = (float) ViewA.getWidth() / (float)bitmapA.getWidth();
int test2[] = new int[2];
viewB.getLocationOnScreen(test2);
//Rect rect1 = new Rect();
//selectedImg.getDrawingRect(rect1);
//Rect rect2 = new Rect();
//logo.getDrawingRect(rect2);
//int logoHeight = logo.getHeight();
//int logoWidth = logo.getWidth();
//int logoLeft = logo.getLeft();
//int selectedImgLeft = selectedImg.getLeft();
float scaleFactorMid = (scaleFactor + scaleFactorWidth)/2;
c.drawBitmap(bitmapA, 0, 0, null);
//c.drawBitmap(logoBitmap, test2[0]*scaleFactorWidth, test2[1]*scaleFactor, null);
bitmapB = getResizedBitmap(bitmapB, (int)(bitmapB.getHeight()*scaleFactorMid), (int)(bitmapB.getWidth()*scaleFactorMid));
c.drawBitmap(bitmapB, bitmapA.getWidth()/2, bitmapA.getHeight()/2, null);
//v.layout(0, 0, v.getWidth(), v.getHeight());
//selectedImg.draw(c);
return b;
}
I tried calculating the scale factor by dividing the view's width/height and the Bitmaps width/height.
I tried a lot more as you can see. Can anyone point me in the right direction?
For your reference below function are helpful,(In this i scaled bitmap statically, you can move the code).
public void mergeTwoBitmap() {
ImageView iv1 = (ImageView) findViewById(R.id.imageView1);
ImageView iv2 = (ImageView) findViewById(R.id.imageView2);
Bitmap bitmap1 = ((BitmapDrawable) iv1.getDrawable()).getBitmap();
Bitmap bitmap2 = ((BitmapDrawable) iv2.getDrawable()).getBitmap();
float img2X = iv2.getX(), img2Y = iv2.getY();
float img2Width = bitmap2.getWidth(), img2Height = bitmap2.getHeight(); // Original Width And Height
bitmap2 = Bitmap.createScaledBitmap(bitmap2, 300, 300, true);
float img2NewX, img2NewY;
img2NewX = img2X + (img2Width - bitmap2.getWidth()) * 0.5f; // here bitmap2.getWidth() after scale bitmap1
img2NewY = img2Y + (img2Height - bitmap2.getHeight()) * 0.5f; // here bitmap2.getHeight() after scale bitmap2
Bitmap b = mergeBitmap(iv1.getWidth(), iv1.getHeight(), bitmap1,
bitmap2, img2NewX, img2NewY);
}
private Bitmap mergeBitmap(float w, float h, Bitmap bmp1, Bitmap bmp2,
float x, float y) {
Bitmap cs = null;
int width = (int) w, height = (int) h;
cs = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas comboImage = new Canvas(cs);
comboImage.drawBitmap(
Bitmap.createScaledBitmap(bmp1, width, height, true), 0f, 0f,
null);
comboImage.drawBitmap(bmp2, x, y, null);
return cs;
}
I create the Layout programatically in onCreate:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
myView = new MyView(SketchActivity.this, layout.getWidth(), layout.getHeight());
layout2.addView(myView);
layout2.bringToFront();
}
}, 50);
The View where i create the (mutable?) bitmap:
public MyView(Context c, int width, int height) {
super(c);
WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int w = display.getWidth(); // deprecated
int h = display.getHeight();
setFocusable(true);
setBackgroundResource(R.drawable.download);
// setting paint
mPaint = new Paint();
mPaint.setAlpha(0);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mPaint.setAntiAlias(true);
// getting image from resources
Resources r = this.getContext().getResources();
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.smoke);
// converting image bitmap into mutable bitmap
bitmap = bm.createBitmap(w, h, Config.ARGB_8888);
mCanvas = new Canvas();
mCanvas.setBitmap(bitmap); // drawXY will result on that Bitmap
mCanvas.drawBitmap(bm, 0, 0, null);
}
My onDraw function:
#Override
protected void onDraw(Canvas canvas) {
mCanvas.drawCircle(x, y, r, mPaint);
canvas.drawBitmap(bitmap, 0, 0, null);
super.onDraw(canvas);
}
OnTouchEvent:
#Override
public boolean onTouchEvent(MotionEvent event) {
x = (int) event.getX();
y = (int) event.getY();
r = 20;
// Atlast invalidate canvas
invalidate();
return true;
}
No LogCat Errors
The problem is in myView. when i create the bitmap bitmap = bm.createBitmap(w, h, Config.ARGB_8888); If i put instead of w,h(width and height of screen) a small number Example: bitmap = bm.createBitmap(20, 20, Config.ARGB_8888); it creates a tiny picture. But if I put w and h, then instead of drawing on all the layout, it only draws on a small part. (even if i try: bitmap = bm.createBitmap(800, 1080, Config.ARGB_8888); it still draws on a small part, instead of all the screen. What should i do?
This solved it:
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// CREATE A MATRIX FOR THE MANIPULATION
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
// RECREATE THE NEW BITMAP
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
return resizedBitmap;
}
public MyView(Context c, int width, int height) {
super(c);
WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int w = display.getWidth(); // deprecated
int h = display.getHeight();
setFocusable(true);
setBackgroundResource(R.drawable.download);
// setting paint
mPaint = new Paint();
mPaint.setAlpha(0);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mPaint.setAntiAlias(true);
mPaint.setMaskFilter(new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL));
// getting image from resources
Resources r = this.getContext().getResources();
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.smoke);
Bitmap bm2 = getResizedBitmap(bm, h, w);
// converting image bitmap into mutable bitmap
bitmap = bm2.createBitmap(w, h, Config.ARGB_8888);
mCanvas = new Canvas();
mCanvas.setBitmap(bitmap); // drawXY will result on that Bitmap
mCanvas.drawBitmap(bm2, 0, 0, null);
}
Looks like you're recycling your Bitmap somwhere in the code. There is a problem.
Yes, you should recycle it, but it's always a bit risky.
EDIT
Looks like you're drawing onto Bitmap. If it's not what you're trying to do, then you shouldn't call onDraw manually. Instead call invalidate() when you want to redraw your view.
You need to add an Options data like this:
Options options = new Options();
options.isMutable = true; // works from api 11
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.smoke, options);