/** * 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.canvas; import loon.BaseIO; import loon.Graphics; import loon.LRelease; import loon.LSystem; import loon.LTexture; import loon.geom.Vector2f; import loon.opengl.Painter; import loon.opengl.TextureSource; import loon.utils.ArrayByte; import loon.utils.MathUtils; import loon.utils.Scale; import loon.utils.TArray; import loon.utils.reply.Function; import loon.utils.reply.GoFuture; public abstract class Image extends TextureSource implements Canvas.Drawable, LRelease { private boolean isTexture = false; private boolean haveToClose = false; public boolean toClose() { return haveToClose; } public static Canvas createCanvas(float w, float h) { return createCanvas((int) w, (int) h); } public static Canvas createCanvas(int w, int h) { return LSystem.base().graphics().createCanvas(w, h); } public static Image createImage(float w, float h) { return createImage((int) w, (int) h); } public static Image createImage(int w, int h) { return LSystem.base().graphics().createCanvas(w, h).image; } public static Image createImage(final String path) { return BaseIO.loadImage(path); } public static Image getResize(final Image image, int w, int h) { Canvas canvas = LSystem.base().graphics().createCanvas(w, h); canvas.draw(image, 0, 0, w, h, 0, 0, image.width(), image.height()); return canvas.image; } public static Image drawClipImage(final Image image, int objectWidth, int objectHeight, int x1, int y1, int x2, int y2) { Canvas canvas = LSystem.base().graphics().createCanvas(objectWidth, objectHeight); canvas.draw(image, 0, 0, objectWidth, objectHeight, x1, y1, x2, y2); return canvas.image; } public static Image drawClipImage(final Image image, int objectWidth, int objectHeight, int x, int y) { Canvas canvas = LSystem.base().graphics().createCanvas(objectWidth, objectHeight); canvas.draw(image, 0, 0, objectWidth, objectHeight, x, y, x + objectWidth, objectHeight + y); return canvas.image; } Canvas canvas; public final GoFuture<Image> state; public Canvas getCanvas() { if (canvas == null) { return canvas = LSystem.base().graphics().createCanvas(width(), height()); } return canvas; } @Override public boolean isLoaded() { return state.isCompleteNow(); } public abstract Scale scale(); @Override public float width() { return scale().invScaled(pixelWidth()); } @Override public float height() { return scale().invScaled(pixelHeight()); } public abstract int pixelWidth(); public abstract int pixelHeight(); public abstract Pattern createPattern(boolean repeatX, boolean repeatY); public Image setFormat(LTexture.Format config) { texconf = config; return this; } public LTexture texture() { if (texture == null || texture.disposed()) { texture = createTexture(texconf); } return texture; } public LTexture updateTexture() { if (texture == null || texture.disposed()) { texture = createTexture(texconf); } else { texture.update(this); } return texture; } public GoFuture<LTexture> textureAsync() { return state.map(new Function<Image, LTexture>() { public LTexture apply(Image image) { return texture(); } }); } public LTexture createTexture(LTexture.Format config) { if (!isLoaded()) { throw new IllegalStateException("Cannot create texture from unready image: " + this); } int texWidth = config.toTexWidth(pixelWidth()); int texHeight = config.toTexHeight(pixelHeight()); if (texWidth <= 0 || texHeight <= 0) { throw new IllegalArgumentException( "Invalid texture size: " + texWidth + "x" + texHeight + " from: " + this); } this.isTexture = true; LTexture tex = new LTexture(gfx, gfx.createTexture(config), config, texWidth, texHeight, scale(), width(), height()); tex.update(this); return tex; } public static abstract class Region extends TextureSource implements Canvas.Drawable { } public Region region(final float rx, final float ry, final float rwidth, final float rheight) { return new Region() { private LTexture tile; @Override public boolean isLoaded() { return Image.this.isLoaded(); } @Override public LTexture draw() { if (tile == null) { tile = Image.this.texture().copy(rx, ry, rwidth, rheight); } return tile; } @Override public GoFuture<Painter> tileAsync() { return Image.this.state.map(new Function<Image, Painter>() { public Painter apply(Image image) { return draw(); } }); } @Override public float width() { return rwidth; } @Override public float height() { return rheight; } @Override public void draw(Object ctx, float x, float y, float width, float height) { Image.this.draw(ctx, x, y, width, height, rx, ry, rwidth, rheight); } @Override public void draw(Object ctx, float dx, float dy, float dw, float dh, float sx, float sy, float sw, float sh) { Image.this.draw(ctx, dx, dy, dw, dh, rx + sx, ry + sy, sw, sh); } }; } public static interface BitmapTransformer { } public abstract Image transform(BitmapTransformer xform); @Override public Painter draw() { return texture(); } @Override public GoFuture<Painter> tileAsync() { return state.map(new Function<Image, Painter>() { public Painter apply(Image image) { return texture(); } }); } protected final Graphics gfx; protected LTexture.Format texconf = LTexture.Format.LINEAR; protected LTexture texture; protected Image(Graphics gfx, GoFuture<Image> state) { this.gfx = gfx; this.state = state; } protected Image(Graphics gfx) { this.gfx = gfx; this.state = GoFuture.success(this); } public abstract void upload(Graphics gfx, LTexture tex); public abstract void getLight(Image buffer, int v); public abstract int getLight(int color, int v); public abstract int[] getPixels(); public abstract int[] getPixels(int pixels[]); public abstract int[] getPixels(int x, int y, int w, int h); public abstract int[] getPixels(int offset, int stride, int x, int y, int width, int height); public abstract int[] getPixels(int pixels[], int offset, int stride, int x, int y, int width, int height); public abstract void setPixels(int[] pixels, int width, int height); public abstract void setPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height); public abstract int[] setPixels(int[] pixels, int x, int y, int w, int h); public abstract void setPixel(LColor c, int x, int y); public abstract void setPixel(int rgb, int x, int y); public abstract int getPixel(int x, int y); public abstract int getRGB(int x, int y); public abstract void setRGB(int rgb, int x, int y); public abstract void getRGB(int startX, int startY, int width, int height, int[] rgbArray, int offset, int scanSize); public abstract void setRGB(int startX, int startY, int width, int height, int[] rgbArray, int offset, int scanSize); public abstract boolean hasAlpha(); public abstract String getSource(); public abstract Image getSubImage(int x, int y, int width, int height); private boolean closed; public final boolean isClosed() { return closed; } public int getWidth() { return (int) width(); } public int getHeight() { return (int) height(); } public TArray<Vector2f> getPoints(final Vector2f size, final int interval, final float scale) { final int[] pixels = getPixels(); final TArray<Vector2f> points = new TArray<Vector2f>(); for (int y = 0; y < getHeight(); y += interval) { for (int x = 0; x < getWidth(); x += interval) { int tx = MathUtils.clamp(x + MathUtils.nextInt(-interval / 2, interval / 2), 0, getWidth() - 1); int ty = MathUtils.clamp(y + MathUtils.nextInt(-interval / 2, interval / 2), 0, getHeight() - 1); int color = pixels[getWidth() * ty + tx]; if (LColor.getRed(color) == 255) { points.add((new Vector2f(tx, ty).sub(size)).mul(scale)); } } } return points; } public String getBase64() { return getRGBAsToArrayByte().toString(); } public ArrayByte getRGBAsToArrayByte() { return new ArrayByte(getRGBABytes()); } public byte[] getBGRABytes() { return getRGBABytes(true); } public byte[] getRGBABytes() { return getRGBABytes(false); } public byte[] getRGBABytes(boolean flag) { int idx = 0; final int bits = 4; final int[] pixesl = getPixels(); byte[] buffer = new byte[getWidth() * getHeight() * bits]; for (int i = 0, size = buffer.length; i < size; i += bits) { int pixel = pixesl[idx++]; if (flag) { buffer[i + 3] = (byte) (LColor.getAlpha(pixel)); buffer[i + 2] = (byte) (LColor.getRed(pixel)); buffer[i + 1] = (byte) (LColor.getGreen(pixel)); buffer[i] = (byte) (LColor.getBlue(pixel)); } else { buffer[i] = (byte) (LColor.getRed(pixel)); buffer[i + 1] = (byte) (LColor.getGreen(pixel)); buffer[i + 2] = (byte) (LColor.getBlue(pixel)); buffer[i + 3] = (byte) (LColor.getAlpha(pixel)); } } return buffer; } public byte[] getBGRBytes() { return getRGBBytes(true); } public byte[] getRGBBytes() { return getRGBBytes(false); } public byte[] getRGBBytes(boolean flag) { int idx = 0; final int bits = 3; final int[] pixesl = getPixels(); byte[] buffer = new byte[getWidth() * getHeight() * bits]; for (int i = 0, size = buffer.length; i < size; i += bits) { int pixel = pixesl[idx++]; if (flag) { buffer[i + 2] = (byte) (LColor.getRed(pixel)); buffer[i + 1] = (byte) (LColor.getGreen(pixel)); buffer[i] = (byte) (LColor.getBlue(pixel)); } else { buffer[i] = (byte) (LColor.getRed(pixel)); buffer[i + 1] = (byte) (LColor.getGreen(pixel)); buffer[i + 2] = (byte) (LColor.getBlue(pixel)); } } return buffer; } public Pixmap getPixmap() { return new Pixmap(getPixels(), getWidth(), getHeight(), hasAlpha()); } public void setPixmap(Pixmap pixmap) { setPixels(pixmap.getData(), pixmap.getWidth(), pixmap.getHeight()); } public Image onHaveToClose(boolean c) { this.haveToClose = c; return this; } public final void close() { if (!this.isTexture) { this.closeImpl(); } else { this.haveToClose = true; } this.closed = true; } public final void destroy() { this.closeImpl(); } protected abstract void closeImpl(); public final Image cpy() { return cpy(false); } public final Image cpy(boolean closed) { Canvas canvas = createCanvas(width(), height()); canvas.draw(this, 0, 0); if (closed) { this.close(); } return canvas.image; } }