package com.libsvg;
/**
*
* @author Pavel.B.Chernov (based on ideas of kushnarev)
*
*/
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.util.EncodingUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.widget.ImageView;
import com.poi.poiandroid.R;
/**
* A Drawable that wraps an Svg image and can be stretched or aligned. You can create a
* SvgDrawable from a file path, an input stream, through XML inflation.
* <p>It can be defined in an XML file with the <code><com.libsvg.SvgDrawable></code> element.</p>
* <p>
* Also see the {@link com.libsvg.SvgImageView} class, which cares about loading and operating
* with svg graphics inside view container.
* </p>
*
* @attr ref android.R.styleable#SvgDrawable_src
* @attr ref android.R.styleable#SvgDrawable_antialias
* @attr ref android.R.styleable#SvgDrawable_filter
* @attr ref android.R.styleable#SvgDrawable_dither
* @attr ref android.R.styleable#SvgDrawable_gravity
*/
public class SvgDrawable extends Drawable {
private static final int DEFAULT_PAINT_FLAGS = Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG;
// Load native libraries
static {
System.loadLibrary("svgandroid");
}
// libsvg-android rasterizer id
private long mSvgId;
private Bitmap mBitmap;
private int mGravity = Gravity.FILL;
private Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS);
// These are scaled to match the target density.
private int mWidth = -1;
private int mHeight = -1;
private double mOriginalAspectRatio = 1.0;
private final Rect mDstRect = new Rect(); // Gravity.apply() sets this
private boolean mApplyGravity;
private ImageView.ScaleType mScaleType = ImageView.ScaleType.FIT_CENTER;
// TODO
// Native libsvgandroid SHOULD USE this value to calculate width and height in pixels
// Because size in SVG image can be written in mm, cm, inches, feets ....
private float mRealDPI = 0;
/**
* Creates a new SvgDrawable object and loads SVG by resource ID
*/
static public SvgDrawable getDrawable(Resources res, int id) {
InputStream inStream = res.openRawResource(id);
if (inStream == null) {
return null;
}
SvgDrawable obj = new SvgDrawable(res, inStream);
return obj;
}
/**
* Create an empty drawable, not dealing with density.
* @deprecated Use {@link #SvgDrawable(Resources)} to ensure
* that the drawable has correctly set its target density.
*/
@Deprecated
public SvgDrawable() {
}
/**
* Create an empty drawable.
*/
public SvgDrawable(Resources res) {
if (res != null) {
mRealDPI = (res.getDisplayMetrics().xdpi + res.getDisplayMetrics().ydpi) / 2;
}
}
/**
* Create a drawable by opening a given file path and decoding the SVG.
* @throws FileNotFoundException
* @deprecated Use {@link #SvgDrawable(Resources, String)} to ensure
* that the drawable has correctly set its target density.
*/
@Deprecated
public SvgDrawable(String filepath) throws FileNotFoundException {
FileInputStream f = new FileInputStream(filepath);
LoadSvg(null, f);
}
/**
* Create a drawable by opening a given file path and decoding the bitmap.
* @throws FileNotFoundException
*/
public SvgDrawable(Resources res, String filepath) throws FileNotFoundException {
FileInputStream f = new FileInputStream(filepath);
LoadSvg(res, f);
}
/**
* Create a drawable by decoding SVG from the given input stream.
* @deprecated Use {@link #SvgDrawable(Resources, java.io.InputStream)} to ensure
* that the drawable has correctly set its target density.
*/
@Deprecated
public SvgDrawable(java.io.InputStream is) {
LoadSvg(null, is);
}
/**
* Create a drawable by decoding a bitmap from the given input stream.
*/
public SvgDrawable(Resources res, java.io.InputStream is) {
LoadSvg(res, is);
}
/**
* Decodes a drawable from the given input stream.
*/
public boolean LoadSvg(Resources res, java.io.InputStream is) {
try {
if (res != null) {
// TODO
mRealDPI = (res.getDisplayMetrics().xdpi + res.getDisplayMetrics().ydpi) / 2;
}
// Read into the buffer
if (is != null) {
byte[] buffer = new byte[is.available()];
is.read(buffer);
is.close();
// And make a string
String content = EncodingUtils.getString(buffer, "UTF-8");
buffer = null;
// Parse it
mSvgId = SvgRaster.svgAndroidCreate();
if (SvgRaster.svgAndroidParseBuffer(mSvgId, content) != 0) {
android.util.Log.w("SvgDrawable", "SvgDrawable cannot decode " + is);
}
SvgRaster.svgAndroidSetAntialiasing(mSvgId, true);
mWidth = SvgRaster.svgAndroidGetWidth(mSvgId);
mHeight = SvgRaster.svgAndroidGetHeight(mSvgId);
mOriginalAspectRatio = ((double)mWidth) / ((double)mHeight);
return true;
}
}
catch (IOException e) {
e.printStackTrace();
}
return false;
}
public final Paint getPaint() {
return mPaint;
}
public final Bitmap getBitmap() {
return mBitmap;
}
/** Get the gravity used to position/stretch the bitmap within its bounds.
* See android.view.Gravity
* @return the gravity applied to the bitmap
*/
public int getGravity() {
return mGravity;
}
/** Set the gravity used to position/stretch the bitmap within its bounds.
See android.view.Gravity
* @param gravity the gravity
*/
public void setGravity(int gravity) {
mApplyGravity = (mGravity != gravity);
mGravity = gravity;
}
public void setAntiAlias(boolean aa) {
com.libsvg.SvgRaster.svgAndroidSetAntialiasing(mSvgId, aa);
}
@Override
public void setFilterBitmap(boolean filter) {
mPaint.setFilterBitmap(filter);
}
@Override
public void setDither(boolean dither) {
mPaint.setDither(dither);
}
public void setScaleType(ImageView.ScaleType scaleType) {
mScaleType = scaleType;
invalidateSelf();
}
/*
* This is essential for correct transformation of SVG image
* It is called from SvgImageView.onSizeChanged , .setImageDrawable, .setScaleType
* Actual processing of scaleType is in private void ImageView.configureBounds()
* But this procedure is called before that.
* So we can adopt our width and height respectively to scaleType
*/
public void adjustToParentSize(int vWidth, int vHeight) {
// Reload original width and height
mWidth = SvgRaster.svgAndroidGetWidth(mSvgId);
mHeight = SvgRaster.svgAndroidGetHeight(mSvgId);
switch (mScaleType) {
case FIT_XY:
mWidth = vWidth;
mHeight = vHeight;
break;
case CENTER: // No scaling
break;
case CENTER_CROP: // Scale so that both width and height of the image will be equal to or larger than view
if (mWidth * vHeight > vWidth * mHeight) {
mHeight = vHeight;
mWidth = (int)(((double)mHeight) * mOriginalAspectRatio);
}
else {
mWidth = vWidth;
mHeight = (int)(((double)mWidth) / mOriginalAspectRatio);
}
break;
case CENTER_INSIDE: // No scale if image fits, otherwise - scale to fit whole image
if (mWidth <= vWidth && mHeight <= vHeight) {
break;
}
if (vWidth < vHeight) {
mWidth = vWidth;
mHeight = (int)(((double)mWidth) / mOriginalAspectRatio);
}
else {
mHeight = vHeight;
mWidth = (int)(((double)mHeight) * mOriginalAspectRatio);
}
break;
case FIT_CENTER: // Scale so that both width and height of the image will be equal to or lesser than view
case FIT_START: // Scale like FIT_CENTER but place bitmap in top left corner
case FIT_END: // Scale like FIT_CENTER but place bitmap in bottom right corner
default:
if (vWidth < vHeight) {
mWidth = vWidth;
mHeight = (int)(((double)mWidth) / mOriginalAspectRatio);
}
else {
mHeight = vHeight;
mWidth = (int)(((double)mHeight) * mOriginalAspectRatio);
}
break;
}
}
/*
* When bounds do change we should render the Bitmap from SVG image
* You should notice that at this moment we have only two cases for mScaleType:
* 1. when we should render image disturbing original aspect ratio
* 2. when we should render image with respect of original aspect ration
* All other processing of scaleType was done before in private void ImageView.configureBounds()
*/
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
if (bounds == null || bounds.width() <= 0 || bounds.height() <= 0) {
return;
}
mApplyGravity = true;
// Create the bitmap to raster svg to
Canvas canvas = new Canvas();
mBitmap = Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888);
canvas.setBitmap(mBitmap);
// Render SVG with use of libandroidsvg
switch (mScaleType) {
case FIT_XY:
SvgRaster.svgAndroidRenderToArea(mSvgId, canvas, 0, 0,
canvas.getWidth(), canvas.getHeight());
break;
case CENTER: // No scaling
case CENTER_INSIDE: // No scale if image fits, otherwise - scale to fit whole image
case CENTER_CROP: // Scale so that both width and height of the image will be equal to or larger than view
case FIT_CENTER: // Scale so that both width and height of the image will be equal to or lesser than view
case FIT_START: // Scale like FIT_CENTER but place bitmap in top left corner
case FIT_END: // Scale like FIT_CENTER but place bitmap in bottom right corner
default:
SvgRaster.svgAndroidRenderToAreaUniform(mSvgId, canvas, 0, 0,
canvas.getWidth(), canvas.getHeight());
break;
}
}
@Override
public void draw(Canvas canvas) {
if (mApplyGravity) {
Gravity.apply(mGravity, mWidth, mHeight, getBounds(), mDstRect);
mApplyGravity = false;
}
Log.d("SVG", "mBitmap: " + mBitmap);
if (mBitmap == null) {
return;
}
canvas.drawBitmap(mBitmap, null, mDstRect, mPaint);
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
throws XmlPullParserException, IOException {
super.inflate(r, parser, attrs);
TypedArray a = r.obtainAttributes(attrs, R.styleable.SvgDrawable);
// Getting a file name
CharSequence cs = a.getText(R.styleable.SvgDrawable_android_src);
String file = cs.toString();
// Is it SVG file?
if (! file.endsWith(".svg")) {
throw new XmlPullParserException(parser.getPositionDescription() +
": <svg> not .svg file as source");
}
final int id = a.getResourceId(R.styleable.SvgDrawable_android_src, 0);
if (id == 0) {
throw new XmlPullParserException(parser.getPositionDescription() +
": <svg> requires a valid src attribute");
}
InputStream inStream = r.openRawResource(id);
if (inStream == null) {
return;
}
LoadSvg(null, inStream);
mPaint.setAntiAlias(a.getBoolean(R.styleable.SvgDrawable_android_antialias,
mPaint.isAntiAlias()));
mPaint.setFilterBitmap(a.getBoolean(R.styleable.SvgDrawable_android_filter,
mPaint.isFilterBitmap()));
mPaint.setDither(a.getBoolean(R.styleable.SvgDrawable_android_dither,
mPaint.isDither()));
setGravity(a.getInt(R.styleable.SvgDrawable_android_gravity, Gravity.FILL));
a.recycle();
}
@Override
public int getIntrinsicWidth() {
return mWidth;
}
@Override
public int getIntrinsicHeight() {
return mHeight;
}
@Override
public int getOpacity() {
return (mBitmap == null || mBitmap.hasAlpha() || mPaint.getAlpha() < 255) ?
PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
}
@Override
public void finalize() throws Throwable{
SvgRaster.svgAndroidDestroy(mSvgId);
super.finalize();
}
}