Click here to Skip to main content
Click here to Skip to main content

Android. ImageView with SVG Support

By , 25 Nov 2010
 

Introduction

As you know, Android doesn’t support SVG format. But benefits of SVG are obvious. First, it’s scalable. You don’t need to have pictures in different resolutions, no need to scale, for example, bitmap image with a quality loss. SVG image can be scaled to any resolution and the quality will be the same. Second, SVG is an ordinary XML file, so its size can be much lesser than the raster format file of the same picture. Moreover, you can change a picture on the fly due to this feature. You can open an SVG file in an ordinary text editor and look how it's composed. And so on… But as Android doesn’t deal with SVG, it will imply some native coding. The good news is that there won’t be a lot of native coding. There are some open source libraries for parsing and rasterizing SVG format.

Prerequisites

There are a lot of tutorials how to begin the native development for the Android platform, so I won’t repeat all of them here. I will just give some useful tips.

  • First, you need the Eclipse IDE. You can download it at [1].
  • Or as an alternative, you can use Motodev Studio [2]. I prefer the second one as it has some delicious features. BTW, you can install Motodev Studio as a plugin for the Eclipse IDE. I couldn’t setup it on OpenSUSE, but as the plugin it works fine.
  • Once Eclipse is installed, add CDT plugin to it.
  • Add Android plugin [3].
  • After that, add Eclipse Sequoyah[4] plugin needed for the native debugging. But make sure you installed CDT before Sequoyah. As the Sequoyah project claims, it installs all dependencies it didn’t really install CDT in my case. While installing Sequoyah, make sure you unchecked “Group Items by Category” and checked “Sequoyah Android Native Support”.
  • Also Windows users will need cygwin[5] (Add the Cygwin/bin path to your system.). While installing it, setup development tools.
  • Download Android SDK [6].
  • And at last, you will need Android NDK. Download CrystaX NDK[7]. It has support for C++ exceptions, RTTI and Standard C++ Library.
  • In Eclipse preferences, set Android SDK & NDK locations. That’s all for now.

First Approach

For the first approach, I will use android-libsvg [8] library. Really it depends on libsvg, libpng, libjpeg, libexpat and zlib. But at this moment, it has support for almost all features of SVG format. To get its sources, create android-libsvg folder somewhere in a file system and go to this folder in console (in cygwin for windows users) and run “bzr branch lp:libsvg-android” command. Bazaar will download sources to this folder.

The Beginning

Ok. Create a new Android project “ImageViewSvg”. Now right click on the project and go to AndroidTools/Add Native support. It will create “jni” folder in the project. Delete all stuff from it and copy contents of “jni” folder of android-libsvg project. Refresh jni folder in the project. Let’s look at Android.mk file in “jni” folder. I'll explain some variables:

  • LOCAL_PATH := $(call my-dir) – my-dyr macro sets LOCAL_PATH variable used to locate source files into current directory
  • include $(CLEAR_VARS) – clears all local variables
  • LOCAL_MODULE – the name of the library
  • LOCAL_CFLAGS – sets compiler flags and some include directories
  • LIBJPEG_SOURCES, … - the list of sources files for each library that will be used
  • LOCAL_LDLIBS – links to additional libraries
  • LOCAL_SRC_FILES – the list of all source files to be compiled, here it contains all sources for all libraries
  • BUILD_SHARED_LIBRARY – link to mk file to build shared library

For more information, please see ANDROID-MK.TXT in NDK.

Sometimes on Windows, after restarting IDE, it can’t run ndk-build. Then right click the project and go to “Build Path/Configure Build Path” and change “Build command” to something like “bash /cygdrive/c/ndk/ndk-build”.

Next create com.toolkits.libsvgandroid package and copy there SvgRaster.java from the libsvg-android project. Preparations are over.

The ImageViewSvg Class

To make ImageView class to support SVG format, it’s enough to inherit from it and override some methods. But I want it to set standard android:src attribute as SVG file and to be able to pick the file from standard “drawable” folder vs. “raw” folder. At the beginning, let’s make all logic to be done in the constructor. To get access to the android:src attribute, add attrs.xml file to the res/values folder:

<?xml version="1.0" encoding="utf-8"?>
<resources>
	<declare-styleable name="ImageViewSvg">
		<attr name="android:src"/>
	</declare-styleable>
</resources>

