com.adobe.plugins.FastCanvasView.java Source code

Java tutorial

Introduction

Here is the source code for com.adobe.plugins.FastCanvasView.java

Source

/**
 * Copyright 2012 Adobe Systems Incorporated
 *
 * 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 com.adobe.plugins;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;

import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES10;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.os.Environment;
import android.util.Base64;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

public class FastCanvasView extends GLSurfaceView {

    private FastCanvas plugin;
    private LinkedList<Command> queue = new LinkedList<Command>();
    private Map<Integer, Texture> textures = new HashMap<Integer, Texture>();
    private String renderCommand;

    public FastCanvasView(Context context, FastCanvas plugin) {
        super(context);
        this.plugin = plugin;
        this.setEGLConfigChooser(false);// turn off the depth buffer
        this.setRenderer(new FastCanvasRenderer());
        this.setRenderMode(RENDERMODE_CONTINUOUSLY);

        this.setFocusableInTouchMode(true);
        this.requestFocus();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        return plugin.webView.onKeyDown(keyCode, event);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        return plugin.webView.onKeyUp(keyCode, event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return plugin.webView.dispatchTouchEvent(event);
    }

    @Override
    public void onResume() {
        Log.i(TAG, "onResume");
        super.onResume();
    }

    @Override
    public void onPause() {
        Log.i(TAG, "onPause");
        FastCanvasJNI.contextLost();
        super.onPause();
    }

    public void surfaceCreated(SurfaceHolder holder) {
        Log.i(TAG, "surfaceCreated");
        onResume();
        super.surfaceCreated(holder);
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        Log.i(TAG, "surfaceChanged");
        super.surfaceChanged(holder, format, w, h);
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.i(TAG, "surfaceDestroyed");
        onPause();
        FastCanvasJNI.release();
        super.surfaceDestroyed(holder);
    }

    public class FastCanvasRenderer implements GLSurfaceView.Renderer {

        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            Log.i(TAG, "onSurfaceCreated: " + gl + ", " + config);

            IntBuffer ib = IntBuffer.allocate(100);
            ib.position(0);
            GLES10.glGetIntegerv(GLES10.GL_RED_BITS, ib);
            int red = ib.get(0);
            GLES10.glGetIntegerv(GLES10.GL_GREEN_BITS, ib);
            int green = ib.get(0);
            GLES10.glGetIntegerv(GLES10.GL_BLUE_BITS, ib);
            int blue = ib.get(0);
            GLES10.glGetIntegerv(GLES10.GL_STENCIL_BITS, ib);
            int stencil = ib.get(0);
            GLES10.glGetIntegerv(GLES10.GL_DEPTH_BITS, ib);
            int depth = ib.get(0);

            Log.i(TAG, "onSurfaceCreated R: " + red + " G: " + green + " B: " + blue + " DEPETH: " + depth
                    + " STENCIL: " + stencil);

            for (final Texture texture : textures.values()) {
                Log.i(TAG, "queue reload texture: " + texture);
                queue.offer(new Command() {
                    @Override
                    public void exec() {
                        loadTexture(texture, null);
                    }
                });
            }
        }

        public void onSurfaceChanged(GL10 gl, int width, int height) {
            Log.i(TAG, "onSurfaceChanged");
            FastCanvasJNI.surfaceChanged(width, height);
        }

        public void onDrawFrame(GL10 gl) {
            if (!queue.isEmpty()) {
                Log.i(TAG, "dump command queue");
                Command cmd;
                while ((cmd = queue.poll()) != null) {
                    try {
                        cmd.exec();
                    } catch (Exception e) {
                        Log.e(TAG, "", e);
                    }
                }
            }

            if (renderCommand != null) {
                FastCanvasJNI.render(renderCommand);
                checkError();
            }
        }

        private static final String TAG = "FastCanvasRenderer";
    }

    private void unloadTexture(int id) {
        Log.i(TAG, "unload texture: " + id);
        Texture old = textures.get(id);
        if (old != null && old.loaded) {
            Log.i(TAG, "unload texture: " + old);
            old.unload();
            textures.remove(id);
        }
    }

    private void loadTexture(Texture texture, CallbackContext callback) {

        Log.i(TAG, "load texture: " + texture);

        unloadTexture(texture.id);

        try {
            FastCanvasTextureDimension dim = texture.load();
            textures.put(texture.id, texture);

            Log.i(TAG, "loaded texture: " + texture);

            if (callback != null) {
                JSONArray args = new JSONArray();
                args.put(dim.width);
                args.put(dim.height);
                callback.success(args);
            }

        } catch (Exception e) {
            Log.i(TAG, "load texture error: ", e);
            if (callback != null) {
                callback.error(e.getMessage());
            }
        }
    }

    interface Command {
        void exec();
    }

    class Texture {
        public final String url;
        public final int id;
        public boolean loaded;

        public Texture(String url, int id) {
            this.url = url;
            this.id = id;
        }

        @Override
        public String toString() {
            return url.substring(0, 30) + "#" + id + "@" + hashCode();
        }

        private void unload() {
            FastCanvasJNI.removeTexture(this.id);
            this.loaded = false;
            checkError();
        }

        private FastCanvasTextureDimension load() throws IOException {

            FastCanvasTextureDimension dim = new FastCanvasTextureDimension();
            Bitmap bmp;

            if (this.url.startsWith("data:")) {
                Log.i(TAG, "DataURL[" + this.url.length() + "] = " + this.url);
                String encodedData = this.url.replaceFirst("^data:image/(png|jpg|jpeg);base64,", "");
                Log.i(TAG, "EncodedData[" + encodedData.length() + "] = " + encodedData);

                byte[] decodedData = Base64.decode(encodedData, Base64.DEFAULT);
                Log.i(TAG, "DecodedData[" + decodedData.length + "] = " + decodedData);

                bmp = BitmapFactory.decodeByteArray(decodedData, 0, decodedData.length);
                Log.i(TAG, "bmp = " + bmp);
            } else {
                String path = "www/" + this.url;

                AssetManager assets = getContext().getAssets();

                // PNG files with premultiplied
                // alpha and GLUtils don't get along
                // http://stackoverflow.com/questions/3921685
                if (path.toLowerCase(Locale.US).endsWith(".png")) {
                    if (FastCanvasJNI.addPngTexture(assets, path, this.id, dim)) {
                        this.loaded = true;
                        return dim;
                    }
                    Log.i(TAG, "native PNG load filaed, falling back to GLUtils");
                }

                bmp = BitmapFactory.decodeStream(assets.open(path));
            }

            int[] glID = new int[1];
            GLES10.glGenTextures(1, glID, 0);
            GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, glID[0]);
            GLES10.glTexParameterf(GLES10.GL_TEXTURE_2D, GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_LINEAR);
            GLES10.glTexParameterf(GLES10.GL_TEXTURE_2D, GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR);

            int width = bmp.getWidth(), height = bmp.getHeight();
            int p2width = 2, p2height = 2;
            while (p2width < width)
                p2width *= 2;
            while (p2height < height)
                p2height *= 2;

            if (width == p2width && height == p2height) {
                GLUtils.texImage2D(GLES10.GL_TEXTURE_2D, 0, bmp, 0);
            } else {
                Log.i(TAG, "load texture scaling texture " + this + " to power of 2");
                GLES10.glTexImage2D(GLES10.GL_TEXTURE_2D, 0, GLES10.GL_RGBA, p2width, p2height, 0, GLES10.GL_RGBA,
                        GLES10.GL_UNSIGNED_BYTE, null);
                GLUtils.texSubImage2D(GLES10.GL_TEXTURE_2D, 0, 0, 0, bmp);
                width = p2width;
                height = p2height;
            }

            checkError();

            FastCanvasJNI.addTexture(this.id, glID[0], width, height);
            Log.i(TAG, "load texture done: " + this);

            dim.width = bmp.getWidth();
            dim.height = bmp.getHeight();
            Log.i(TAG, "bitmap = " + bmp.getWidth() + "x" + bmp.getHeight());

            this.loaded = true;
            return dim;
        }

        private static final String TAG = "FastCanvasTexture";
    }

    public boolean execute(String action, JSONArray args, final CallbackContext callbackContext)
            throws JSONException {
        Log.i(TAG, "execute: " + action);

        try {

            if (action.equals("render")) {
                renderCommand = args.getString(0);
                return true;

            } else if (action.equals("setBackgroundColor")) {
                String color = args.getString(0);
                Log.i(TAG, "setBackground: " + color);
                try {
                    int red = Integer.valueOf(color.substring(0, 2), 16);
                    int green = Integer.valueOf(color.substring(2, 4), 16);
                    int blue = Integer.valueOf(color.substring(4, 6), 16);
                    FastCanvasJNI.setBackgroundColor(red, green, blue);
                } catch (Exception e) {
                    Log.e(TAG, "Invalid background color: " + color, e);
                }

                return true;

            } else if (action.equals("loadTexture")) {
                final Texture texture = new Texture(args.getString(0), args.getInt(1));
                Log.i(TAG, "loadTexture " + texture);
                queue.offer(new Command() {
                    @Override
                    public void exec() {
                        loadTexture(texture, callbackContext);
                    }
                });
                return true;

            } else if (action.equals("unloadTexture")) {
                Log.i(TAG, "unload texture");
                int id = args.getInt(0);
                unloadTexture(id);

                return true;

            } else if (action.equals("setOrtho")) {
                int width = args.getInt(0);
                int height = args.getInt(1);

                Log.i(TAG, "setOrtho: " + width + ", " + height);
                FastCanvasJNI.setOrtho(width, height);

                return true;

            } else if (action.equals("capture")) {
                Log.i(TAG, "capture");

                // set the root path to /mnt/sdcard/
                String file = Environment.getExternalStorageDirectory() + args.getString(4);
                File dir = new File(file).getParentFile();
                if (!dir.isDirectory() && !dir.mkdirs()) {
                    callbackContext.sendPluginResult(
                            new PluginResult(PluginResult.Status.ERROR, "Could not create directory"));
                    return true;
                }

                int x = args.optInt(0, 0);
                int y = args.optInt(1, 0);
                int width = args.optInt(2, -1);
                int height = args.optInt(3, -1);

                FastCanvasJNI.captureGLLayer(callbackContext.getCallbackId(), x, y, width, height, file);

                return true;

            } else if (action.equals("toDataURL")) {
                Log.i(TAG, "toDataURL");

                String mimeType = args.getString(0);
                int quality = args.getInt(1);
                int width = args.getInt(2);
                int height = args.getInt(3);
                Log.i(TAG, "toDataURL[" + mimeType + "][" + quality + "] = " + width + "x" + height);
                // toDataURL(mimeType, quality);

                byte[] pixels = FastCanvasJNI.captureGLLayerDirect(width, height);
                Log.i(TAG, "toDataURL::pixels = " + pixels + " = " + pixels.length);

                ByteArrayOutputStream out = new ByteArrayOutputStream();
                Bitmap bmp = BitmapFactory.decodeByteArray(pixels, 0, pixels.length);
                bmp.compress(Bitmap.CompressFormat.JPEG, quality, out);

                String dataURL = "data:" + mimeType + ";base64," + Base64.encodeToString(out.toByteArray(), 0);

                callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, dataURL));
                return true;

            } else if (action.equals("isAvailable")) {
                callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, true));
                return true;

            } else {
                Log.i(TAG, "invalid execute action: " + action);
            }

        } catch (Exception e) {
            Log.e(TAG, "error executing action: " + action + "(" + args + ")", e);
        }

        return false;
    }

    private void checkError() {
        int error = GLES10.glGetError();
        if (error != GLES10.GL_NO_ERROR) {
            Log.i(TAG, "glError=" + error);
        }
        assert error == GLES10.GL_NO_ERROR;
    }

    private static final String TAG = "FastCanvasView";
}