/**
* Copyright 2011 The ForPlay Authors
*
* 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 forplay.sample.sprites.core.sprite;
import static forplay.core.ForPlay.assetManager;
import static forplay.core.ForPlay.graphics;
import static forplay.core.ForPlay.json;
import forplay.core.Asserts;
import forplay.core.AssetWatcher;
import forplay.core.Image;
import forplay.core.Json;
import forplay.core.ResourceCallback;
/**
* Class for loading and parsing sprite sheets.
* <p>
* To use, call {@link #getSprite(String imageUrl, String jsonUrl)} with an image path and json
* data, or {@link #getSprite(String jsonUrl)} with json data containing image urls.
*/
// TODO(pdr): the two getSprite() methods are messy, clean them up.
public class SpriteLoader {
// prevent instantiation
private SpriteLoader() {
}
/**
* Return a {@link Sprite}, given a path to the image and a path to the json sprite description.
* <p>
* json data should be in the following format:
*
* <pre>
* {@code {
* "sprites": [
* {"id": "sprite_0", "x": 30, "y": 30, "w": 37, "h": 37},
* {"id": "sprite_1", "x": 67, "y": 30, "w": 37, "h": 37},
* {"id": "sprite_2", "x": 104, "y": 30, "w": 37, "h": 37},
* {"id": "sprite_3", "x": 141, "y": 30, "w": 37, "h": 37}
* ]}
* }
* </pre>
*/
public static Sprite getSprite(String imagePath, String jsonPath) {
Image image = assetManager().getImage(imagePath);
final Image[] images = new Image[]{image};
// temp image to prevent NPE if using the Sprite's Layer (Sprite.getLayer()) before the image
// has loaded or before a sprite has been set (Sprite.setSprite()).
final Image tempImage = graphics().createImage(1, 1);
final Sprite sprite = new Sprite(graphics().createImageLayer(tempImage));
// load and parse json
assetManager().getText(jsonPath, new ResourceCallback<String>() {
@Override
public void done(String json) {
try {
parseJson(images, sprite, json);
} catch (Throwable err) {
sprite.error(err);
return;
}
sprite.doneLoadingData();
}
@Override
public void error(Throwable err) {
sprite.error(err);
}
});
// set callback for image
image.addCallback(new ResourceCallback<Image>() {
@Override
public void done(Image resource) {
sprite.doneLoadingImages();
}
@Override
public void error(Throwable err) {
sprite.error(err);
}
});
return sprite;
}
/**
* Return a {@link Sprite}, given a path to the json sprite description.
* <p>
* json data should be in the following format:
*
* <pre>
* {@code {
* "urls": ["images/peasprites2.png", "images/peasprites3.png"],
* "sprites": [
* {"id": "sprite_0", "url": 0, "x": 30, "y": 30, "w": 37, "h": 37},
* {"id": "sprite_1", "url": 0, "x": 67, "y": 30, "w": 37, "h": 37},
* {"id": "sprite_2", "url": 1, "x": 104, "y": 30, "w": 37, "h": 37},
* {"id": "sprite_3", "url": 1, "x": 141, "y": 30, "w": 37, "h": 37}
* ]}
* }
* </pre>
*/
public static Sprite getSprite(String jsonPath) {
// temp image to prevent NPE if using the Sprite's Layer (Sprite.getLayer()) before the image
// has loaded or before a sprite has been set (Sprite.setSprite()).
final Image tempImage = graphics().createImage(1, 1);
final Sprite sprite = new Sprite(graphics().createImageLayer(tempImage));
// create asset watcher for the image assets
final AssetWatcher watcher = new AssetWatcher(new AssetWatcher.Listener() {
@Override
public void done() {
sprite.doneLoadingImages();
}
@Override
public void error(Throwable e) {
sprite.error(e);
}
});
// load and parse json, then add each image parsed from the json to the asset watcher to load
assetManager().getText(jsonPath, new ResourceCallback<String>() {
@Override
public void done(String json) {
try {
parseJson(null, sprite, json);
for (SpriteImage spriteImage : sprite.spriteImages()) {
watcher.add(spriteImage.image());
}
watcher.start();
} catch (Throwable err) {
sprite.error(err);
return;
}
sprite.doneLoadingData();
}
@Override
public void error(Throwable err) {
sprite.error(err);
}
});
return sprite;
}
/**
* Parse a json sprite sheet and add the sprite images to the sheet.
* <p>
* If images is null, the images urls are parsed from the json.
*
* @param image Image to associate with each {@link SpriteImage}, or null to parse from the json
* @param sheet Sprite to store the {@link SpritesImage}s
* @param json json to parse
*/
private static void parseJson(Image[] images, Sprite sprite, String json) {
Json.Object document = json().parse(json);
// parse image urls, if necessary
if (images == null || images.length == 0) {
Json.Array urls = document.getArray("urls");
Asserts.checkNotNull(urls, "No urls provided for sprite images");
images = new Image[urls.length()];
for (int i = 0; i < urls.length(); i++) {
images[i] = assetManager().getImage(urls.getString(i));
}
}
// parse the sprite images
Json.Array spriteImages = document.getArray("sprites");
for (int i = 0; i < spriteImages.length(); i++) {
Json.Object jsonSpriteImage = spriteImages.getObject(i);
String id = jsonSpriteImage.getString("id");
int imageId = jsonSpriteImage.getInt("url"); // will return 0 if not specified
Asserts.checkElementIndex(imageId, images.length, "URL must be an index into the URLs array");
int x = jsonSpriteImage.getInt("x");
int y = jsonSpriteImage.getInt("y");
int width = jsonSpriteImage.getInt("w");
int height = jsonSpriteImage.getInt("h");
SpriteImage spriteImage = new SpriteImage(images[imageId], x, y, width, height);
sprite.addSpriteImage(id, spriteImage);
}
}
}