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);
Related
I have a drawable with transparent left and right parts. I need to draw some progress inside it.
startDrawable.setBounds(0, 0, startDrawable.getIntrinsicWidth(), canvas.getHeight());
startDrawable.draw(canvas);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
Path path = new Path();
path.moveTo(0, 0);
path.lineTo(startDrawable.getIntrinsicWidth() / 2, 0);
path.lineTo(startDrawable.getIntrinsicWidth() / 2, canvas.getHeight());
canvas.drawPath(path, paint);
But unfortunately this code fill transparent parts too and it looks like black rectangle.
I want to see something like this. Does it possible to draw black color only inside not trasparent part of image ??
You can create two different Bitmaps:
one that includes your normal background, lines, shapes and any draw you want (be sure to stay INSIDE your choosed bounds)
one that is the shape used to CUT the previous one via "Paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.xxxxxxx))" method (do a "PorterDuffXfermode" search on Google Images to see all mixes possibilities)
In this way you are free to draw what you want in a normal rectangular/squared Bitmap, and at the end you apply a Mask (over the whole Bitmap) using a free shape (triangular, circular, etc..). Same tecnique is used to create a circular picture starting from the original rectangular version.
For example this code creates a rounded version of a rectangular Bitmap:
#Nullable
public static Bitmap getRoundedBitmap(#Nullable final Bitmap bmp, final int radius) {
if ((bmp == null) || (radius < 1)) return null;
Bitmap cBitmap;
if (bmp.getWidth() != radius || bmp.getHeight() != radius) {
float cSmallest = Math.min(bmp.getWidth(), bmp.getHeight());
float cFactor = cSmallest / radius;
try {
cBitmap = Bitmap.createScaledBitmap(bmp, (int)(bmp.getWidth() / cFactor), (int)(bmp.getHeight() / cFactor), true);
} catch (Exception e) {
cBitmap = null;
}
} else cBitmap = bmp;
if (cBitmap == null) return null;
final Bitmap cOutput = Bitmap.createBitmap(radius, radius, Bitmap.Config.ARGB_8888);
final Canvas cCanvas = new Canvas(cOutput);
final Paint cPaint = new Paint();
final Rect cRect = new Rect(0, 0, radius, radius);
cPaint.setAntiAlias(true);
cPaint.setFilterBitmap(true);
cPaint.setDither(true);
cPaint.setStyle(Paint.Style.FILL);
cCanvas.drawARGB(0, 0, 0, 0);
cPaint.setColor(Color.BLACK);
cCanvas.drawCircle(radius / 2f, radius / 2f, radius / 2f, cPaint);
cPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//draws the rectangular Bitmap and use the special Paint object that has the circular "mask" set
cCanvas.drawBitmap(cBitmap, cRect, cRect, cPaint);
return cOutput;
}
I want to add transparent canvas on the price quote. Exactly we can see in following picture ($74). I tried to follow SO question but nothing was working for me. Here is my code:
#Override
public Bitmap transform(Bitmap bitmap) {
// TODO Auto-generated method stub
synchronized (ImageTransform.class) {
if (bitmap == null) {
return null;
}
Bitmap resultBitmap = bitmap.copy(bitmap.getConfig(), true);
Bitmap bitmapImage = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_heart);
bitmapImage = Bitmap.createScaledBitmap(bitmapImage, 50, 50, true);
Canvas canvas = new Canvas(resultBitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(40);
paint.setShadowLayer(2.0f, 1.0f, 1.0f, Color.BLACK);
canvas.drawText("$250", 20, 400, paint);
canvas.drawBitmap(bitmapImage, 510, 55, null);
bitmap.recycle();
return resultBitmap;
}
How can I add a transparent color or image on price section?
Edit-1
However I am able to add canvas rectangle but not at the right position
Paint paintrect = new Paint();
paintrect.setColor(Color.BLACK);
paintrect.setStyle(Paint.Style.FILL_AND_STROKE);
paintrect.setStrokeWidth(10);
float left = 20;
float top = 80;
float right = 50;
float bottom = 0;
canvas.drawRect(left, top, right, bottom, paintrect);
I got this :
You can see black rectangle on top-left of image.
You are almost there!
Paint paintrect = new Paint();
paintrect.setColor(Color.BLACK);
paintrect.setStyle(Paint.Style.FILL_AND_STROKE);
paintrect.setStrokeWidth(10);
paintrect.setAlpha(127);
float left = 18;
float top = 360;
float right = 128;
float bottom = 416;
canvas.drawRect(left, top, right, bottom, paintrect);
You will have to tweak the rectangle coordinates to get it right where you want; I estimated as best I could.
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.
I have searched for many answers here for drawing a circle on bitmap using canvas. However, I got some error in the code and the application stopped without any exception.
Can anyone give me some help? It works fine with I create a blank bitmap and draw a circle on it.
Any help will be appreciated!
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.main);
Paint paint = new Paint();
//paint.setAntiAlias(true);
paint.setColor(Color.BLUE);
Canvas canvas = new Canvas(bmp);
canvas.drawCircle(50, 50, 10, paint);
ImageView imageView = (ImageView)findViewById(R.id.imageView1);
//imageView.setAdjustViewBounds(true);
imageView.setImageBitmap(bmp);
Read somewhere that Resource bitmaps are immutable. Try...
bmp = bmp.copy(bmp.getConfig(), true);
This will draw a circle for you for given height
private RectF outerCircle;
diameter =400;
int left = (width - diameter) / 2;
int top = (height - diameter) / 2;
int bottom = top + diameter;
int right = left + diameter;
outerCircle = new RectF(left, top, right, bottom);
I think I'm a bit confused about how to use custom views. I'm following along with slides from a talk given by Eric Burke from Square (from this year's anddevcon, slides here: http://www.andevcon.com/AndevCon_II/downloadpresentation.aspx?aid=Taming_Android__User_Experience_Lessons_from_Square_pdf.zip&sid=2).
His code, or at least the part he showed in the slides, went something like this:
public class EditablePhoto extends View {
private Bitmap framedPhoto;
private Bitmap image;
private Drawable placeholder;
public EditablePhoto(Context context) {
super(context);
}
#Override protected void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
int measuredWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
int measuredHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
//ensure view always square
int min = Math.min(measuredHeight, measuredWidth);
setMeasuredDimension(min, min);
}
#Override
protected void onDraw(Canvas canvas) {
if(placeholder == null && image==null) return;
if(framedPhoto == null) {
createFramedPhoto(Math.min(getWidth(), getHeight()));
}
canvas.drawBitmap(framedPhoto, 0, 0, null);
}
private void createFramedPhoto(int size) {
Drawable imageDrawable = (image!=null)
? new BitmapDrawable(image) : placeholder;
Bitmap output = Bitmap.createBitmap(size, size,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
RectF outerRect = new RectF(0, 0, size, size);
float outerRadius = size / 18f;
//Red rectangle
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.RED);
canvas.drawRoundRect(outerRect, outerRadius, outerRadius, paint);
paint.setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.SRC_IN));
imageDrawable.setBounds(0, 0, size, size);
canvas.saveLayer(outerRect, paint, Canvas.ALL_SAVE_FLAG);
imageDrawable.draw(canvas);
canvas.restore();
}
}
What I don't get is how to actually use this View now.... Where and when do you set the bitmaps, which are private fields in this class...?
Generally confused and would love some enlightenment.
More than one year passed, but I hope this will help anyone who looking for the right answer. In my case, I putted this line of code
framedPhoto = output;
as the last one in createFramedPhoto() method. It works.
In the example, the author created a rounded rectangle as background then he draw the bitmap on it with XOR mode, so all pixel outside the rounded rectangle will be trim off.
OnDraw() is the method where you will Draw your view on canvas. here too you can analyze onDraw() will fisrt call CreateFramePhoto then draw this Bitmap on canvas .
You can add this customView in layout Either from xml or in Java Class
1) Through Xml :
<EditablePhoto android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
..........................
/>
dont forgate to add constructor EditablePhoto(Context context, AttributeSet attributeSet) for this case
2) through Java class :
EditablePhoto editablePhoto = new EditablePhoto(this);
addView(editablePhoto) // or do anthing you want with this