/* * Copyright 2014 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.samples.apps.iosched.ui; import android.graphics.*; import android.util.Log; import com.google.android.gms.maps.model.Tile; import com.google.android.gms.maps.model.TileProvider; import java.io.*; import java.util.concurrent.ConcurrentLinkedQueue; import com.larvalabs.svgandroid.SVG; import com.larvalabs.svgandroid.SVGBuilder; import static com.google.samples.apps.iosched.util.LogUtils.makeLogTag; public class SVGTileProvider implements TileProvider { private static final String TAG = makeLogTag(SVGTileProvider.class); private static final int POOL_MAX_SIZE = 5; private static final int BASE_TILE_SIZE = 256; private final TileGeneratorPool mPool; private final Matrix mBaseMatrix; private final int mScale; private final int mDimension; /** * NOTE: must use a synchronize block when using {@link android.graphics.Picture#draw(android.graphics.Canvas)} */ private final Picture mSvgPicture; public SVGTileProvider(File file, float dpi) throws IOException { mScale = Math.round(dpi + .3f); // Make it look nice on N7 (1.3 dpi) mDimension = BASE_TILE_SIZE * mScale; mPool = new TileGeneratorPool(POOL_MAX_SIZE); SVG svg = new SVGBuilder().readFromInputStream(new FileInputStream(file)).build(); mSvgPicture = svg.getPicture(); RectF limits = svg.getLimits(); mBaseMatrix = new Matrix(); mBaseMatrix.setPolyToPoly( new float[]{ 0, 0, limits.width(), 0, limits.width(), limits.height() }, 0, new float[]{ 40.95635986328125f, 98.94217824936158f, 40.95730018615723f, 98.94123077396628f, 40.95791244506836f, 98.94186019897214f }, 0, 3 ); } @Override public Tile getTile(int x, int y, int zoom) { TileGenerator tileGenerator = mPool.get(); byte[] tileData = tileGenerator.getTileImageData(x, y, zoom); mPool.restore(tileGenerator); return new Tile(mDimension, mDimension, tileData); } private class TileGeneratorPool { private final ConcurrentLinkedQueue<TileGenerator> mPool = new ConcurrentLinkedQueue<TileGenerator>(); private final int mMaxSize; private TileGeneratorPool(int maxSize) { mMaxSize = maxSize; } public TileGenerator get() { TileGenerator i = mPool.poll(); if (i == null) { return new TileGenerator(); } return i; } public void restore(TileGenerator tileGenerator) { if (mPool.size() < mMaxSize && mPool.offer(tileGenerator)) { return; } // pool is too big or returning to pool failed, so just try to clean // up. tileGenerator.cleanUp(); } } public class TileGenerator { private Bitmap mBitmap; private ByteArrayOutputStream mStream; public TileGenerator() { mBitmap = Bitmap.createBitmap(mDimension, mDimension, Bitmap.Config.ARGB_8888); mStream = new ByteArrayOutputStream(mDimension * mDimension * 4); } public byte[] getTileImageData(int x, int y, int zoom) { mStream.reset(); Matrix matrix = new Matrix(mBaseMatrix); float scale = (float) (Math.pow(2, zoom) * mScale); matrix.postScale(scale, scale); matrix.postTranslate(-x * mDimension, -y * mDimension); mBitmap.eraseColor(Color.TRANSPARENT); Canvas c = new Canvas(mBitmap); c.setMatrix(matrix); // NOTE: Picture is not thread-safe. synchronized (mSvgPicture) { mSvgPicture.draw(c); } BufferedOutputStream stream = new BufferedOutputStream(mStream); mBitmap.compress(Bitmap.CompressFormat.PNG, 0, stream); try { stream.close(); } catch (IOException e) { Log.e(TAG, "Error while closing tile byte stream."); e.printStackTrace(); } return mStream.toByteArray(); } /** * Attempt to free memory and remove references. */ public void cleanUp() { mBitmap.recycle(); mBitmap = null; try { mStream.close(); } catch (IOException e) { // ignore } mStream = null; } } }