/**
* 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.robovm;
import loon.Graphics;
import loon.LSystem;
import loon.LTexture;
import loon.canvas.Image;
import loon.canvas.ImageImpl;
import loon.canvas.LColor;
import loon.canvas.Pattern;
import loon.jni.OpenGLES;
import loon.opengl.GL20;
import loon.utils.Scale;
import org.robovm.apple.coregraphics.CGBitmapContext;
import org.robovm.apple.coregraphics.CGBitmapInfo;
import org.robovm.apple.coregraphics.CGColorSpace;
import org.robovm.apple.coregraphics.CGImage;
import org.robovm.apple.coregraphics.CGImageAlphaInfo;
import org.robovm.apple.coregraphics.CGInterpolationQuality;
import org.robovm.apple.coregraphics.CGRect;
import org.robovm.apple.uikit.UIColor;
import org.robovm.apple.uikit.UIImage;
import org.robovm.rt.bro.ptr.IntPtr;
/*
* 在IOS环境中有个小问题,那就是UIImage提供的API操作像素不太方便,大量地址调用导致效率低下,看情况我可能需要单独构建一个纯像素的图像操作类
* (这个问题是共通的,就算不使用robovm,而使用j2obj或mono转换代码也一样)
*/
public class RoboVMImage extends ImageImpl {
private CGImage image;
public RoboVMImage(Graphics gfx, Scale scale, CGImage img, String source) {
super(gfx, scale, (int) img.getWidth(), (int) img.getHeight(), source,
img);
}
public RoboVMImage(RoboVMGame game, boolean async, int preWidth,
int preHeight, String source) {
super(game, async, Scale.ONE, preWidth, preHeight, source);
}
public CGImage cgImage() {
return image;
}
public UIImage toUIImage() {
return new UIImage(cgImage());
}
@Override
public Pattern createPattern(boolean repeatX, boolean repeatY) {
if (image == null) {
throw new IllegalStateException(
"Can't create pattern from un-ready image.");
}
return new RoboVMPattern(UIColor.fromPatternImage(toUIImage())
.getCGColor(), repeatX, repeatY);
}
@Override
public void getRGB(int startX, int startY, int width, int height,
int[] rgbArray, int offset, int scanSize) {
if (width <= 0 || height <= 0){
return;
}
int bytesPerRow = 4 * width;
CGBitmapContext context = CGBitmapContext.create(width, height, 8,
bytesPerRow, CGColorSpace.createDeviceRGB(), new CGBitmapInfo(
CGImageAlphaInfo.PremultipliedFirst.value()));
context.setInterpolationQuality(CGInterpolationQuality.None);
draw(context, 0, 0, width, height, startX, startY, width, height);
int[] pixelData = new int[height * width * 4];
context.getData().get(pixelData);
int i = 0;
int dst = offset;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int r = pixelData[i++];
int g = pixelData[i++];
int b = pixelData[i++];
int a = pixelData[i++];
rgbArray[dst + x] = a << 24 | r << 16 | g << 8 | b;
}
dst += scanSize;
}
context.dispose();
}
@Override
public void setRGB(int startX, int startY, int width, int height,
int[] rgbArray, int offset, int scanSize) {
if (width <= 0 || height <= 0){
return;
}
int bytesPerRow = 4 * width;
CGBitmapContext context = CGBitmapContext.create(width, height, 8,
bytesPerRow, CGColorSpace.createDeviceRGB(), new CGBitmapInfo(
CGImageAlphaInfo.PremultipliedFirst.value()));
context.setInterpolationQuality(CGInterpolationQuality.None);
draw(context, 0, 0, width, height, startX, startY, width, height);
int[] pixelData = new int[height * width * 4];
context.getData().get(pixelData);
int i = 0;
int dst = offset;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int argb = rgbArray[dst + x];
pixelData[i++] = (argb >> 16) & 255;
pixelData[i++] = (argb >> 8) & 255;
pixelData[i++] = (argb) & 255;
pixelData[i++] = (argb >> 24) & 255;
}
dst += scanSize;
}
context.getData().set(pixelData);
context.dispose();
}
@Override
public Image transform(BitmapTransformer xform) {
UIImage ximage = new UIImage(
((RoboVMTransformer) xform).transform(cgImage()));
return new RoboVMImage(gfx, scale, ximage.getCGImage(), source);
}
@Override
public void draw(Object ctx, float x, float y, float width, float height) {
CGBitmapContext bctx = (CGBitmapContext) ctx;
y += height;
bctx.saveGState();
bctx.translateCTM(x, y);
bctx.scaleCTM(1, -1);
bctx.drawImage(new CGRect(0, 0, width, height), cgImage());
bctx.restoreGState();
}
@Override
public void draw(Object ctx, float dx, float dy, float dw, float dh,
float sx, float sy, float sw, float sh) {
sx *= scale.factor;
sy *= scale.factor;
sw *= scale.factor;
sh *= scale.factor;
CGImage image = cgImage();
CGBitmapContext bctx = (CGBitmapContext) ctx;
float iw = image.getWidth(), ih = image.getHeight();
float scaleX = dw / (sw - sx), scaleY = dh / (sh - sy);
bctx.saveGState();
bctx.translateCTM(dx, dy + dh);
bctx.scaleCTM(1, -1);
bctx.clipToRect(new CGRect(0, 0, dw, dh));
bctx.translateCTM(-sx * scaleX, -(ih - (sy + sh)) * scaleY);
bctx.drawImage(new CGRect(0, 0, iw * scaleX, ih * scaleY), image);
bctx.restoreGState();
}
@Override
public String toString() {
return "Image[src=" + source + ", cgimg=" + image + "]";
}
public void dispose() {
if (image != null) {
image.dispose();
image = null;
}
}
protected RoboVMImage(Graphics gfx, Scale scale, int pixelWidth,
int pixelHeight, String source) {
super(gfx, scale, pixelWidth, pixelHeight, null, source);
}
@Override
public void upload(Graphics gfx, LTexture tex) {
int width = pixelWidth, height = pixelHeight;
if (width == 0 || height == 0) {
((RoboVMGraphics) gfx).game.log().info(
"Ignoring texture update for empty image (" + width + "x"
+ height + ").");
return;
}
CGBitmapContext bctx = RoboVMGraphics.createCGBitmap(width, height);
CGRect rect = new CGRect(0, 0, width, height);
bctx.clearRect(rect);
bctx.drawImage(rect, image);
upload(gfx, tex.getID(), width, height, bctx.getData());
bctx.dispose();
}
protected void upload(Graphics gfx, int tex, int width, int height,
IntPtr data) {
gfx.gl.glBindTexture(GL20.GL_TEXTURE_2D, tex);
gfx.gl.glPixelStorei(GL20.GL_UNPACK_ALIGNMENT, 1);
OpenGLES.glTexImage2Dp(GL20.GL_TEXTURE_2D, 0, GL20.GL_RGBA, width,
height, 0, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, data);
}
@Override
protected void setBitmap(Object bitmap) {
image = (CGImage) bitmap;
}
@Override
protected Object createErrorBitmap(int pixelWidth, int pixelHeight) {
return RoboVMGraphics.createCGBitmap(pixelWidth, pixelHeight).toImage();
}
public void getLight(Image buffer, int v) {
int width = (int) buffer.width();
int height = (int) buffer.height();
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
int rgbValue = buffer.getRGB(x, y);
if (rgbValue != 0) {
int color = getLight(rgbValue, v);
buffer.setRGB(color, x, y);
}
}
}
}
public int getLight(int color, int v) {
int red = LColor.getRed(color);
int green = LColor.getGreen(color);
int blue = LColor.getBlue(color);
red += v;
green += v;
blue += v;
blue = blue > 255 ? 255 : blue;
red = red > 255 ? 255 : red;
green = green > 255 ? 255 : green;
red = red < 0 ? 0 : red;
green = green < 0 ? 0 : green;
blue = blue < 0 ? 0 : blue;
return LColor.getRGB(red, green, blue);
}
@Override
public int[] getPixels() {
int width = (int) image.getWidth();
int height = (int) image.getHeight();
int[] rgbArray = new int[width * height];
return getPixels(rgbArray);
}
@Override
public int[] getPixels(int[] pixels) {
int width = (int) image.getWidth();
int height = (int) image.getHeight();
int length = width * height;
int[] rgbArray = new int[length];
getRGB(0, 0, width, height, rgbArray, length, width);
return rgbArray;
}
@Override
public int[] getPixels(int x, int y, int w, int h) {
return getPixels(0, w * h, x, y, w, h);
}
@Override
public int[] getPixels(int offset, int stride, int x, int y, int width,
int height) {
int[] rgbArray = new int[width * height];
getRGB(x, y, width, height, rgbArray, offset, width);
return rgbArray;
}
@Override
public int[] getPixels(int[] pixels, int offset, int stride, int x, int y,
int width, int height) {
getRGB(x, y, width, height, pixels, offset, stride);
return pixels;
}
@Override
public void setPixels(int[] pixels, int width, int height) {
setPixels(pixels, 0, width, 0, 0, width, height);
}
@Override
public void setPixels(int[] pixels, int offset, int stride, int x, int y,
int width, int height) {
setRGB(x, y, width, height, pixels, offset, stride);
}
@Override
public int[] setPixels(int[] pixels, int x, int y, int w, int h) {
setRGB(x, y, w, h, pixels, 0, w);
return pixels;
}
@Override
public void setPixel(LColor c, int x, int y) {
setPixel(c.getARGB(), x, y);
}
@Override
public void setPixel(int rgb, int x, int y) {
setRGB(rgb, x, y);
}
@Override
public int getPixel(int x, int y) {
int width = (int) image.getWidth();
int height = (int) image.getHeight();
byte bytesPerPixel = 4;
int bytesPerRow = bytesPerPixel * width;
CGBitmapContext context = CGBitmapContext.create(width, height, 8,
bytesPerRow, CGColorSpace.createDeviceRGB(), new CGBitmapInfo(
CGImageAlphaInfo.PremultipliedFirst.value()));
context.setInterpolationQuality(CGInterpolationQuality.None);
draw(context, 0, 0, width, height);
int[] pixelData = new int[height * width * 4];
context.getData().get(pixelData);
int rowOffset = y * bytesPerRow;
int colOffset = x * bytesPerPixel;
int pixelDataLoc = rowOffset + colOffset;
int r = pixelData[pixelDataLoc + 0];
int g = pixelData[pixelDataLoc + 1];
int b = pixelData[pixelDataLoc + 2];
int a = pixelData[pixelDataLoc + 3];
context.dispose();
return LColor.getARGB(r, g, b, a);
}
@Override
public int getRGB(int x, int y) {
return getPixel(x, y);
}
@Override
public void setRGB(int rgb, int x, int y) {
int width = (int) image.getWidth();
int height = (int) image.getHeight();
byte bytesPerPixel = 4;
int bytesPerRow = bytesPerPixel * width;
CGBitmapContext context = CGBitmapContext.create(width, height, 8,
bytesPerRow, CGColorSpace.createDeviceRGB(), new CGBitmapInfo(
CGImageAlphaInfo.PremultipliedFirst.value()));
context.setInterpolationQuality(CGInterpolationQuality.None);
draw(context, 0, 0, width, height);
int[] pixelData = new int[height * width * 4];
context.getData().get(pixelData);
int rowOffset = y * bytesPerRow;
int colOffset = x * bytesPerPixel;
int pixelDataLoc = rowOffset + colOffset;
pixelData[pixelDataLoc + 0] = (rgb >> 16) & 255;
pixelData[pixelDataLoc + 1] = (rgb >> 8) & 255;
pixelData[pixelDataLoc + 2] = (rgb) & 255;
pixelData[pixelDataLoc + 3] = (rgb >> 24) & 255;
context.getData().set(pixelData);
context.dispose();
}
@Override
public boolean hasAlpha() {
return image.getAlphaInfo().equals(CGImageAlphaInfo.Last)
|| image.getAlphaInfo().equals(CGImageAlphaInfo.First);
}
@Override
public Image getSubImage(int x, int y, int width, int height) {
return new RoboVMImage(LSystem.base().graphics(), LSystem.base()
.graphics().scale(), CGImage.createWithImageInRect(image,
new CGRect(x, y, width, height)), "<canvas>");
}
@Override
protected void finalize() {
dispose();
}
@Override
protected void closeImpl() {
this.dispose();
}
}