Chromium Code Reviews| 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..d5b3061bd20f4f59bb72fc8b7131da06732857fe |
| --- /dev/null |
| +++ b/ui/android/java/src/org/chromium/ui/resources/sprites/CrushedSpriteResource.java |
| @@ -0,0 +1,242 @@ |
| +// 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.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 mSpriteSize; |
| + 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) { |
|
David Trainor- moved to gerrit
2015/10/15 21:04:57
Add trace events around the bitmap and json loadin
Theresa
2015/10/24 00:06:45
Done.
|
| + BitmapFactory.Options opts = new BitmapFactory.Options(); |
| + opts.inScaled = false; |
| + mBitmap = BitmapFactory.decodeResource(resources, bitmapResId, opts); |
| + |
| + if (mBitmap != null) { |
| + mBitmapSize.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); |
| + try { |
| + parseMetadata(metadataResId, opts.inDensity, resources); |
| + } 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) { |
|
David Trainor- moved to gerrit
2015/10/15 21:04:57
If we still need this, can the constructor use thi
Theresa
2015/10/24 00:06:45
Done.
|
| + BitmapFactory.Options opts = new BitmapFactory.Options(); |
| + opts.inScaled = false; |
| + return BitmapFactory.decodeResource(resources, bitmapResId, opts); |
| + } |
| + |
| + @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 size of an individual sprite in px. |
| + */ |
| + public int getSpriteSize() { |
| + return mSpriteSize; |
| + } |
| + |
| + /** |
| + * 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: |
| + * { |
| + * "version": <version number>, |
| + * "densities": |
| + * [ |
| + * { |
| + * "density": <density number>, |
| + * "size": <unscaled sprite size>, |
| + * "rectangles": [ |
| + * [<list of ints for frame 0>], |
| + * [<list of ints for frame 1>], |
| + * ...... |
| + * ] |
| + * }, |
| + * { |
| + * "density": <density number>, |
| + * "size": <unscaled sprite size>, |
| + * "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 version. |
| + String name = reader.nextName(); |
| + assert name.equals("version"); |
| + reader.nextDouble(); |
| + |
| + // 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 size name. |
| + reader.skipValue(); // Skip size value. |
| + reader.skipValue(); // Skip rectangles name. |
| + reader.skipValue(); // Skip rectangles array. |
| + return false; |
| + } |
| + |
| + name = reader.nextName(); |
| + assert name.equals("size"); |
| + mSpriteSize = 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; |
| + } |
| + } |
| +} |