Let’s look at ImageView class constructor sources. It contains the following code:

Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src);
if (d != null) {
    setImageDrawable(d);
}

And look at setImageBitmap method. It just calls setImageDrawable. So we can use it in our constructor if we have the corresponding bitmap. But what about getting file from “drawable” folder. Nothing supernatural – get resource ID from the android:src attribute and read raw file into an input stream. Next, libandroidsvg gives us a way to parse SVG file:

So, the constructor will look like this:

public ImageViewSvg(Context context, AttributeSet attrs, int defStyle) {

		// Let's try load supported by ImageView formats
		super(context, attrs, defStyle);
        
        if(this.getDrawable() == null)
        {
        	//  Get defined attributes
            TypedArray a = context.obtainStyledAttributes(attrs,
                    R.styleable.ImageViewSvg, defStyle, 0);
                        
            // Getting a file name
            CharSequence cs = a.getText(R.styleable.ImageViewSvg_android_src);
            String file = cs.toString();
            
            // Is it SVG file?
            if (file.endsWith(".svg")) {
            	
            	// Retrieve ID of the resource
                int id = a.getResourceId(R.styleable.ImageViewSvg_android_src, -1);
                if(id != -1){
               	try {
                		// Get the input stream for the raw resource
                		InputStream inStream = getResources().openRawResource(id);
                		int size = inStream.available();
                		
                		// Read into the buffer
                		byte[] buffer = new byte[size];
                		inStream.read(buffer);
						inStream.close();
						
						// And make a string
						mSvgContent = 
						EncodingUtils.getString
							(buffer, "UTF-8");
						
						// Parse it
            			mSvgId = SvgRaster.svgAndroidCreate();
            			SvgRaster.svgAndroidParseBuffer
					(mSvgId, mSvgContent.toString());
            			SvgRaster.svgAndroidSetAntialiasing(mSvgId, true);
                    	            			           	
            			mIsSvg = true;	

						
					} catch (IOException e) {
						mIsSvg = false;
						e.printStackTrace();
					}                	
                }
            }
        }
}

Another problem is that SVG doesn’t have a size. It’s scalable and it’s all. Moreover ImageView layout parameters can be set to wrap_content, fill_parent or we can set predefined size of the image. But when a layout is required, Android sets the size and we can override onSizeChanged method. The only problem is wrap_content attribute. In this case, the size will be 0. The idea is to replace wrap_content with fill_parent on the fly. But doing it in the constructor will give nothing. If you debug through source codes, you will see that parent layout drags layout parameters from attributes directly and calls setLayoutParams method. Let’s override it:

@Override 
public void setLayoutParams(ViewGroup.LayoutParams params){
	if(mIsSvg)
	{
		// replace WRAP_CONTENT if needed
		if(params.width == ViewGroup.LayoutParams.WRAP_CONTENT
				&& getSuggestedMinimumWidth() == 0)
			params.width = ViewGroup.LayoutParams.FILL_PARENT;
		if(params.height == ViewGroup.LayoutParams.WRAP_CONTENT
				&& getSuggestedMinimumHeight() == 0)
			params.height = ViewGroup.LayoutParams.FILL_PARENT;
	}
	super.setLayoutParams(params);
}

And onSizeChanged:

@Override 
public void onSizeChanged(int w, int h, int ow, int oh){
	if(mIsSvg){
		//Create the bitmap to raster svg to
			Canvas canvas = new Canvas();
		mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
		canvas.setBitmap(mBitmap);
		// Render SVG with use of libandroidsvg
		SvgRaster.svgAndroidRenderToArea(
			mSvgId, canvas,
			0, 0, canvas.getWidth(), canvas.getHeight());	
		this.setImageBitmap(mBitmap);
	}
	else
		super.onSizeChanged(w, h, ow, oh);
}

And at last, it’s the time to try it. Create the following layout:

<?xml version="1.0" encoding="utf-8"?>
	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
		android:background="#AA0000"
		android:layout_height="fill_parent" 
		android:layout_width="fill_parent"
		android:layout_weight="1.0" 
		android:gravity="center"
		>
		<com.imageviewsvg.controls.ImageViewSvg
			android:src="@drawable/lion" 
			android:layout_width="100dip"
			android:layout_height="100dip" 
			android:id="@+id/svgview"
			android:layout_gravity="center" 
			/>
	</LinearLayout>

And run:

pic1.JPG

