GLImage.java 5.79 KB
Newer Older
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
1 2 3 4 5 6 7 8 9 10 11
package com.projectseptember.RNGL;

import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
12
import android.util.Log;
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
13 14 15 16 17 18 19 20 21 22 23 24 25

import com.facebook.common.util.UriUtil;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

/*
This class is maintained and inspired from
https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java
also inspired from
https://github.com/CyberAgent/android-gpuimage/blob/master/library/src/jp/co/cyberagent/android/gpuimage/GPUImage.java
 */
Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
26
public class GLImage { // TODO : we need to check support for local images
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
27
    private final Context context;
Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
28
    private Uri src;
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
29 30 31 32 33
    private GLTexture texture;

    private boolean isDirty;
    private AsyncTask<Void, Void, Bitmap> task;
    private Runnable onload;
Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
34
    private RunInGLThread glScheduler;
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
35

Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
36
    public GLImage (Context context, RunInGLThread glScheduler, Runnable onload) {
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
37 38
        this.context = context;
        this.onload = onload;
Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
39
        this.glScheduler = glScheduler;
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
40 41 42
        this.texture = new GLTexture();
    }

Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
43
    public void setSrc(Uri src) {
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
44 45 46 47 48 49 50 51 52
        if (this.src == src) return;
        this.src = src;
        reloadImage();
    }

    private void reloadImage () {
        isDirty = true;
    }

Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
53 54 55
    public void onLoad (final Bitmap bitmap) {
        glScheduler.runInGLThread(new Runnable() {
            public void run() {
Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
56
                Log.i("GLImage", "loaded="+src.getPath());
Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
57 58 59 60
                texture.setPixels(bitmap);
                onload.run();
            }
        });
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
61 62 63 64 65
    }

    public GLTexture getTexture() {
        if (isDirty) {
            if (task != null) task.cancel(true);
Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
66
            task = new LoadImageUriTask(this, src).execute();
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
67 68 69 70 71
            isDirty = false;
        }
        return texture;
    }

Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
72
    public static @Nullable Uri getResourceDrawableUri(Context context, @Nullable String name) {
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
        if (name == null || name.isEmpty()) {
            return null;
        }
        name = name.toLowerCase().replace("-", "_");
        int resId = context.getResources().getIdentifier(
                name,
                "drawable",
                context.getPackageName());
        return new Uri.Builder()
                .scheme(UriUtil.LOCAL_RESOURCE_SCHEME)
                .path(String.valueOf(resId))
                .build();
    }


Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
88
    private static class LoadImageUriTask extends LoadImageTask {
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
89 90 91 92 93 94 95 96 97 98

        private final Uri mUri;

        public LoadImageUriTask(GLImage gpuImage, Uri uri) {
            super(gpuImage);
            mUri = uri;
        }

        @Override
        protected Bitmap decode(BitmapFactory.Options options) {
Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
99 100
            Log.i("GLImage", "loading...="+mUri.getPath());
            // FIXME: image loading is very long (probably decoding)... possible to re-use some React Native work ?
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
101 102 103 104 105
            try {
                InputStream inputStream;
                if (mUri.getScheme().startsWith("http") || mUri.getScheme().startsWith("https")) {
                    inputStream = new URL(mUri.toString()).openStream();
                } else {
Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
106
                    inputStream = glImage.context.getContentResolver().openInputStream(mUri);
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
107 108 109 110 111 112 113 114 115 116
                }
                return BitmapFactory.decodeStream(inputStream, null, options);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected int getImageOrientation() throws IOException {
Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
117
            Cursor cursor = glImage.context.getContentResolver().query(mUri,
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
118 119 120 121 122 123 124 125 126 127 128 129 130
                    new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);

            if (cursor == null || cursor.getCount() != 1) {
                return 0;
            }

            cursor.moveToFirst();
            int orientation = cursor.getInt(0);
            cursor.close();
            return orientation;
        }
    }

Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
131
    private static abstract class LoadImageTask extends AsyncTask<Void, Void, Bitmap> {
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
132

Gaëtan Renaudeau's avatar
wip  
Gaëtan Renaudeau committed
133
        protected GLImage glImage;
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

        public LoadImageTask (GLImage glImage) {
            this.glImage = glImage;
        }

        @Override
        protected Bitmap doInBackground(Void... params) {
            return loadResizedImage();
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            glImage.onLoad(bitmap);
        }

        protected abstract Bitmap decode(BitmapFactory.Options options);

        private Bitmap loadResizedImage() {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            decode(options);
            options = new BitmapFactory.Options();
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            options.inTempStorage = new byte[32 * 1024];
            Bitmap bitmap = decode(options);
            if (bitmap == null) {
                return null;
            }

Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
164 165
            Bitmap transformedBitmap;
            Matrix matrix = new Matrix();
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
166 167 168 169 170 171 172 173 174

            try {
                int orientation = getImageOrientation();
                if (orientation != 0) {
                    matrix.postRotate(orientation);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
175 176 177 178 179 180 181

            matrix.postScale(1, -1);

            transformedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
            bitmap.recycle();

            return transformedBitmap;
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
182 183 184 185 186
        }

        protected abstract int getImageOrientation() throws IOException;
    }
}