/** * Copyright 2008 - 2015 The Loon Game Engine 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. * * @project loon * @author cping * @email:javachenpeng@yahoo.com * @version 0.5 */ package loon.lwjgl; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.Font; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; import java.util.Arrays; import javax.imageio.ImageIO; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import loon.Assets; import loon.LRelease; import loon.LSystem; import loon.Sound; import loon.canvas.Image; import loon.canvas.ImageImpl; import loon.utils.MathUtils; import loon.utils.Scale; import loon.utils.StringUtils; public class Lwjgl3Assets extends Assets { private final static String DEF_RES = "assets/"; public static interface JavaSEResource extends LRelease { InputStream getInputStream(); String getResourceName(); URI getURI(); } public static abstract class DataRes { String path; String name; InputStream in; URI uri; @Override public int hashCode() { return (name == null) ? super.hashCode() : name.hashCode(); } public void close() { if (in != null) { try { in.close(); in = null; } catch (IOException e) { } } if (uri != null) { uri = null; } } } public static class ClassRes extends DataRes implements JavaSEResource { private ClassLoader classLoader; public ClassRes(String path) { this(path, null); } public ClassRes(String path, ClassLoader classLoader) { this.path = path; this.name = "classpath://" + path; this.classLoader = classLoader; } @Override public InputStream getInputStream() { try { if (classLoader == null) { return (in = Lwjgl3Assets.classLoader .getResourceAsStream(path)); } else { return (in = classLoader.getResourceAsStream(path)); } } catch (Exception e) { e.printStackTrace(); } return null; } @Override public String getResourceName() { return name; } @Override public URI getURI() { try { if (uri != null) { return uri; } return (uri = classLoader.getResource(path).toURI()); } catch (URISyntaxException ex) { throw new RuntimeException(ex); } } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } ClassRes other = (ClassRes) obj; if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; } @Override public int hashCode() { return super.hashCode(); } } public static class FileRes extends DataRes implements JavaSEResource { public FileRes(String path) { this.path = path; this.name = "file://" + path; } @Override public InputStream getInputStream() { try { if (in != null) { return in; } File file = new File(path); return (in = new FileInputStream(file)); } catch (FileNotFoundException e) { throw new RuntimeException("file " + name + " not found !", e); } } @Override public String getResourceName() { return name; } @Override public URI getURI() { try { if (uri != null) { return uri; } return (uri = new URL(path).toURI()); } catch (Exception e) { throw new RuntimeException(e); } } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } FileRes other = (FileRes) obj; if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; } @Override public int hashCode() { return super.hashCode(); } } public static class RemoteRes extends DataRes implements JavaSEResource { public RemoteRes(String url) { this.path = url; this.name = url; } @Override public InputStream getInputStream() { try { if (in != null) { return in; } return in = new URL(path).openStream(); } catch (Exception e) { throw new RuntimeException(e); } } @Override public String getResourceName() { return name; } @Override public URI getURI() { try { return new URL(path).toURI(); } catch (Exception e) { throw new RuntimeException(e); } } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } RemoteRes other = (RemoteRes) obj; if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; } @Override public int hashCode() { return super.hashCode(); } } public static class SDRes extends DataRes implements JavaSEResource { public SDRes(String path) { path = StringUtils.replaceIgnoreCase(path, "\\", "/"); if (StringUtils.startsWith(path, '/') || StringUtils.startsWith(path, '\\')) { path = path.substring(1, path.length()); } this.path = path; this.name = "sdcard://" + path; } @Override public InputStream getInputStream() { try { if (in != null) { return in; } return (in = new FileInputStream(new File(path))); } catch (FileNotFoundException e) { throw new RuntimeException("file " + name + " not found !", e); } } @Override public String getResourceName() { return name; } @Override public URI getURI() { try { if (uri != null) { return uri; } return (uri = new URL(path).toURI()); } catch (Exception e) { throw new RuntimeException(e); } } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } SDRes other = (SDRes) obj; if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; } @Override public int hashCode() { return super.hashCode(); } } public static JavaSEResource classRes(String path) { return new ClassRes(path); } public static JavaSEResource fileRes(String path) { return new FileRes(path); } public static JavaSEResource remoteRes(String path) { return new RemoteRes(path); } public static JavaSEResource sdRes(String path) { return new SDRes(path); } private InputStream filestream(String path) { try { File file = new File(path); if (file.exists()) { return new FileInputStream(file); } else { file = new File(StringUtils.replaceIgnoreCase(getPath(path), DEF_RES, "")); if (file.exists()) { return new FileInputStream(file); } else { return classLoader.getResourceAsStream(path); } } } catch (Throwable t) { return null; } } public InputStream strRes(final String path) { if (path == null) { return null; } InputStream in = filestream(path); if (in != null) { return in; } if (path.indexOf("->") == -1) { if (path.startsWith("sd:")) { in = sdRes(path.substring(3, path.length())).getInputStream(); } else if (path.startsWith("class:")) { in = classRes(path.substring(6, path.length())) .getInputStream(); } else if (path.startsWith("path:")) { in = fileRes(path.substring(5, path.length())).getInputStream(); } else if (path.startsWith("url:")) { in = remoteRes(path.substring(4, path.length())) .getInputStream(); } } return in; } private final Lwjgl3Game game; private File[] directories = {}; private Scale assetScale = null; public Lwjgl3Assets(Lwjgl3Game game) { super(game.asyn()); this.game = game; Lwjgl3Assets.pathPrefix = "assets/"; } public void addDirectory(File dir) { File[] ndirs = new File[directories.length + 1]; System.arraycopy(directories, 0, ndirs, 0, directories.length); ndirs[ndirs.length - 1] = dir; directories = ndirs; } public void setAssetScale(float scaleFactor) { this.assetScale = new Scale(scaleFactor); } @Override public Image getRemoteImage(final String url, int width, int height) { final Lwjgl3Image image = new Lwjgl3Image(game, true, width, height, url); asyn.invokeAsync(new Runnable() { public void run() { try { BufferedImage bmp = ImageIO.read(new URL(url)); image.succeed(new ImageImpl.Data(Scale.ONE, bmp, bmp .getWidth(), bmp.getHeight())); } catch (Exception error) { image.fail(error); } } }); return image; } @Override public Sound getSound(String path) { return getSound(path, false); } @Override public Sound getMusic(String path) { return getSound(path, true); } @Override public String getTextSync(String path) throws Exception { return requireResource(path).readString(); } @Override public byte[] getBytesSync(String path) throws Exception { return requireResource(path).readBytes(); } private static Lwjgl3Audio _audio; protected Sound getSound(String path, boolean music) { if (_audio == null) { _audio = new Lwjgl3Audio(); } Exception err = null; String ext = LSystem.getExtension(path); if (ext == null || ext.length() == 0) { for (String suff : SUFFIXES) { final String soundPath = path + suff; try { return _audio.createSound(path, new ByteArrayInputStream( getBytesSync(soundPath)), music); } catch (Exception e) { e.printStackTrace(); err = e; } } } else { try { return _audio.createSound(path, new ByteArrayInputStream( getBytesSync(path)), music); } catch (Exception e) { e.printStackTrace(); err = e; } } return new Sound.Error(err); } static ClassLoader classLoader; static { try { classLoader = Lwjgl3Assets.class.getClassLoader(); } catch (Exception e) { classLoader = Thread.currentThread().getContextClassLoader(); } } protected Resource requireResource(String path) throws IOException { final String serachPath = getPath(path); URL url = classLoader.getResource(serachPath); if (url != null) { boolean isFile = url.getProtocol().equals("file"); if (isFile) { File file = new File(URLDecoder.decode(url.getPath(), LSystem.ENCODING)); if (file.exists()) { return new FileResource(this, file); } else { return new URLResource(this, serachPath); } } else { return new URLResource(this, serachPath); } } else { File file = resolvePath(serachPath); if (file.exists()) { return new FileResource(this, new File(serachPath)); } } for (File dir : directories) { File f = new File(dir, path).getCanonicalFile(); if (f.exists()) { return new FileResource(this, f); } } throw new FileNotFoundException(path); } protected final static File resolvePath(String path) { File file = new File(path); if (!file.exists()) { path = getPath(path); if (path.startsWith(LSystem.FRAMEWORK_IMG_NAME)) { path = DEF_RES + path; } file = new File(path); if (!file.exists() && (path.indexOf('\\') != -1 || path.indexOf('/') != -1)) { file = new File(path.substring(path.indexOf('/') + 1, path.length())); } if (!file.exists() && (path.indexOf('\\') != -1 || path.indexOf('/') != -1)) { file = new File(LSystem.getFileName(path = file .getAbsolutePath())); } if (!file.exists()) { file = new File(LSystem.getFileName(path = (DEF_RES + path))); } } return file; } static byte[] toByteArray(InputStream in) throws IOException { try { byte[] buffer = new byte[512]; int size = 0, read = 0; while ((read = in.read(buffer, size, buffer.length - size)) > 0) { size += read; if (size == buffer.length) buffer = Arrays.copyOf(buffer, size * 2); } if (size < buffer.length) { buffer = Arrays.copyOf(buffer, size); } return buffer; } finally { in.close(); } } protected BufferedImage scaleImage(BufferedImage image, float viewImageRatio) { int swidth = MathUtils.iceil(viewImageRatio * image.getWidth()); int sheight = MathUtils.iceil(viewImageRatio * image.getHeight()); BufferedImage scaled = new BufferedImage(swidth, sheight, BufferedImage.TYPE_INT_ARGB_PRE); Graphics2D gfx = scaled.createGraphics(); gfx.drawImage(image.getScaledInstance(swidth, sheight, java.awt.Image.SCALE_SMOOTH), 0, 0, null); gfx.dispose(); return scaled; } protected Scale assetScale() { return (assetScale != null) ? assetScale : game.graphics().scale(); } abstract static class Resource { public abstract BufferedImage readImage() throws IOException; public abstract InputStream openStream() throws IOException; public AudioInputStream openAudioStream() throws Exception { return AudioSystem.getAudioInputStream(openStream()); } public Font createFont() throws Exception { return Font.createFont(Font.TRUETYPE_FONT, openStream()); } public byte[] readBytes() throws IOException { return toByteArray(openStream()); } public String readString() throws Exception { return new String(readBytes(), LSystem.ENCODING); } } protected static class URLResource extends Resource { public final String url; private Lwjgl3Assets assets; public URLResource(Lwjgl3Assets assets, String url) { this.url = url; this.assets = assets; } @Override public InputStream openStream() throws IOException { return assets.strRes(url); } @Override public BufferedImage readImage() throws IOException { return ImageIO.read(assets.strRes(url)); } } protected static class FileResource extends Resource { public final File file; private Lwjgl3Assets assets; public FileResource(Lwjgl3Assets assets, File file) { this.assets = assets; this.file = file; } @Override public InputStream openStream() throws IOException { return assets.strRes(file.getPath()); } @Override public BufferedImage readImage() throws IOException { return ImageIO.read(assets.strRes(file.getPath())); } @Override public AudioInputStream openAudioStream() throws Exception { return AudioSystem.getAudioInputStream(file); } @Override public Font createFont() throws Exception { return Font.createFont(Font.TRUETYPE_FONT, file); } @Override public byte[] readBytes() throws IOException { InputStream in = openStream(); try { byte[] buffer = new byte[(int) file.length()]; in.read(buffer); return buffer; } finally { in.close(); } } } @Override protected ImageImpl.Data load(String path) throws Exception { Exception error = null; for (Scale.ScaledResource rsrc : assetScale().getScaledResources(path)) { try { BufferedImage image = requireResource(rsrc.path).readImage(); Scale viewScale = game.graphics().scale(), imageScale = rsrc.scale; float viewImageRatio = viewScale.factor / imageScale.factor; if (viewImageRatio < 1) { image = scaleImage(image, viewImageRatio); imageScale = viewScale; } if (game.setting.convertImagesOnLoad) { BufferedImage convertedImage = Lwjgl3ImplGraphics .convertImage(image); if (convertedImage != image) { game.log().debug( "Converted image: " + path + " [type=" + image.getType() + "]"); image = convertedImage; } } return new ImageImpl.Data(imageScale, image, image.getWidth(), image.getHeight()); } catch (FileNotFoundException ex) { error = ex; } } game.log().warn( "Could not load image: " + path + " [error=" + error + "]"); throw error != null ? error : new FileNotFoundException(path); } @Override protected ImageImpl createImage(boolean async, int rwid, int rhei, String source) { return new Lwjgl3Image(game, async, rwid, rhei, source); } }