Debugging Native Code

To debug native code, you can follow the instructions given by Carlos Souto at Sequoyah Project [9]. But during the first time, some things are unclear. So there are several tips:

  • While configuring C++ debug configuration, the application really must be app_process, don’t care Eclipse says the program doesn’t exist, it will be created later.
  • You should run ndk-gdb each time you run debugging. Sometimes the command should be ndk-gdb –adb=/tools/adb –force.
  • Don’t forget to set "debuggable" in the manifest.

Second Approach. Anti Grain Geometry.

There is another library where you can use to rasterize SVG files. It’s Anti Grain Geometry[10]. The only extra library that will be needed is libexpat. We already have it in our project. In the jni folder, create folders like this:

pic2.JPG

Copy the corresponding files from agg sources folder into gpc/include/src folders. There in an examples folder where you can find svg_viewer folder. Copy all files except svg_test into aggsvg jni folder. It will parse and rasterize SVG with use of AGG. But it has only basic support for SVG and can’t parse complicated stuff. You should extend the parser by yourself. In aggsvg-android folder, create aggsvgandroid.cpp file. The example parses SVG from a file system. To parse a string, add the following method to a parser class:

void parser::parse(const char *chars, int length){

    	char msg[1024];

	    XML_Parser p = XML_ParserCreate(NULL);
	    if(p == 0)
	    {
		    throw exception("Couldn't allocate memory for parser");
	    }

	    XML_SetParamEntityParsing(p, XML_PARAM_ENTITY_PARSING_ALWAYS);
	    XML_UseForeignDTD(p, true);

	    XML_SetUserData(p, this);
	    XML_SetElementHandler(p, start_element, end_element);
	    XML_SetCharacterDataHandler(p, content);

	    int done = 0;
	    std::string str = std::string(chars);
	    std::istringstream inputString(str);

	    while(true){
	    	if(done)
	    		break;
            size_t len = inputString.readsome(m_buf, buf_size);
            done = len < buf_size;
            if(!XML_Parse(p, m_buf, len, done))
            {
                sprintf(msg,
                    "%s at line %d\n",
                    XML_ErrorString(XML_GetErrorCode(p)),
                    (int)XML_GetCurrentLineNumber(p));
                throw exception(msg);
            }
	    }
        XML_ParserFree(p);

        char* ts = m_title;
        while(*ts)
        {
            if(*ts < ' ') *ts = ' ';
            ++ts;
        }
    }

At the end of Android.mk file, add section to build another library. It’s pretty simple. Just clear variables after the first library build and set them to build another library. And here is the class to rasterize with use of AGG:

class SvgRasterizer{
	agg::svg::path_renderer m_path;
    double m_min_x;
    double m_min_y;
    double m_max_x;
    double m_max_y;
    double m_x;
    double m_y;
    pix_format_e pixformat;
	agg::rendering_buffer m_rbuf_window;

public:
	SvgRasterizer(pix_format_e format, uint32_t width, 
			uint32_t height, void *pixels) : \
		m_path(), \
		m_min_x(0.0), \
		m_min_y(0.0), \
		m_max_x(0.0), \
		m_max_y(0.0), \
		pixformat(format)
	{
		m_rbuf_window.attach((unsigned char*)pixels, width, height, 4*width);
	}

	void parse_svg(const char* svg, int length){
		// Create parser
		agg::svg::parser p(m_path);
		// Parse SVG
		p.parse(svg, length);
		// Make all polygons CCW-oriented
		m_path.arrange_orientations();
		// Get bounds of the image defined in SVG
        m_path.bounding_rect(&m_min_x, &m_min_y, &m_max_x, &m_max_y);
	}

	void rasterize_svg()
	{
		typedef agg::pixfmt_rgba32 pixfmt;
		typedef agg::renderer_base<pixfmt> renderer_base;
		typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_solid;

        pixfmt pixf(m_rbuf_window);
        renderer_base rb(pixf);
        renderer_solid ren(rb);

        agg::rasterizer_scanline_aa<> ras;
        agg::scanline_p8 sl;
        agg::trans_affine mtx;

        double scl;
        // Calculate the scale the image to fit given bitmap
        if(m_max_y > m_max_x)
        	scl = pixf.height()/m_max_y;
        else
        	scl = pixf.width()/m_max_x;

        // Default gamma as is
        ras.gamma(agg::gamma_power(1.0));
        mtx *= agg::trans_affine_scaling(scl);

        m_path.expand(0.0);

        // Render image
        m_path.render(ras, sl, ren, mtx, rb.clip_box(), 1.0);

        ras.gamma(agg::gamma_none());
	}
};

