I'm trying to merge two bitmaps 640 pixel wide each, but the result is always a 1024 wide bitmap. How can i get android to create a 1280 wide bitmap?
I'm using the code below for scaling my bitmaps:
public static Bitmap scaleWidthImage(Bitmap image, int destWidth) {
int origWidth = image.getWidth();
int origHeight = image.getHeight();
int destHeight;
if (origWidth > destWidth) {
destHeight = origHeight / (origWidth / destWidth);
} else {
destHeight = origHeight * (destWidth / origWidth);
}
image = Bitmap.createScaledBitmap(image, destWidth, destHeight, false);
return image;
}
And this code for mergin both bitmaps in one
fun mergeBitmap(fr: Bitmap, sc: Bitmap): Bitmap? {
val comboBitmap: Bitmap
val width: Int = fr.width + sc.width
val height: Int = fr.height
comboBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val comboImage = Canvas(comboBitmap)
comboImage.drawBitmap(fr, 0f, 0f, null)
comboImage.drawBitmap(sc, fr.width.toFloat(), 0f, null)
return comboBitmap
}
When the widths of both bitmaps are less than 1024 pixels, the merged bitmap has the desired width, but when the sum of both is greater than 1024, it does not.
I've tried with the two images below :
Image 640x640 :
Image 640x800 :
I've made this method to merge the two bitmaps into one:
public static Bitmap drawBitmapsHorizontally(Bitmap bitmapOne, Bitmap bitmapTwo) {
ArrayList<Bitmap> bitmaps = new ArrayList<>();
bitmaps.add(bitmapOne);
bitmaps.add(bitmapTwo);
int width = 0;
for (Bitmap map : bitmaps)
width += map.getWidth();
// you can set your favorite height (e.g. 720) instead of getting the max height of the two bitmaps.
Bitmap bitmap = Bitmap.createBitmap(width,
Math.max(bitmapOne.getHeight(),bitmapTwo.getHeight()),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.setDensity(DisplayMetrics.DENSITY_MEDIUM);
bitmap.setDensity(DisplayMetrics.DENSITY_MEDIUM);
canvas.drawBitmap(bitmapOne, 0f, 0f, null);
canvas.drawBitmap(bitmapTwo, bitmapOne.getWidth(), 0f, null);
return bitmap;
}
Result 1280x800 :
Related
I'm trying to draw a bitmap on the top of another bitmap like this :
I'm using the following code to create an empty background with 420x420 as size, and draw the star on it :
for (int i = 0; i < 10; i++) {
Bitmap resized;
if (stretchedPosition.contains(i)) {
resizedStar = Bitmap.createScaledBitmap(star, star.getWidth() - 80, star.getHeight() + 80, true);
} else
resizedStar = Bitmap.createScaledBitmap(star, star.getWidth() + 80, star.getHeight() - 80, true);
resized = makeBackground(resized);
//code for generating a GIF from bitmaps
}
public Bitmap makeBackground(Bitmap resized) {
Bitmap emptyBitmap = Bitmap.createBitmap(420, 420, Bitmap.Config.ARGB_8888);
int positionLeft = 0;
int positionTop = 0;
Bitmap newBitmap = Bitmap.createBitmap(emptyBitmap.getWidth(), emptyBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
canvas.drawBitmap(emptyBitmap, positionLeft, positionTop, null);
canvas.drawColor(Color.GREEN);
int bitmap1Width = resized.getWidth();
int bitmap1Height = resized.getHeight();
int bitmap2Width = emptyBitmap.getWidth();
int bitmap2Height = emptyBitmap.getHeight();
float marginLeft = (float) (bitmap1Width * 0.5 - bitmap2Width * 0.5);
float marginTop = (float) (bitmap1Height * 0.5 - bitmap2Height * 0.5);
canvas.drawBitmap(resized, new Matrix(), null);
canvas.drawBitmap(emptyBitmap, marginLeft, marginTop, null);
return newBitmap;
}
THE ISSUE:
As you can see here, the girl image is not centered and the image gets cut as well.
This may helps you;
Bitmap fileBitmap = BitmapFactory.decodeFile(filePath);
Bitmap blankBitmap = getBlankBitmap(YOUR_WIDTH, YOUR_HEIGHT);
Bitmap mergeBitmap = overlay(blankBitmap, fileBitmap);
you can generate blank bitmap using following method:
public static Bitmap getBlankBitmap(int w, int h) {
Bitmap.Config conf = Bitmap.Config.ARGB_8888; // see other conf types
Bitmap bmp = Bitmap.createBitmap(w, h, conf); // this creates a MUTABLE bitmap
Canvas canvas = new Canvas(bmp);
return bmp;
}
and Merge two bitmap like this;
public static Bitmap overlay(Bitmap bmp1, Bitmap bmp2) {
Bitmap bmOverlay = Bitmap.createBitmap(bmp1.getWidth(), bmp1.getHeight(), bmp1.getConfig());
int padding = (bmp1.getWidth() / 2) - (bmp2.getWidth() / 2);
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(bmp1, new Matrix(), null);
canvas.drawBitmap(bmp2, padding, 0, null);
bmp1.recycle();
bmp2.recycle();
return bmOverlay;
}
I have bitmaps which are squares or rectangles. I take the shortest side and do something like this:
int value = 0;
if (bitmap.getHeight() <= bitmap.getWidth()) {
value = bitmap.getHeight();
} else {
value = bitmap.getWidth();
}
Bitmap finalBitmap = null;
finalBitmap = Bitmap.createBitmap(bitmap, 0, 0, value, value);
Then I scale it to a 144 x 144 Bitmap using this:
Bitmap lastBitmap = null;
lastBitmap = Bitmap.createScaledBitmap(finalBitmap, 144, 144, true);
Problem is that it crops the top left corner of the original bitmap, Anyone has the code to crop the center of the bitmap?
This can be achieved with: Bitmap.createBitmap(source, x, y, width, height)
if (srcBmp.getWidth() >= srcBmp.getHeight()){
dstBmp = Bitmap.createBitmap(
srcBmp,
srcBmp.getWidth()/2 - srcBmp.getHeight()/2,
0,
srcBmp.getHeight(),
srcBmp.getHeight()
);
}else{
dstBmp = Bitmap.createBitmap(
srcBmp,
0,
srcBmp.getHeight()/2 - srcBmp.getWidth()/2,
srcBmp.getWidth(),
srcBmp.getWidth()
);
}
While most of the above answers provide a way to do this, there is already a built-in way to accomplish this and it's 1 line of code (ThumbnailUtils.extractThumbnail())
int dimension = getSquareCropDimensionForBitmap(bitmap);
bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension);
...
//I added this method because people keep asking how
//to calculate the dimensions of the bitmap...see comments below
public int getSquareCropDimensionForBitmap(Bitmap bitmap)
{
//use the smallest dimension of the image to crop to
return Math.min(bitmap.getWidth(), bitmap.getHeight());
}
If you want the bitmap object to be recycled, you can pass options that make it so:
bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
From: ThumbnailUtils Documentation
public static Bitmap extractThumbnail (Bitmap source, int width, int
height)
Added in API level 8 Creates a centered bitmap of the desired size.
Parameters source original bitmap source width targeted width
height targeted height
I was getting out of memory errors sometimes when using the accepted answer, and using ThumbnailUtils resolved those issues for me. Plus, this is much cleaner and more reusable.
Have you considered doing this from the layout.xml ? You could set for your ImageView the ScaleType to android:scaleType="centerCrop" and set the dimensions of the image in the ImageView inside the layout.xml.
You can used following code that can solve your problem.
Matrix matrix = new Matrix();
matrix.postScale(0.5f, 0.5f);
Bitmap croppedBitmap = Bitmap.createBitmap(bitmapOriginal, 100, 100,100, 100, matrix, true);
Above method do postScalling of image before cropping, so you can get best result with cropped image without getting OOM error.
For more detail you can refer this blog
Here a more complete snippet that crops out the center of an [bitmap] of arbitrary dimensions and scales the result to your desired [IMAGE_SIZE]. So you will always get a [croppedBitmap] scaled square of the image center with a fixed size. ideal for thumbnailing and such.
Its a more complete combination of the other solutions.
final int IMAGE_SIZE = 255;
boolean landscape = bitmap.getWidth() > bitmap.getHeight();
float scale_factor;
if (landscape) scale_factor = (float)IMAGE_SIZE / bitmap.getHeight();
else scale_factor = (float)IMAGE_SIZE / bitmap.getWidth();
Matrix matrix = new Matrix();
matrix.postScale(scale_factor, scale_factor);
Bitmap croppedBitmap;
if (landscape){
int start = (tempBitmap.getWidth() - tempBitmap.getHeight()) / 2;
croppedBitmap = Bitmap.createBitmap(tempBitmap, start, 0, tempBitmap.getHeight(), tempBitmap.getHeight(), matrix, true);
} else {
int start = (tempBitmap.getHeight() - tempBitmap.getWidth()) / 2;
croppedBitmap = Bitmap.createBitmap(tempBitmap, 0, start, tempBitmap.getWidth(), tempBitmap.getWidth(), matrix, true);
}
Probably the easiest solution so far:
public static Bitmap cropCenter(Bitmap bmp) {
int dimension = Math.min(bmp.getWidth(), bmp.getHeight());
return ThumbnailUtils.extractThumbnail(bmp, dimension, dimension);
}
imports:
import android.media.ThumbnailUtils;
import java.lang.Math;
import android.graphics.Bitmap;
To correct #willsteel solution:
if (landscape){
int start = (tempBitmap.getWidth() - tempBitmap.getHeight()) / 2;
croppedBitmap = Bitmap.createBitmap(tempBitmap, start, 0, tempBitmap.getHeight(), tempBitmap.getHeight(), matrix, true);
} else {
int start = (tempBitmap.getHeight() - tempBitmap.getWidth()) / 2;
croppedBitmap = Bitmap.createBitmap(tempBitmap, 0, start, tempBitmap.getWidth(), tempBitmap.getWidth(), matrix, true);
}
public Bitmap getResizedBitmap(Bitmap bm) {
int width = bm.getWidth();
int height = bm.getHeight();
int narrowSize = Math.min(width, height);
int differ = (int)Math.abs((bm.getHeight() - bm.getWidth())/2.0f);
width = (width == narrowSize) ? 0 : differ;
height = (width == 0) ? differ : 0;
Bitmap resizedBitmap = Bitmap.createBitmap(bm, width, height, narrowSize, narrowSize);
bm.recycle();
return resizedBitmap;
}
public static Bitmap resizeAndCropCenter(Bitmap bitmap, int size, boolean recycle) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
if (w == size && h == size) return bitmap;
// scale the image so that the shorter side equals to the target;
// the longer side will be center-cropped.
float scale = (float) size / Math.min(w, h);
Bitmap target = Bitmap.createBitmap(size, size, getConfig(bitmap));
int width = Math.round(scale * bitmap.getWidth());
int height = Math.round(scale * bitmap.getHeight());
Canvas canvas = new Canvas(target);
canvas.translate((size - width) / 2f, (size - height) / 2f);
canvas.scale(scale, scale);
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
canvas.drawBitmap(bitmap, 0, 0, paint);
if (recycle) bitmap.recycle();
return target;
}
private static Bitmap.Config getConfig(Bitmap bitmap) {
Bitmap.Config config = bitmap.getConfig();
if (config == null) {
config = Bitmap.Config.ARGB_8888;
}
return config;
}
val sourceWidth = source.width
val sourceHeight = source.height
val xScale = newWidth.toFloat() / sourceWidth
val yScale = newHeight.toFloat() / sourceHeight
val scale = xScale.coerceAtLeast(yScale)
val scaledWidth = scale * sourceWidth
val scaledHeight = scale * sourceHeight
val left = (newWidth - scaledWidth) / 2
val top = (newHeight - scaledHeight) / 2
val targetRect = RectF(
left, top, left + scaledWidth, top
+ scaledHeight
)
val dest = Bitmap.createBitmap(
newWidth, newHeight,
source.config
)
val mutableDest = dest.copy(source.config, true)
val canvas = Canvas(mutableDest)
canvas.drawBitmap(source, null, targetRect, null)
binding.imgView.setImageBitmap(mutableDest)
I have a 480 x 800 bitmap that I am using for a live wallpaper I am creating. When I test on the emulator the bitmap width and height scales fine but when I test on my Samsung S3 the bitmap width scales fine but the height is too short, shows black rectangle at the bottom. Is there a standard bitmap size I should be working with or is there something wrong in my code?:
public void doDraw(Canvas c) {
c.drawColor(Color.rgb(0,0,0)); // Clear the background.
final int canvasWidth = getScreenWidth();
final int canvasHeight = getScreenHeight();
int imageWidth = mBitmap.getWidth();
int imageHeight = mBitmap.getHeight();
float scaleFactor = Math.min( (float)canvasWidth / imageWidth,
(float)canvasHeight / imageHeight );
Bitmap scaled = Bitmap.createScaledBitmap( mBitmap,
(int)(scaleFactor * imageWidth),
(int)(scaleFactor * imageHeight),
true );
c.drawBitmap(scaled, 0, 0, null);
I think you want Math.max() instead of Math.min()
I want to merge two bitmaps side-by-side into one bitmap. The following code is merge sub-bottom. How do I merge side-by-side into one bitmap ?
public Bitmap mergeBitmap(Bitmap fr, Bitmap sc)
{
Bitmap comboBitmap;
int width, height;
width = fr.getWidth() + sc.getWidth();
height = fr.getHeight();
comboBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas comboImage = new Canvas(comboBitmap);
comboImage.drawBitmap(fr, 0f, 0f, null);
comboImage.drawBitmap(sc, 0f , fr.getHeight(), null);
return comboBitmap;
}
public Bitmap mergeBitmap(Bitmap fr, Bitmap sc)
{
Bitmap comboBitmap;
int width, height;
width = fr.getWidth() + sc.getWidth();
height = fr.getHeight();
comboBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas comboImage = new Canvas(comboBitmap);
comboImage.drawBitmap(fr, 0f, 0f, null);
comboImage.drawBitmap(sc, fr.getWidth(), 0f , null);
return comboBitmap;
}
This article goes through the process of combining 2 images one below the other(only works with PNG or JPG ). It will involve passing 2 Bitmaps, which will then get combined using the Canvas class. You can do somme minor changes to get your two images side by side:
public Bitmap combineImages(Bitmap c, Bitmap s) { // can add a 3rd parameter 'String loc' if you want to save the new image - left some code to do that at the bottom
Bitmap cs = null;
int width, height = 0;
if(c.getHeight() > s.getHeight()) {
width = c.getWidth() + s.getWidth(;
height = c.getHeight());
} else {
width = c.getWidth() + s.getWidth();
height = s.getHeight();
}
cs = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas comboImage = new Canvas(cs);
comboImage.drawBitmap(c, 0f, 0f, null);
comboImage.drawBitmap(s, c.getWidth(), 0f, null);
// this is an extra bit I added, just incase you want to save the new image somewhere and then return the location
/*String tmpImg = String.valueOf(System.currentTimeMillis()) + ".png";
OutputStream os = null;
try {
os = new FileOutputStream(loc + tmpImg);
cs.compress(CompressFormat.PNG, 100, os);
} catch(IOException e) {
Log.e("combineImages", "problem combining images", e);
}*/
return cs;
}
How to set all white the 10 rows on the left side of a Bitmap?
I'v got a Bitmap that has to be padded on the left side. I thought i can create a new image iterate on the old one getpixel for each position and setpixel on the new one (white or colored) than return the new bitmap...is this wrong?
Any suggestion? thanks a lot!
You can instead create a new Bitmap with the extra padding number of pixels.
Set this as the canvas bitmap and Color the entire image with the required color and then copy your bitmap.
public Bitmap pad(Bitmap Src, int padding_x, int padding_y) {
Bitmap outputimage = Bitmap.createBitmap(Src.getWidth() + padding_x,Src.getHeight() + padding_y, Bitmap.Config.ARGB_8888);
Canvas can = new Canvas(outputimage);
can.drawARGB(FF,FF,FF,FF); //This represents White color
can.drawBitmap(Src, padding_x, padding_y, null);
return outputimage;
}
public Bitmap addPaddingTopForBitmap(Bitmap bitmap, int paddingTop) {
Bitmap outputBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight() + paddingTop, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(outputBitmap);
canvas.drawColor(Color.RED);
canvas.drawBitmap(bitmap, 0, paddingTop, null);
return outputBitmap;
}
public Bitmap addPaddingBottomForBitmap(Bitmap bitmap, int paddingBottom) {
Bitmap outputBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight() + paddingBottom, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(outputBitmap);
canvas.drawColor(Color.RED);
canvas.drawBitmap(bitmap, 0, 0, null);
return outputBitmap;
}
public Bitmap addPaddingRightForBitmap(Bitmap bitmap, int paddingRight) {
Bitmap outputBitmap = Bitmap.createBitmap(bitmap.getWidth() + paddingRight, bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(outputBitmap);
canvas.drawColor(Color.RED);
canvas.drawBitmap(bitmap, 0, 0, null);
return outputBitmap;
}
public Bitmap addPaddingLeftForBitmap(Bitmap bitmap, int paddingLeft) {
Bitmap outputBitmap = Bitmap.createBitmap(bitmap.getWidth() + paddingLeft, bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(outputBitmap);
canvas.drawColor(Color.RED);
canvas.drawBitmap(bitmap, paddingLeft, 0, null);
return outputBitmap;
}
Here's a kotlin extension function with RxJava to get it done. I haven't tested completely but based combined the previous answers to get something
fun Bitmap.pad(top: Float = 0F, bottom: Float = 0F, left: Float = 0F, right: Float = 0F): Single<Bitmap> {
return Single.create<Bitmap> { emitter ->
val output = Bitmap.createBitmap(
(width + left + right).toInt(),
(height + top + bottom).toInt(),
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(output)
canvas.drawBitmap(this, left, top, null)
emitter.onSuccess(output)
}.subscribeOn(Schedulers.computation())
}
I think the coroutine version would simply be
suspend fun Bitmap.pad(top: Float = 0F, bottom: Float = 0F, left: Float = 0F, right: Float = 0F): Bitmap {
val output = Bitmap.createBitmap(
(width + left + right).toInt(),
(height + top + bottom).toInt(),
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(output)
canvas.drawBitmap(this, left, top, null)
return output
}
I just did this to give padding from all side. Hope it will help someone.
Combination of https://stackoverflow.com/a/44060669/6480433 & https://stackoverflow.com/a/6957333/6480433 these answer.
Bitmap outputimage = Bitmap.createBitmap(Src.getWidth() + padding_x,Src.getHeight() + padding_y, Bitmap.Config.ARGB_8888);
Canvas can = new Canvas(outputimage);
can.drawBitmap(Src, padding_x, padding_y, null);
Bitmap output = Bitmap.createBitmap(outputimage.getWidth()+padding_x, outputimage.getHeight() + padding_y, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
canvas.drawBitmap(outputimage, 0, 0, null);
return output;
You might want to look here:
http://download.oracle.com/javase/1.4.2/docs/api/java/awt/image/BufferedImage.html
methods you might want to use are: getHeight() then you know how many pixels to set and iterate over 10 columns
and setRGB (int x, int y, int RGB) to set the pixel