Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scaling Canvas to resize SVG in Android

I'm not having much luck wrapping my head around this, so I'm hoping someone can help me out.

I'm getting a drawable from an SVG using svg-android, but the drawable isn't scaling to the view. Everything that I've been able to find says I should draw directly to canvas and rescale the canvas, but when I try that it only seems to change the bounds but not scale the image.

This is what I've tried so far:

ImageView testview = (ImageView)findViewById(R.id.testview);

//Get SVG and convert to drawable
SVG vector = SVGParser.getSVGFromResource(getResources(),R.drawable.testvector);
Drawable test = vector.createPictureDrawable();

testview.setBackground(test);  //displays fine, but won't scale to the dimensions of
                               //the View

//function that clips the image but doesn't scale:
Drawable testTwo = new CustomPictureDrawable(vector.getPicture(), 
(float)0.5, (float)0.5);

testView.setBackground(testTwo);

class CustomPictureDrawable extends PictureDrawable {
   private float scalex, scaley;

public CustomPictureDrawable(Picture picture, float scalex, float scaley) {
    super(picture);
    this.scalex = scalex;
    this.scaley = scaley;
}

@Override
public void draw(Canvas canvas) {
    Matrix original = canvas.getMatrix();
    canvas.scale(scalex, scaley);
    super.draw(canvas);
    canvas.setMatrix(original);
}
}

//doesn't display anything
Picture testThree = vector.getPicture();

Bitmap b = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_4444);
Canvas c = new Canvas(b);

c.drawPicture(testThree, new Rect(0,0,10,10));

testview.draw(c);

I've also found a function that will create a scaled bitmap, but the image quality is significantly reduced, so I may as well just use scaled PNGs.

Obviously I'm missing something, and my lack of experience is making it really frustrating.

What I'd like to be able to do is have svg-android completely re-scale the SVG before pulling a Picture or PictureDrawable out of it, but I can't figure out how to step through the SVGParser, and running multipliers on every coordinate pair would probably be super resource intensive anyway.

[edit] Is the only way to scale and re-draw the Picture and assign that to a view to create custom Views and override OnDraw?

i.e.

Picture testThree = vector.getPicture();

Bitmap b = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_4444);
Canvas c = new Canvas(b);

c.drawPicture(testThree, new Rect(0,0,10,10));

//CustomView extends ImageView or Button or whatever with OnDraw overridden and no
//other changes 
CustomView testview = (CustomView)findViewById(R.id.testview); 

testview.OnDraw(c);

Am I on the right track? Canvas c would overwrite the default canvas (which is what I want), wouldn't it?

like image 789
Fafhrd Avatar asked Jan 05 '13 01:01

Fafhrd


2 Answers

I had a similar problem and this is what I learned and how I solved it. First I tried to render my svg directly into a canvas

svg.renderToCanvas(canv);

which didn't have the size I wanted. Then I tried to resize the image using

svg.renderToCanvas(canv,new RectF(0,0,200,200));

and

svg.setDocumentWidth(200);
svg.renderToCanvas(canv);

but both didn't work, so I started to look at my SVG. The problem was that my SVG started like this

<svg width="66.3679625" height="100.4148375" xmlns="http://www.w3.org/2000/svg">

and it seems impossible, once width and height are set in the svg, to change this in code afterwards. There is also an answer in the AndroidSvgFAQ where they recommend to remove those attributes. So I changed this to

 <svg version="1.1" viewBox="0 0 1280 696" xmlns="http://www.w3.org/2000/svg">

The viewBox attribute is the frame of my SVG picture, so if now I use the code above

svg.setDocumentWidth(200);
svg.renderToCanvas(canv);

I get the content of the box with upper left angle at (0,0) and lower right angle at (1200,696) resized such that the width is 200. You can change the behaviour for the ratio initialy it keeps ratios. Note that one can omit width, heigth and ViewBox in SVG and set the ViewBox programmatically

svg.setDocumentViewBox(0,0,width,height);

To get to know more about the ViewBox attribute see W3ViewBox.

like image 174
Yannick Widmer Avatar answered Oct 08 '22 02:10

Yannick Widmer


I figured it out. Or at least figured out a method that works. The answer was staring me in the face in that scaled bitmap function that I didn't like. I fundamentally misunderstood how the Picture class and Draw calls work.

Code that seems to have pulled it off:

//Get a Picture from the SVG
SVG vector = SVGParser.getSVGfromResource(getResources(), R.raw.testvector);

Picture test = vector.getPicture();

//Redraw the picture to a new size
Bitmap bitmap = Bitmap.createBitmap(desired width, desired height, config);

Canvas canvas = new Canvas(bitmap);

Picture resizePicture = new Picture();

canvas = resizePicture.beginRecording(desiredWidth, desiredGeight);

canvas.drawPicture(test, new Rect(0,0,desiredWidth, desiredHeight);

resizePicture.endRecording();

//get a drawable from resizePicture
Drawable vectorDrawing = new PictureDrawable(resizePicture);

I can size it to whatever View I want by getting desiredWidth and desiredHeight from getWidth() and getHeight() calls, and setBackground(vectorDrawing) at the end to put it on the View.

like image 8
Fafhrd Avatar answered Oct 08 '22 01:10

Fafhrd