And in sources, I’ve added an ability to test both approaches:

pic3.JPG

Conclusion

So, there are at last two ways to show SVG file in Android. The main benefit of libsvg-android is that it is ready to use, but it is more than three times slower than AGG, with which you should extend SVG parser by your own. Also with AGG, you get extra features for the image processing. I’ve just used ImageView in the layout, but to use it programmatically you, of cause, should override more methods, such as setImageResource for example.

That’s all. Thanks!

Resources

  1. http://www.eclipse.org
  2. http://developer.motorola.com/docstools/motodevstudio
  3. http://developer.android.com/guide/developing/tools/adt.html
  4. http://www.eclipse.org/sequoyah/
  5. http://www.cygwin.com/
  6. http://developer.android.com/sdk/index.html
  7. http://www.crystax.net/android/ndk-r4.php?lang=en
  8. https://launchpad.net/libsvg-android
  9. http://www.eclipse.org/sequoyah/documentation/native_debug.php
  10. http://www.antigrain.com/

History

  • 25th November, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Igor Kushnarev
Software Developer
Russian Federation Russian Federation
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionErro on WindowsmemberMember 1001296629 Apr '13 - 5:58 
12:54:04 **** Incremental Build of configuration Default for project ImageViewSvg ****
ndk-build V=1
Cannot run program "ndk-build" (in directory...
???
What can I do?
QuestionHow can write svg file in android?memberparagchauhan2010@gmail.com25 Sep '12 - 9:38 
How can i write svg file in android ?
AnswerRe: How can write svg file in android?memberIgor Kushnarev25 Sep '12 - 19:24 
What do you mean?
GeneralRe: How can write svg file in android?memberparagchauhan2010@gmail.com26 Sep '12 - 8:44 
https://www.dropbox.com/s/irqtw023bz4kaje/Screenshot_2012-09-26-08-41-36.png[^]
 
pls check this image i need write svg file like this
 
Thanks
Parag
GeneralRe: How can write svg file in android?memberIgor Kushnarev26 Sep '12 - 18:39 
Sorry, I still don't understand what you want...
Do you want to draw the image from the scratch? - You can use Inkscape for example...
Or do you want to use a ready image?
I don't think it's a good idea to use SVG for user interface.
But if you want just use right layout parameters.
GeneralRe: How can write svg file in android?memberparagchauhan201026 Sep '12 - 20:11 
Ok thanks for reply now i use SVG as Image and try to write .svg format runtime so automatically font style set in svg
QuestionSome issue with rotationmembervaris15 Oct '11 - 17:20 
Hello,
 
Thank you for your article. It is a good one.
I have found one problem with your example:
when select "agg" and rotate a phone the application crashes all the time.
AnswerRe: Some issue with rotationmemberIgor Kushnarev16 Oct '11 - 18:57 
Hello,
 
Did you try to debug to find out where it happens?
QuestionError while trying your Examplecodemembert0mM3k12 Jul '11 - 22:54 
Hello Igor,
thanks for your work here. This is exactly what I'm looking for.
But when i try to run the code i got an "UnsatisfiedLinkError: Couldn't load svgandroid: findLibrary returned null"
Any idea how to fix this problem?
 
Thanks in Advance
AnswerRe: Error while trying your ExamplecodememberIgor Kushnarev13 Jul '11 - 5:06 
Hello,
 
Did you have any errors during building all?
 
It seems that the svgandroid library wasn't built.
Please check that it exists.
Also walk through Android.mk and check all libs it depends were built.
 
Thanks
GeneralRe: Error while trying your Examplecodemembert0mM3k13 Jul '11 - 21:03 
Hey,
thanks for your quick reply. I solved the problem some minutes ago Smile | :)
It was a problem which occured because i'm using windows.
I talked to the developers of the "libsvg-android" library and he managed me through my problem Wink | ;-)
Now I was able to watch your work. Great Job by the way Smile | :)
 
But now I have a problem when I try to change the pic. I exported a SVG Picture from OpenStreetMap.org and this app does not start.
 
Any idea what goes wrong?
 
