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
Related
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;
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;
I have an image, and I am trying to overlay a cropped version of the image over the original. Something like this:
Original
Overlay Image
Image after the overlay image is cropped to roughly 50%
Right now, my code below reverses what I want, and returns an image like this:
public void setOverlay(ImageView image, Bitmap originalBitmap, double percentageCompleted) {
int percentHeight;
int height = originalBitmap.getHeight();
Bitmap cropped;
if(percentageCompleted == 0){
cropped = Bitmap.createBitmap(overlay, 0, 0, overlay.getWidth() , 0 );
} else {
percentHeight = (int) Math.floor(height * (percentageCompleted));
Log.d("HEIGHT", Double.toString(height));
Log.d("PERCENT Completed", Double.toString(percentageCompleted));
Log.d("PERCENT HEIGHT", Integer.toString(percentHeight));
cropped = Bitmap.createBitmap(overlay, 0, 0, overlay.getWidth() , height-percentHeight);
}
originalBitmap = overlay(originalBitmap, cropped);
//set imageview to new bitmap
image.setImageBitmap(originalBitmap );
}
public Bitmap overlay(Bitmap bmp1, Bitmap bmp2) {
Bitmap bmp3 = bmp1.copy(Bitmap.Config.ARGB_8888,true);//mutable copy
Canvas canvas = new Canvas(bmp3 );
canvas.drawBitmap(bmp2, new Matrix(), null);
return bmp3;
}
I know I could reverse the image files, but I want to start with the original image and have the overlay draw upwards, rather than starting with the overlay, and drawing the original downwards. Any help is appreciated!
You can use this method:
Canvas.drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
Here is my overlay method that may meet your need:
public static Bitmap overlay(Bitmap base, Bitmap overlay, Float percentage) {
Bitmap resultBitmap = Bitmap.createBitmap(base.getWidth(), base.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(resultBitmap);
Paint paint = new Paint();
// base bitmap
canvas.drawBitmap(base, 0F, 0F, paint);
// overlay bitmap
int yOffset = (int) (percentage * base.getHeight());
Rect rect = new Rect(0, yOffset, overlay.getWidth(), overlay.getHeight());
canvas.drawBitmap(overlay, rect, rect, paint);
return resultBitmap;
}
I call applyEffect in my game logic when the screen is paused, but the image is not set. In its place i get a black background. I instead expect it to show a blurred texture, of the form in image below as expected once I set the gaussian blur shader .
This is sadly the result ;
Why is the screen black?
Here is how i call the method
if (Gdx.input.isKeyPressed(Keys.P) || pause)
{
state = State.GAME_PAUSED;
setBackgroundTexture(applyEffect());
gameScreenGamePauseMenu.sendInMenu();
}
public TextureRegion applyEffect()
{
boolean m_fboEnabled = true;
FrameBuffer m_fbo = null;
float m_fboScaler = 1;
TextureRegion m_fboRegion = null;
int width = Gdx.graphics.getWidth();
int height = Gdx.graphics.getHeight();
if(m_fboEnabled) // enable or disable the supersampling
{
if(m_fbo == null)
{
// m_fboScaler increase or decrease the antialiasing quality
m_fbo = new FrameBuffer(Format.RGB565, (int)(width * m_fboScaler), (int)(height * m_fboScaler), false);
m_fboRegion = new TextureRegion(m_fbo.getColorBufferTexture());
m_fboRegion.flip(false, true);
}
m_fbo.begin();
}
if(m_fbo != null)
{
m_fbo.end();
getStage().getBatch().begin();
getStage().getBatch().draw(m_fboRegion, 0, 0, width, height);
getStage().getBatch().end();
}
return m_fboRegion;
}
Iam using Libgdx to make my game
I have a motion JPEG stream that is put on a canvas in Android. My problem is that at the bottom right of the canvas there is a little, gray, pixelated bar. It kinda of disappears and re-appears. I don't have this issue on other platforms that use the same stream, so i'm guessing its an Android problem. Here is my code:
Canvas canvas = null;
try
{
Bitmap bmp = BitmapFactory.decodeByteArray(notifi.imgData, 0, notifi.imgData.length);
if (bmp == null)
System.out.println("Skipping invalid MJpeg frame");
else
{
canvas = holder.lockCanvas(null);
if (canvas == null)
{
System.out.println("Cannot lock canvas, skipping MJpeg frame");
return;
}
canvas.drawColor(Color.BLACK);
Rect dst = null;
int viewWidth = mPreview.getWidth();
int viewHeight = mPreview.getHeight();
float ratio = bmp.getWidth() / (float)bmp.getHeight();
int desiredHeight = (int)(viewWidth / ratio);
if (desiredHeight > viewHeight)
{ // Letterbox
int maxWidth = (int)(viewHeight * ratio);
int pad = (viewWidth - maxWidth) / 2;
dst = new Rect(pad, 0, maxWidth + pad, viewHeight);
}
else
{
int pad = (viewHeight - desiredHeight) / 2;
dst = new Rect(0, pad, viewWidth, desiredHeight + pad);
}
canvas.drawBitmap(bmp, null, dst, null);
}
} finally {
if (canvas != null)
holder.unlockCanvasAndPost(canvas);
}
}
});
canvas = holder.lockCanvas(null); looks suspect.
Try canvas = holder.lockCanvas(); instead.
I notice that you are adding a "Pad" to the destination rectangle to change it's height.
If you do that, I wonder if the destination rectangle size will match the size of the image you are drawing and thus "over draw" some gray bar as you are seeing. Just a guess - maybe if you remove the height pad it will go away? Just a guess.
dst = new Rect(0, pad, viewWidth, desiredHeight + pad);
canvas.drawBitmap(bmp, null, dst, null);