How to draw multiple bitmaps vertically, one below the other - java

I'm trying to draw a bunch of bitmaps vertically one below the other like this :
Original Bitmap :
Result expected :
Code :
int repeater = 4;
Bitmap bitmapTextSticker = ImageUtils.drawable2Bitmap(ResourceUtils.getDrawable(R.drawable.icon));
Bitmap background = Bitmap.createBitmap(bitmapTextSticker.getWidth(), bitmapTextSticker.getHeight() * repeater, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(background);
int top = 0;
for (int i = 0; i < repeater; i++) {
top = (i == 0 ? 0 : top + bitmapTextSticker.getHeight());
canvas.drawBitmap(bitmapTextSticker, 0f, top, null);
}
The above code is working, but the output is cropped.

I'm not sure if I'm missing something in your question, but all you have to do is change the top coordinate for each bitmap when calling drawBitmap(). In your specific code, just add:
top += height;
inside the for loop
Edit: from your updated question, it sounds like a Bitmap scaling issue, not a problem with position. Even your 1st bitmap is cropped. So, you should probably use a different canvas.drawbitmap() method:
public void drawBitmap (Bitmap bitmap, Rect src, RectF dst, Paint paint)
You can specify the dimensions of the destination Rect, which should let it scale properly. So, your code should be:
for(...){
...
RectF dest = new RectF(new Rect(0, top, bitmapTextSticker.getWidth(), bitmapTextSticker.getHeight()));
canvas.drawBitmap(bitmapTextSticker, null, dest, null);
}

The issue was I didn't set the density for the bitmap and the canvas :
ArrayList<Bitmap> bitmaps = new ArrayList<>();
for (int i = 0; i < repeater; i++) bitmaps.add(bmp);
int height = 0;
for (Bitmap map : bitmaps) height += map.getHeight();
Bitmap bitmap = Bitmap.createBitmap(bmp.getWidth(), height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.setDensity(DisplayMetrics.DENSITY_MEDIUM);
bitmap.setDensity(DisplayMetrics.DENSITY_MEDIUM);
int drawHeight = 0;
for (Bitmap map : bitmaps) {
if (map != null) {
canvas.drawBitmap(map, 0, drawHeight, null);
drawHeight += map.getHeight();
}
}
return bitmap;

Related

Bitmap is created with rough edges

So I am using this library https://github.com/uptechteam/MotionViews-Android/ for creating bitmaps, which can be resized and dragged around just like snapchat/instagram stickers. But the problem is that the bitmaps that are generated are not smooth and have rough edges like this:
As you can see the text are somewhat distorted and the background has rough edges. Here is the code for generating the bitmap:
/**
* If reuseBmp is not null, and size of the new bitmap matches the size of the reuseBmp,
* new bitmap won't be created, reuseBmp it will be reused instead
*
* #param textLayer text to draw
* #param reuseBmp the bitmap that will be reused
* #return bitmap with the text
*/
#NonNull
private Bitmap createBitmap(#NonNull TextLayer textLayer, #Nullable Bitmap reuseBmp) {
int boundsWidth = canvasWidth;
int alignment = textLayer.getFont().getAlignment();
int style = textLayer.getFont().getStyle();
// init params - size, color, typeface
if (style == STYLE_UNDERLINE) {
textPaint.setFlags(Paint.UNDERLINE_TEXT_FLAG);
} else {
textPaint.setFlags(0);
}
textPaint.setAntiAlias(true);
textPaint.setDither(false);
textPaint.setElegantTextHeight(true);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setTextSize(textLayer.getFont().getSize() * canvasWidth);
textPaint.setColor(textLayer.getFont().getColor());
textPaint.setTypeface(fontProvider.getTypeface(textLayer.getFont().getTypeface()));
textBackgroundColor = textLayer.getFont().getBackgroundColor();
// drawing text guide : http://ivankocijan.xyz/android-drawing-multiline-text-on-canvas/
// Static layout which will be drawn on canvas
Layout.Alignment layoutAlignment;
if (alignment == ALIGNMENT_LEFT) {
layoutAlignment = Layout.Alignment.ALIGN_NORMAL;
} else if (alignment == ALIGNMENT_RIGHT) {
layoutAlignment = Layout.Alignment.ALIGN_OPPOSITE;
} else {
layoutAlignment = Layout.Alignment.ALIGN_CENTER;
}
StaticLayout sl;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
sl = new StaticLayout(
textLayer.getText(), // - text which will be drawn
textPaint,
boundsWidth, // - width of the layout
layoutAlignment, // - layout alignment
1, // 1 - text spacing multiply
1, // 1 - text spacing add
true); // true - include padding
} else {
StaticLayout.Builder builder = StaticLayout.Builder.obtain(textLayer.getText(), 0, textLayer.getText().length(), textPaint, boundsWidth)
.setAlignment(layoutAlignment)
.setLineSpacing(1, 1)
.setIncludePad(true);
sl = builder.build();
}
// calculate height for the entity, min - Limits.MIN_BITMAP_HEIGHT
int boundsHeight = sl.getHeight();
// create bitmap not smaller than TextLayer.Limits.MIN_BITMAP_HEIGHT
int bmpHeight = (int) (canvasHeight * Math.max(TextLayer.Limits.MIN_BITMAP_HEIGHT,
1.0F * boundsHeight / canvasHeight));
// create bitmap where text will be drawn
Bitmap bmp;
if (reuseBmp != null && reuseBmp.getWidth() == boundsWidth
&& reuseBmp.getHeight() == bmpHeight) {
// if previous bitmap exists, and it's width/height is the same - reuse it
bmp = reuseBmp;
bmp.eraseColor(Color.TRANSPARENT); // erase color when reusing
} else {
bmp = Bitmap.createBitmap(boundsWidth, bmpHeight, Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(bmp);
final Rect rect = new Rect(0, 0, bmp.getWidth(), bmp.getHeight());
final RectF rectF = new RectF(rect);
final Paint textBackgroundPaint = new Paint();
textBackgroundPaint.setFlags(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
textBackgroundPaint.setDither(false);
textBackgroundPaint.setColor(textBackgroundColor);
textBackgroundPaint.setStyle(Paint.Style.FILL_AND_STROKE);
textBackgroundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
canvas.drawRoundRect(rectF, textBackgroundRadius, textBackgroundRadius, textBackgroundPaint);
canvas.drawBitmap(bmp, rect, rect, textBackgroundPaint);
canvas.save();
// move text to center if bitmap is bigger that text
if (boundsHeight < bmpHeight) {
//calculate Y coordinate - In this case we want to draw the text in the
//center of the canvas so we move Y coordinate to center.
float textYCoordinate = (float) (bmpHeight - boundsHeight) / 2;
canvas.translate(0, textYCoordinate);
}
//draws static layout on canvas
sl.draw(canvas);
canvas.restore();
return bmp;
}
I tried using antialias, filterbitmap on the paint but it does nothing. Also tried setting the view's layer type to layer_type_software but the result is the same. How to fix this? Any help would be appreciated.
Please use below code for removing rough edges from bitmap
canvas.drawBitmap(bitmap, x, y, new Paint(Paint.ANTI_ALIAS_FLAG));
use the Paint.ANTI_ALIAS_FLAG flag, this flag is used for removing the jagged edges and smooth the edges

Draw text on bitmap in the bottom left corner

I'm trying to draw some text on bitmap with a fixed position (Bottom left corner) no matter how bitmap size different.
Code below works but, the Text is drawn on the center of the bitmap
public Bitmap drawTextToBitmap(Context gContext,
Bitmap bitmap,
String gText) {
Resources resources = gContext.getResources();
float scale = resources.getDisplayMetrics().density;
android.graphics.Bitmap.Config bitmapConfig =
bitmap.getConfig();
if (bitmapConfig == null) {
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
}
bitmap = bitmap.copy(bitmapConfig, true);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(getResources().getColor(R.color.fujiColor));
paint.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/DS-DIGI.TTF"));
paint.setTextSize((int) (14 * scale));
paint.setShadowLayer(1f, 0f, 1f, getResources().getColor(R.color.fujiShadowColor));
Rect bounds = new Rect();
paint.getTextBounds(gText, 0, gText.length(), bounds);
int x = (bitmap.getWidth() - bounds.width()) / 2;
int y = (bitmap.getHeight() + bounds.height()) / 2;
canvas.drawText(gText, x, y, paint);
return bitmap;
}
What I need is something similar to this :
Thank you.
As mentioned in the official docs, the text is drawn taking the (x,y) values as origin. Change the x,y values. Something along the following lines should work.
int horizontalSpacing = 24;
int verticalSpacing = 36;
int x = horizontalSpacing;//(bitmap.getWidth() - bounds.width()) / 2;
int y = bitmap.getHeight()-verticalSpacing;//(bitmap.getHeight() + bounds.height()) / 2;

Android Bitmap - Crop a circle into semicircle

I'm trying to cut a circle into semicircles using android canvas. The circle is loaded using Bitmap class.
Here's the example:
I've been looking for any solution, especially the ones which enable you to crop a bitmap using coordinates, but to no avail.
Any help is appreciated, thanks before..
I had the same challenge before and I solved it in a simple way, The main idea is simple! Use a Bitmap mask, Fill the pixel you want to save (in this case a pie) with the highest integer value (0xFFFFFFFF), So you can use a bitwiseAND to gain the result color, Other pixels of the mask Bitmap will be a transparent black color (0x00000000), When you're done with the mask, Create the result Bitmap and fill the pixels as the method below does:
public Bitmap applyPieMask(Bitmap src, float startAngle, float sweepAngle) {
int width = src.getWidth();
int height = src.getHeight();
//create bitmap mask with the same dimension of the src bitmap
Bitmap mask = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(mask);
canvas.drawColor(0x00000000);//fill mask bitmap with transparent black!
//init mask paint
Paint maskPaint = new Paint();
maskPaint.setColor(0xFFFFFFFF);//pick highest value for bitwise AND operation
maskPaint.setAntiAlias(true);
//choose entire bitmap as a rect
RectF rect = new RectF(0, 0, width, height);
canvas.drawArc(rect, startAngle, sweepAngle, true, maskPaint);//mask the pie
Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
//combine src color and mask to gain the result color
int color = mask.getPixel(i, j) & src.getPixel(i, j);
result.setPixel(i, j, color);
}
}
return result;
}
And here we go ...
public void doIt(View view) {
ImageView imageView = (ImageView) findViewById(R.id.iv);
Bitmap src = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(src);
canvas.drawColor(Color.BLUE);//fill src bitmap with blue color
imageView.setImageBitmap(applyPieMask(src, -90, 60));
}
Hope you find it helpful

Java - Copy pixels of BufferedImage to list

I made a bitmap class with a list of Pixels. The Pixel class has a position and a color. Now I am trying to get a bitmap from a BufferedImage.
This is the code I have tried:
private static Bitmap fromImage(BufferedImage image) {
Bitmap bitmap = new Bitmap();
for (int x = image.getMinX(); x < image.getWidth(); x++) {
for (int y = image.getMinY(); y < image.getHeight(); y++) {
bitmap.setPixel(new Pixel(RGBAColor.fromColor(
new Color(image.getRGB(x, y))), new Vector2(x, y)));
}
}
return bitmap;
}
public static Bitmap fromImage(String path) {
try {
BufferedImage img = ImageIO.read(Galaxy2D.class.getResource(
"/com/mcmastery/galaxy2d/resources/" + path));
BufferedImage conImg = new BufferedImage(img.getWidth(), img.getHeight(),
BufferedImage.TYPE_INT_ARGB);
conImg.createGraphics().drawImage(img, 0, 0, null);
return Bitmap.fromImage(conImg);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
It works, but when it paints the image to the screen it distorts it.
Original image: http://gyazo.com/0832ebf26f940b6dbf218e43101e63f8.png
Image painted to screen using bitmap methods: http://gyazo.com/c4b776f0e5fa0e54c167c10947582834.png
I have tried DataBufferInt too, and it still produces the same result. With other images, it just changes some pixel's positions to positions close to where they are supposed to be. But with the crosshair above, it turns it black and distorts it.
Not sure what a Bitmap is used for (I can't find it in the JDK7 API).
Maybe you can just use the getSubImage(...) method of the BufferedImage.

Android: padding left a bitmap with white color

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

Categories

Resources