DEBUG/Zygote(33): Process 393 terminated by signal (11)
INFO/ActivityManager(61): Process com.imageviewsvg (pid 393) has died.
ERROR/InputDispatcher(61): channel '40656578 com.imageviewsvg/com.imageviewsvg.MainActivity (server)' ~ Consumer closed input channel or an error occurred.  events=0x8
ERROR/InputDispatcher(61): channel '40656578 com.imageviewsvg/com.imageviewsvg.MainActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
 

GeneralRe: Error while trying your ExamplecodememberIgor Kushnarev13 Jul '11 - 22:00 
Hi, again,

libsvg-android doesn't support rgb(255, 255, 255) color representation. (At least at the moment I wrote the article.)
Check if there are occurences of such representation and use fill: #ffffff
 
Also, there may be some other unsupported features unsupported by libsvg-android.
Check the structure of the .svg file you're using.
AnswerRe: Error while trying your ExamplecodememberMirko Häberlin6 Mar '13 - 6:58 
You need to add
 
static {
        System.loadLibrary("svgandroid");
    }

QuestionNeed a simple andriod app to create Gallery and load imagememberSenthilKM14326 Jun '11 - 4:44 
where can I find an application to create gallery and load image? is it possible for you to create a simple app and upload it? Smile | :)
AnswerRe: Need a simple andriod app to create Gallery and load imagememberIgor Kushnarev26 Jun '11 - 20:40 
What gallery do you mean? There is a Gallery widget in Andrioid API.
QuestionDownload Sourcememberkrolser7 Jun '11 - 3:40 
How to download sources?
Link "Download source - 1.3 MB" doesn't work.
AnswerRe: Download SourcememberIgor Kushnarev11 Jun '11 - 3:05 
All works fine Confused | :confused:
GeneralSVG that doesn't workmemberfeeder18035 Feb '11 - 0:51 
Hi! I am working on a proyect where I need SVG in Android. I've just tried this (after some problems with ndk etc) and it works fine. The problem is that I need to show a bigger file that doesn't seem to work. The SVG file is this one: SVG file. I don't know why it doesn't work. Do you have any idea?
GeneralRe: SVG that doesn't workmemberIgor Kushnarev7 Feb '11 - 0:40 
Hi,
 
First don't use agg. It doesn't support many features of SVG. It can render only simple images.
As for libsvg-android it seems it doesn't support rgb(255, 255, 255) color representation.
Use fill: #ffffff, or check for updates at https://launchpad.net/libsvg-android[^]. Maybe developers already added this feature.
 
Thanks
GeneralThank You for Your work! I've made some improvements.memberdiov13 Dec '10 - 22:29 
Igor, You have done a great job! Thank You!
 
I've started using Your class and found some ways to improve it.
1. Actually, there are width and height in SVG file. Almost any SVG file has desired width and height. So I needed to take it into account.
2. I need a Drawable class, not an ImageView one.
3. I want to use advantage of scale types for images: fitXY, fitStart, fitEnd, centerCrop and so on.
4. I want to use fitXY scale type, which requires non-uniform scaling.
5. Besides, I do not want standard ImageView class to scale rasterized bitmap. I want that Svg Drawable would scale itself in vector before rasterization.
 
I've done all that and posted an article here.
I believe, soon it will be accessible as "Android ImageView and Drawable with SVG support"
 
Again, thank You!
GeneralRe: Thank You for Your work! I've made some improvements.memberIgor Kushnarev13 Dec '10 - 23:53 
Thanks Smile | :) , I'll see your article. Seems to be very useful
GeneralMy votememberKodbar Ka26 Nov '10 - 4:23 
Thanks! That is very helpful.
Do you know this library ?
http://code.google.com/p/svg4mobile/
I'm finding the best way to use
SVG in adroid app.
GeneralRe: My votememberIgor Kushnarev26 Nov '10 - 6:26 
Thank you for the post. I didn't know about svg4mobile and I'll take a glance on it.
GeneralRe: My votememberIgor Kushnarev28 Nov '10 - 22:29 
I've tried it. The parsing is slow. But if the speed is not the issue and the native code is unacceptable it's a good point to start to.
But it seems to be abandoned since May 2010
GeneralMy vote of 5memberAWdrius26 Nov '10 - 1:00 
Thanks for sharing.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 25 Nov 2010
Article Copyright 2010 by Igor Kushnarev
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid