App Hangs while Scaling Bitmap in Canvas - android

Drawing text on Bitmap
public Bitmap textAsBitmap(String text, float textSize, int textColor) {
m_paint.setTextSize(textSize);
m_paint.setColor(textColor);
m_paint.setTextAlign(Paint.Align.LEFT);
int width = (int) ( m_paint.measureText(text) + 0.5f); // round
float baseline = (int) (- m_paint.ascent() + 0.5f); // ascent() is negative
int height = (int) (baseline + m_paint.descent() + 0.5f);
final Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
canvas1.setBitmap(image);
canvas1.drawText(text, 0, baseline, m_paint);
return image;
}
Step 2- Drawing Canvas
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
if (CustomTextview.GetDB().size() != 0) {
for (CustomTextview textview : CustomTextview.GetDB()) {
scale = textview.GETSCALE();
final Bitmap bitmap= textAsBitmap(textview.text,textview.size*scale,textview.color);
if (bitmap!=null)
canvas.drawBitmap(bitmap, textview.X, textview.Y, textview.paint);
}
}
canvas.restore();
}
I am using scale listener to scale the bitmap.but when ever scale,it hangs after 5 to 10 mins..

You can simply draw text on the Canvas directly without having to create a bitmap first.
To do that, create a Paint with the right configuration, calculate how big your text will be when it is drawn and then draw the text itself directly onto the canvas.
Paint textPaint = new Paint();
textPaint.setColor(Color.WHITE);
textPaint.setAntiAlias(true);
textPaint.setFakeBoldText(true);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setTextSize(30f);
String myText = "This is a test";
Rect textBounds = new Rect();
textPaint.getTextBounds(myText, 0, myText.length(), bounds);
canvas.drawText(myText, 0, myText.length() rect.width()/2, rect.height()/2, textPaint);
The code above will draw the text centered, but you can easily change this to fit your own needs.

You can try this.
android:largeHeap="true"
inside of application in your AndroidMenifest.xml. such as
<application
android:largeHeap="true" >
</application>
Hopefully works properly. Thanks

The reason it hangs is probably because creating a lot of bitmaps that way will quickly cause out of memory.
If you really need a bitmap you can create a single bitmap and reuse it instead. Better is probably as other suggested solution to use canvas api directly. You can even use a single instance of Paint to avoid memory overhead.

Related

Android canvas fill background color (Canvas application)

By having the following codes, I have some questions.
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView( new View(this) {
Paint mPaint = new Paint();
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
int width = this.getWidth();
int height = this.getHeight();
int radius = width > height ? height/2 : width/2;
int center_x = width/2;
int center_y = height/2;
// prepare a paint
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setAntiAlias(true);
// draw a rectangle
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.FILL); //fill the background with blue color
canvas.drawRect(center_x - radius, center_y - radius, center_x + radius, center_y + radius, mPaint);
// draw some text and rotation
mPaint.setTextSize(50);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setColor(Color.BLACK);
canvas.drawText( "Hello World" , center_x , center_y, mPaint);
}
});
}
}
Q1: How can I fill blue colour in the frame? (The words still appear)
Q2: How many views and surfaces in this app? How can I count these in the app?
Q3: How many windows in this app?
Q4: In the code, I dont see any bitmap object in it.
However, I thought that bitmap is the object that I can really draw things on it. Is my
understanding incorrect?
One possibility is that Canvas constructor initializes bitmap when it is newed.
Q5: I knew that these graphic thing will finally go to surface and then pass to
surfaceflinger for final composition. Where does it locate in my code?
Thanks for any reply.
Five questions. Let's see where I can help.
Q1: Tell the Paint to fill the rectangle: paint.setStyle(Paint.Style.FILL);
Q2: I see only the one view you create programmatically. Why would you like to count the views?
Q3: Again: One
Q4: You draw an mutable bitmaps by wrapping them with a Canvas. The method to actually draw are part of Canvas
Q5: The code you show is part of an Activity. The Activity is called by Android. It's your entry point into your App.
Thanks for the answer. I did the job of making the code for marked answer, and it works.
Bitmap bg = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bg);
// paint background with the trick
Paint rect_paint = new Paint();
rect_paint.setStyle(Paint.Style.FILL);
rect_paint.setColor(Color.rgb(0, 0, 0));
rect_paint.setAlpha(0x80); // optional
canvas.drawRect(0, 0, width, height, rect_paint); // that's painting the whole canvas in the chosen color.
Q2:Hierarchy Viewer is very useful when you want to count how many views in your app.
Optimizing Your UI

