| Index: ui/android/java/src/org/chromium/ui/resources/sprites/CrushedSpriteResource.java
|
| diff --git a/ui/android/java/src/org/chromium/ui/resources/sprites/CrushedSpriteResource.java b/ui/android/java/src/org/chromium/ui/resources/sprites/CrushedSpriteResource.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..43343c0ed0a199305ff4d57f11de67c0d22ad301
|
| --- /dev/null
|
| +++ b/ui/android/java/src/org/chromium/ui/resources/sprites/CrushedSpriteResource.java
|
| @@ -0,0 +1,269 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package org.chromium.ui.resources.sprites;
|
| +
|
| +import android.content.res.Resources;
|
| +import android.graphics.Bitmap;
|
| +import android.graphics.BitmapFactory;
|
| +import android.graphics.Rect;
|
| +import android.util.JsonReader;
|
| +
|
| +import org.chromium.base.TraceEvent;
|
| +import org.chromium.ui.resources.Resource;
|
| +
|
| +
|
| +import java.io.IOException;
|
| +import java.io.InputStream;
|
| +import java.io.InputStreamReader;
|
| +import java.util.ArrayList;
|
| +
|
| +/**
|
| + * A {@link Resource} that provides an unscaled {@link Bitmap} and corresponding metadata for a
|
| + * crushed sprite. A crushed sprite animation is run by drawing rectangles from a bitmap to a
|
| + * canvas. Each frame in the animation draws its rectangles on top of the previous frame.
|
| + *
|
| + * TODO(twellington): Add an Instrumentation test for this class.
|
| + */
|
| +public class CrushedSpriteResource implements Resource {
|
| + private static final Rect EMPTY_RECT = new Rect();
|
| +
|
| + private Bitmap mBitmap;
|
| + private final Rect mBitmapSize = new Rect();
|
| + private int mSpriteWidth;
|
| + private int mSpriteHeight;
|
| + private int[][] mRectangles;
|
| +
|
| + /**
|
| + * @param bitmapResId The id of the bitmap resource.
|
| + * @param metadataResId The id of the raw resource containing JSON metadata.
|
| + * @param resources A {@link Resources} instance to load assets from.
|
| + */
|
| + public CrushedSpriteResource(int bitmapResId, int metadataResId, Resources resources) {
|
| + mBitmap = loadBitmap(bitmapResId, resources);
|
| +
|
| + if (mBitmap != null) {
|
| + mBitmapSize.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
|
| + try {
|
| + TraceEvent.begin("CrushedSpriteResource.parseMetadata");
|
| + parseMetadata(metadataResId, mBitmap.getDensity(), resources);
|
| + TraceEvent.end("CrushedSpriteResource.parseMetadata");
|
| + } catch (IOException e) {
|
| + e.printStackTrace();
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Convenience method for use in reloading an unscaled {@link Bitmap} without recreating the
|
| + * entire resource.
|
| + * @param bitmapResId The id of the bitmap resource for this crushed sprite.
|
| + * @param resources A {@link Resources} instance to load assets from.
|
| + */
|
| + public static Bitmap loadBitmap(int bitmapResId, Resources resources) {
|
| + TraceEvent.begin("CrushedSpriteResource.loadBitmap");
|
| + BitmapFactory.Options opts = new BitmapFactory.Options();
|
| + opts.inScaled = false;
|
| + Bitmap bitmap = BitmapFactory.decodeResource(resources, bitmapResId, opts);
|
| + TraceEvent.end("CrushedSpriteResource.loadBitmap");
|
| + return bitmap;
|
| + }
|
| +
|
| + @Override
|
| + public Bitmap getBitmap() {
|
| + return mBitmap;
|
| + }
|
| +
|
| + @Override
|
| + public Rect getBitmapSize() {
|
| + return mBitmapSize;
|
| + }
|
| +
|
| + @Override
|
| + public Rect getPadding() {
|
| + return EMPTY_RECT;
|
| + }
|
| +
|
| + @Override
|
| + public Rect getAperture() {
|
| + return EMPTY_RECT;
|
| + }
|
| +
|
| + /**
|
| + * @return The unscaled width of an individual sprite in px.
|
| + */
|
| + public int getSpriteWidth() {
|
| + return mSpriteWidth;
|
| + }
|
| +
|
| + /**
|
| + * @return The unscaled height of an individual sprite in px.
|
| + */
|
| + public int getSpriteHeight() {
|
| + return mSpriteHeight;
|
| + }
|
| +
|
| + /**
|
| + * @return The number of frames in the sprite animation.
|
| + */
|
| + public int getFrameCount() {
|
| + return mRectangles.length;
|
| + }
|
| +
|
| + /**
|
| + * Each sprite frame is represented by a set of rectangles. Most frames consist of small
|
| + * rectangles representing the change from the previous frame. Each rectangle is represented
|
| + * using six consecutive values that specify the values to be used when creating the destination
|
| + * and source rectangles that get painted:
|
| + * 0: destination x 1: destination y 2: source x 3: source y 4: width 5: height
|
| + *
|
| + * @return The unscaled rectangles that need to be painted for each sprite frame in px.
|
| + */
|
| + public int[][] getFrameRectangles() {
|
| + return mRectangles.clone();
|
| + }
|
| +
|
| + /**
|
| + * Parses the raw JSON resource specified by {@code metadataResId}. The JSON is expected to be
|
| + * in this format:
|
| + * {
|
| + * "apiVersion": <version number>,
|
| + * "densities":
|
| + * [
|
| + * {
|
| + * "density": <density number>,
|
| + * "width": <unscaled sprite width>,
|
| + * "height": <unscaled sprite height>,
|
| + * "rectangles": [
|
| + * [<list of ints for frame 0>],
|
| + * [<list of ints for frame 1>],
|
| + * ......
|
| + * ]
|
| + * },
|
| + * {
|
| + * "density": <density number>,
|
| + * "width": <unscaled sprite width>,
|
| + * "height": <unscaled sprite height>,
|
| + * "rectangles": [
|
| + * [<list of ints for frame 0>],
|
| + * [<list of ints for frame 1>],
|
| + * ......
|
| + * ]
|
| + * },
|
| + * ......
|
| + * ]
|
| + * }
|
| + *
|
| + * @param metadataResId The id of the raw resource containing JSON to parse.
|
| + * @param bitmapDensity The density of the unscaled {@link Bitmap} that was loaded.
|
| + * @param resources A {@link Resources} instance to load assets from.
|
| + * @throws IOException
|
| + */
|
| + private void parseMetadata(int metadataResId, int bitmapDensity, Resources resources)
|
| + throws IOException {
|
| + InputStream inputStream = resources.openRawResource(metadataResId);
|
| + JsonReader reader = new JsonReader(new InputStreamReader(inputStream));
|
| + try {
|
| + reader.beginObject(); // Start reading top-level object.
|
| +
|
| + // Consume apiVersion.
|
| + String name = reader.nextName();
|
| + assert name.equals("apiVersion");
|
| + reader.nextString();
|
| +
|
| + // Parse array of densities.
|
| + name = reader.nextName();
|
| + assert name.equals("densities");
|
| + reader.beginArray(); // Start reading array of densities.
|
| + while (reader.hasNext()) {
|
| + reader.beginObject(); // Start reading object for this density.
|
| + boolean foundDensity = parseMetadataForDensity(reader, bitmapDensity);
|
| + reader.endObject(); // Stop reading object for this density.
|
| +
|
| + if (foundDensity) break;
|
| + }
|
| + } finally {
|
| + reader.close();
|
| + inputStream.close();
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Reads a JSON object for a specific density and populates variables if the density matches
|
| + * {@code bitmapDensity}.
|
| + *
|
| + * @param reader The JsonReader reading the JSON metadata.
|
| + * @param bitmapDensity The density of the unscaled {@link Bitmap} that was loaded.
|
| + * @return True if the JSON object being parsed corresponds to bitmapDensity.
|
| + * @throws IOException
|
| + */
|
| + private boolean parseMetadataForDensity(JsonReader reader, int bitmapDensity)
|
| + throws IOException {
|
| + String name = reader.nextName();
|
| + assert name.equals("density");
|
| + int density = reader.nextInt();
|
| +
|
| + // If this is metadata for a density other than bitmapDensity, skip parsing the rest of the
|
| + // object.
|
| + if (density != bitmapDensity) {
|
| + reader.skipValue(); // Skip width name.
|
| + reader.skipValue(); // Skip width value.
|
| + reader.skipValue(); // Skip height name.
|
| + reader.skipValue(); // Skip height value.
|
| + reader.skipValue(); // Skip rectangles name.
|
| + reader.skipValue(); // Skip rectangles array.
|
| + return false;
|
| + }
|
| +
|
| + name = reader.nextName();
|
| + assert name.equals("width");
|
| + mSpriteWidth = reader.nextInt();
|
| +
|
| + name = reader.nextName();
|
| + assert name.equals("height");
|
| + mSpriteHeight = reader.nextInt();
|
| +
|
| + name = reader.nextName();
|
| + assert name.equals("rectangles");
|
| +
|
| + parseFrameRectangles(reader);
|
| +
|
| + return true;
|
| + }
|
| +
|
| +
|
| + /**
|
| + * Parses the 2D JSONArray of frame rectangles and populates {@code mRectangles}.
|
| + * @param reader The JsonReader reading the JSON metadata.
|
| + * @throws IOException
|
| + */
|
| + private void parseFrameRectangles(JsonReader reader) throws IOException {
|
| + ArrayList<ArrayList<Integer>> allFrameRectangles = new ArrayList<ArrayList<Integer>>();
|
| + int frameCount = 0;
|
| +
|
| + reader.beginArray(); // Start reading 2D rectangles array.
|
| + while (reader.hasNext()) {
|
| + ArrayList<Integer> frameRectangles = new ArrayList<Integer>();
|
| + reader.beginArray(); // Start reading frame array.
|
| + while (reader.hasNext()) {
|
| + frameRectangles.add(reader.nextInt());
|
| + }
|
| + reader.endArray(); // Stop reading frame array.
|
| + allFrameRectangles.add(frameRectangles);
|
| + frameCount++;
|
| + }
|
| + reader.endArray(); // Stop reading 2D rectangles array.
|
| +
|
| + // Convert 2D ArrayList to int[][].
|
| + mRectangles = new int[frameCount][];
|
| + for (int i = 0; i < frameCount; i++) {
|
| + ArrayList<Integer> frameRectangles = allFrameRectangles.get(i);
|
| + int[] frameRectanglesArray = new int[frameRectangles.size()];
|
| + for (int j = 0; j < frameRectangles.size(); j++) {
|
| + frameRectanglesArray[j] = frameRectangles.get(j);
|
| + }
|
| + mRectangles[i] = frameRectanglesArray;
|
| + }
|
| + }
|
| +}
|
|
|