Android drawText including text wrapping

I am currently creating an image editor and am attempting to draw text on top of on image using canvas.drawText(). So far I have been successful in doing this but when the user enters text that is too long, the text just continues on one line out of the page and doesn't wrap itself to the width of the screen. How would I go about doing this? I have tried using a static layout but cannot seem to get it to work, has anyone got a tutorial to do this?
My function for drawing on a canvas using static layout:
public Bitmap createImage(float scr_x,float scr_y,String user_text){
Canvas canvas = new Canvas(image);
scr_x = 100;
scr_y = 100;
final TextPaint tp = new TextPaint(Color.WHITE);
canvas.save();
StaticLayout sl = new StaticLayout("" + user_text, tp, originalBitmap.getWidth(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
sl.draw(canvas);
return image;
}
Okay, I've updated my code, but when I try to draw on the image nothing happens at all, I have no idea why either:
public Bitmap createImage(String user_text) {
// canvas object with bitmap image as constructor
Canvas canvas = new Canvas(image);
TextPaint tp = new TextPaint();
tp.setColor(Color.RED);
tp.setTextSize(50);
tp.setTextAlign(Align.CENTER);
tp.setAntiAlias(true);
StaticLayout sl = new StaticLayout("" + user_text, tp,
canvas.getWidth(), Layout.Alignment.ALIGN_NORMAL, 1, 0, false);
canvas.translate(100, 100);
sl.draw(canvas);
return image;
}
Is staticlayout not meant to be used to draw on canvas?
Yes, StaticLayout is what you're meant to use to draw multi-line text on a Canvas. Save yourself a world of pain and don't think about breaking text yourself -- you're on the right path.
I'm not sure about the bitmap problem, but your second code above worked just fine to draw text on a canvas for me.
Learn to use StaticLayout , then draw the Layout object onto a canvas using the Layout.draw() method.
References
public Bitmap drawMultilineTextToBitmap(Context gContext,
int gResId,
String gText) {
// prepare canvas
Resources resources = gContext.getResources();
float scale = resources.getDisplayMetrics().density;
Bitmap bitmap = BitmapFactory.decodeResource(resources, gResId);
android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig();
// set default bitmap config if none
if(bitmapConfig == null) {
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
}
// resource bitmaps are imutable,
// so we need to convert it to mutable one
bitmap = bitmap.copy(bitmapConfig, true);
Canvas canvas = new Canvas(bitmap);
// new antialiased Paint
TextPaint paint=new TextPaint(Paint.ANTI_ALIAS_FLAG);
// text color - #3D3D3D
paint.setColor(Color.rgb(61, 61, 61));
// text size in pixels
paint.setTextSize((int) (14 * scale));
// text shadow
paint.setShadowLayer(1f, 0f, 1f, Color.WHITE);
// set text width to canvas width minus 16dp padding
int textWidth = canvas.getWidth() - (int) (16 * scale);
// init StaticLayout for text
StaticLayout textLayout = new StaticLayout(
gText, paint, textWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
// get height of multiline text
int textHeight = textLayout.getHeight();
// get position of text's top left corner
float x = (bitmap.getWidth() - textWidth)/2;
float y = (bitmap.getHeight() - textHeight)/2;
// draw text to the Canvas center
canvas.save();
canvas.translate(x, y);
textLayout.draw(canvas);
canvas.restore();
return bitmap;
}
source : http://www.skoumal.net/en/android-drawing-multiline-text-on-bitmap/
You should handle it yourself, calculating the text size and wrapping the content in some way (break line at max width or wrap last word).
I already did it on Java SE with the FontMetrics, never for Android; but you should take a look:
http://developer.android.com/reference/android/graphics/Paint.FontMetrics.html
As pointed by Lisa, StaticLayout is the way to go to measure text wrapping.

Center text on a bitmap

I'm trying to draw a text on the center of a bitmap however I can't do it even though I used align.center. The code is:
public Bitmap drawTextToBitmap(Context gContext, String gText) {
Resources resources = gContext.getResources();
float scale = resources.getDisplayMetrics().density;
Bitmap bitmap =
BitmapFactory.decodeResource(resources, R.drawable.blank_marker);
android.graphics.Bitmap.Config bitmapConfig =
bitmap.getConfig();
// set default bitmap config if none
if(bitmapConfig == null) {
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
}
// resource bitmaps are imutable,
// so we need to convert it to mutable one
bitmap = bitmap.copy(bitmapConfig, true);
Canvas canvas = new Canvas(bitmap);
// new antialised Paint
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
// text color - #3D3D3D
paint.setColor(Color.rgb(61, 61, 61));
// text size in pixels
paint.setTextSize((int) (25 * scale));
// text shadow
paint.setShadowLayer(1f, 0f, 1f, Color.WHITE);
// draw text to the Canvas center
Rect bounds = new Rect();
paint.setTextAlign(Align.CENTER);
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 * scale, y * scale, paint);
return bitmap;
}
What am I doing wrong?
It's a lot more straightforward than you think.
Draw the text at half the Bitmap's width and height (center point) in combination with Paint.setTextAlign(Align.CENTER).
The alignment property will take care of the rest.
I guess none of the answers given above are good enough so I post my answer. Try it out guys, it will work on all devices and is not complex at all:
String text = "Text"; //your string
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(activity.getResources().getColor(R.color.white));
paint.setTextSize(30);
// draw text to the Canvas center
Rect boundsText = new Rect();
paint.getTextBounds(text, 0, text.length(), boundsText);
int x = (bitmap.getWidth() - boundsText.width()) / 2;
int y = (bitmap.getHeight() + boundsText.height()) / 2;
canvas.drawText(text, x, y, paint);
Where is the text drawing? The issue might be since you changed the text align to Align.CENTER. Your code calculating x and y assumes the text rendering is using Align.LEFT, I believe.
Either use setTextAlign(Align.CENTER) and render at the actual bitmap center, or use setTextAlign(Align.LEFT) and use the current x and y calculations you are using.

Getting rendered text height, which is equivalent to rendered text width using paint.measureText

I have the following code to draw text.
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setTextSize(400);
paint.setColor(Color.WHITE);
paint.setAntiAlias(true);
paint.setTextAlign(Align.LEFT);
paint.setStyle(Style.FILL);
String text = "698";
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
int textWidth = bounds.width();
int textHeight = bounds.height();
Bitmap originalBitmap = Bitmap.createBitmap(textWidth,
textHeight, Bitmap.Config.ARGB_8888);
Canvas singleUseCanvas = new Canvas(originalBitmap);
singleUseCanvas.drawColor(Color.BLUE);
singleUseCanvas.drawText(text, 0, textHeight, paint);
canvas.drawBitmap(originalBitmap, 0, 0, null);
}
I am getting undesired outcome, which its right and bottom sides are being cropped.
I avoid right side cropping, by using
float textWidth = paint.measureText(text);
Bitmap originalBitmap = Bitmap.createBitmap((int)(textWidth + 0.5),
textHeight, Bitmap.Config.ARGB_8888);
I am getting the following improvement
Yet. My bottom still being cropped. May I know what is the correct way to obtained rendered text height, which is analogy to rendered text width using paint.measureText?
I think i would be helpful to have a look at this post:
Android Paint: .measureText() vs .getTextBounds()
It have a good survey about sizing of rendered text and also text height which is your concern.

How to rotate a drawable with anti-aliasing enabled

I need to rotate an ImageView by a few degrees. I'm doing this by subclassing ImageView and overloading onDraw()
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.scale(0.92f,0.92f);
canvas.translate(14, 0);
canvas.rotate(1,0,0);
super.onDraw(canvas);
canvas.restore();
}
The problem is that the image that results shows a bunch of jaggies.
http://img.skitch.com/20100608-gmdy4sexsm1c71di9rgiktdjhu.png
How can I antialias an ImageView that I need to rotate in order to eliminate jaggies? Is there a better way to do this?
If you know that your Drawable is a BitmapDrawable, you can use anti-aliasing in the bitmap's Paint to do something like the following:
/**
* Not as full featured as ImageView.onDraw(). Does not handle
* drawables other than BitmapDrawable, crop to padding, or other adjustments.
*/
#Override
protected void onDraw(Canvas canvas) {
final Drawable d = getDrawable();
if( d!=null && d instanceof BitmapDrawable && ((BitmapDrawable)d).getBitmap()!=null ) {
final Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
final int paddingLeft = getPaddingLeft();
final int paddingTop = getPaddingTop();
canvas.save();
// do my rotation and other adjustments
canvas.scale(0.92f,0.92f);
canvas.rotate(1,0,0);
if( paddingLeft!=0 )
canvas.translate(paddingLeft,0);
if( paddingTop!=0 )
canvas.translate(0,paddingTop);
canvas.drawBitmap( ((BitmapDrawable)d).getBitmap(),0,0,p );
canvas.restore();
}
}
Set antialias and filterBitmap to the paint that draw's the bitmap.
I had a similar problem and this worked for me:
Paint bitmapPaint = new Paint();
bitmapPaint.setAntiAlias(true);
bitmapPaint.setFilterBitmap(true);
I did the following to get it to work:
In AndroidManifest.xml I enabled hardware-acceleration <application android:hardwareAccelerated="true">.
Instead of using a custom draw-method, I changed the layer-type of the parent-view to hardware-accelerated with anti-aliasing enabled and added my subviews as follows:
Paint layerPaint = new Paint();
layerPaint.setAntiAlias(true);
layerPaint.setFilterBitmap(true);
layerPaint.setDither(true);
RelativeLayout parentLayout = (RelativeLayout) LayoutInflater.from(context).inflate(R.layout.myLayout, null);
parentLayout.setLayerType(View.LAYER_TYPE_HARDWARE, layerPaint);
parentLayout.addView(myChildView);
I have to add that clipping cannot be disabled when using this approach.
This rotation solves just one part of problem - jagged edges, but the image instead became wierd.
ifound only one solution yet:
in ondraw or dispatch draw method
Bitmap bmp = ((BitmapDrawable)image.getDrawable()).getBitmap();
double scale = (0.0+bmp.getWidth()+40.0)/getWidth();
if (scale > 1){
bmp = Bitmap.createScaledBitmap(bmp, getWidth()-40, (int) (bmp.getHeight()/scale), true);
}
canvas.drawColor(Color.TRANSPARENT);
canvas.save();
canvas.translate(20, yShift);
int left = borderSize;
int top = borderSize;
int right = bmp.getWidth() - borderSize;
int bottom = bmp.getHeight() - borderSize;
if(showText){
mPaint.setColor(Color.WHITE);
canvas.rotate(getAngel());
canvas.drawRect(left, top, right, bottom, mPaint);
canvas.translate(0,0);
textFrame.draw(canvas);
}else{
// rotaed bitmap
mMatrix.setRotate(getAngel());
// draw bitmap after creation of new rotated bitmap from rotated matrix
canvas.drawBitmap(Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), mMatrix, true)
, 0, 0, mPaint);
}
canvas.restore();
#Primoz990's solution worked for me :) I also enabled Dithering, so my final code which removes the jagged edges on rotation is this:
Paint bitmapPaint = new Paint();
bitmapPaint.setAntiAlias(true);
bitmapPaint.setFilterBitmap(true);
bitmapPaint.setDither(true);

Categories

Resources