All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jp.co.cyberagent.android.gpuimage.GPUImage Maven / Gradle / Ivy

/*
 * Copyright (C) 2012 CyberAgent
 *
 * 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 jp.co.cyberagent.android.gpuimage;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.Semaphore;

import jp.co.cyberagent.android.gpuimage.GPUImageRenderer.Rotation;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ConfigurationInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.media.ExifInterface;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.opengl.GLSurfaceView;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.provider.MediaStore;
import android.view.Display;
import android.view.WindowManager;

/**
 * The main accessor for GPUImage functionality. This class helps to do common
 * tasks through a simple interface.
 */
public class GPUImage {
    private final Context mContext;
    private final GPUImageRenderer mRenderer;
    private GLSurfaceView mGlSurfaceView;
    private GPUImageFilter mFilter;
    private Bitmap mCurrentBitmap;

    /**
     * Instantiates a new GPUImage object.
     * 
     * @param context the context
     */
    public GPUImage(final Context context) {
        if (!supportsOpenGLES2(context)) {
            throw new IllegalStateException("OpenGL ES 2.0 is not supported on this phone.");
        }

        mContext = context;
        mFilter = new GPUImageFilter();
        mRenderer = new GPUImageRenderer(mFilter);
    }

    /**
     * Checks if OpenGL ES 2.0 is supported on the current device.
     * 
     * @param context the context
     * @return true, if successful
     */
    private boolean supportsOpenGLES2(final Context context) {
        final ActivityManager activityManager = (ActivityManager)
                context.getSystemService(Context.ACTIVITY_SERVICE);
        final ConfigurationInfo configurationInfo =
                activityManager.getDeviceConfigurationInfo();
        final boolean supportsEs2 = configurationInfo.reqGlEsVersion >=
                0x20000;
        return supportsEs2;
    }

    /**
     * Sets the GLSurfaceView which will display the preview.
     * 
     * @param view the GLSurfaceView
     */
    public void setGLSurfaceView(final GLSurfaceView view) {
        mGlSurfaceView = view;
        mGlSurfaceView.setEGLContextClientVersion(2);
        mGlSurfaceView.setRenderer(mRenderer);
        mGlSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
        mGlSurfaceView.requestRender();
    }

    /**
     * Request the preview to be rendered again.
     */
    public void requestRender() {
        if (mGlSurfaceView != null) {
            mGlSurfaceView.requestRender();
        }
    }

    /**
     * Sets the up camera to be connected to GPUImage to get a filtered preview.
     * 
     * @param camera the camera
     */
    public void setUpCamera(final Camera camera) {
        setUpCamera(camera, 0, false, false);
    }

    /**
     * Sets the up camera to be connected to GPUImage to get a filtered preview.
     * 
     * @param camera the camera
     * @param degrees by how many degrees the image should be rotated
     * @param flipHorizontal if the image should be flipped horizontally
     * @param flipVertical if the image should be flipped vertically
     */
    public void setUpCamera(final Camera camera, final int degrees, final boolean flipHorizontal,
            final boolean flipVertical) {
        mGlSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) {
            setUpCameraGingerbread(camera);
        } else {
            camera.setPreviewCallback(mRenderer);
            camera.startPreview();
        }
        Rotation rotation = Rotation.NORMAL;
        switch (degrees) {
            case 90:
                rotation = Rotation.ROTATION_90;
                break;
            case 180:
                rotation = Rotation.ROTATION_180;
                break;
            case 270:
                rotation = Rotation.ROTATION_270;
                break;
        }
        mRenderer.setRotation(rotation, flipHorizontal, flipVertical);
    }

    @TargetApi(11)
    private void setUpCameraGingerbread(final Camera camera) {
        mRenderer.setUpSurfaceTexture(camera);
    }

    /**
     * Sets the filter which should be applied to the image which was (or will
     * be) set by setImage(...).
     * 
     * @param filter the new filter
     */
    public void setFilter(final GPUImageFilter filter) {
        mFilter = filter;
        mRenderer.setFilter(mFilter);
        requestRender();
    }

    /**
     * Sets the image on which the filter should be applied.
     * 
     * @param bitmap the new image
     */
    public void setImage(final Bitmap bitmap) {
        setImage(bitmap, false);
        mCurrentBitmap = bitmap;
    }

    private void setImage(final Bitmap bitmap, final boolean recycle) {
        mRenderer.setImageBitmap(bitmap, recycle);
        requestRender();
    }

    /**
     * Sets the image on which the filter should be applied from a Uri.
     * 
     * @param uri the uri of the new image
     */
    public void setImage(final Uri uri) {
        setImage(new File(getPath(uri)));
    }

    /**
     * Sets the image on which the filter should be applied from a File.
     * 
     * @param file the file of the new image
     */
    public void setImage(final File file) {
        new LoadImageTask(this, file).run();
    }

    private String getPath(final Uri uri) {
        String[] projection = {
                MediaStore.Images.Media.DATA,
        };
        Cursor cursor = mContext.getContentResolver()
                .query(uri, projection, null, null, null);
        int pathIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        String path = null;
        if (cursor.moveToFirst()) {
            path = cursor.getString(pathIndex);
        }
        cursor.close();
        return path;
    }

    /**
     * Gets the current displayed image with applied filter as a Bitmap.
     * 
     * @return the current image with filter applied
     */
    public Bitmap getBitmapWithFilterApplied() {
        return getBitmapWithFilterApplied(mCurrentBitmap);
    }

    /**
     * Gets the given bitmap with current filter applied as a Bitmap.
     * 
     * @param bitmap the bitmap on which the current filter should be applied
     * @return the bitmap with filter applied
     */
    public Bitmap getBitmapWithFilterApplied(final Bitmap bitmap) {
        if (mGlSurfaceView != null) {
            mRenderer.deleteImage();
            final Semaphore lock = new Semaphore(0);
            mRenderer.runOnDraw(new Runnable() {

                @Override
                public void run() {
                    mFilter.onDestroy();
                    lock.release();
                }
            });
            requestRender();

            try {
                lock.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        GPUImageRenderer renderer = new GPUImageRenderer(mFilter);
        renderer.setRotation(Rotation.NORMAL,
                mRenderer.isFlippedHorizontally(), mRenderer.isFlippedVertically());
        PixelBuffer buffer = new PixelBuffer(bitmap.getWidth(), bitmap.getHeight());
        buffer.setRenderer(renderer);
        renderer.setImageBitmap(bitmap, false);
        Bitmap result = buffer.getBitmap();
        mFilter.onDestroy();
        renderer.deleteImage();
        buffer.destroy();

        mRenderer.setFilter(mFilter);
        if (mCurrentBitmap != null) {
            mRenderer.setImageBitmap(mCurrentBitmap, false);
        }
        requestRender();

        return result;
    }

    /**
     * Gets the images for multiple filters on a image. This can be used to
     * quickly get thumbnail images for filters. 
* Whenever a new Bitmap is ready, the listener will be called with the * bitmap. The order of the calls to the listener will be the same as the * filter order. * * @param bitmap the bitmap on which the filters will be applied * @param filters the filters which will be applied on the bitmap * @param listener the listener on which the results will be notified */ public static void getBitmapForMultipleFilters(final Bitmap bitmap, final List filters, final ResponseListener listener) { if (filters.isEmpty()) { return; } GPUImageRenderer renderer = new GPUImageRenderer(filters.get(0)); renderer.setImageBitmap(bitmap, false); PixelBuffer buffer = new PixelBuffer(bitmap.getWidth(), bitmap.getHeight()); buffer.setRenderer(renderer); for (GPUImageFilter filter : filters) { renderer.setFilter(filter); listener.response(buffer.getBitmap()); filter.onDestroy(); } renderer.deleteImage(); buffer.destroy(); } /** * Save current image with applied filter to Pictures. It will be stored on * the default Picture folder on the phone below the given folerName and * fileName.
* This method is async and will notify when the image was saved through the * listener. * * @param folderName the folder name * @param fileName the file name * @param listener the listener */ public void saveToPictures(final String folderName, final String fileName, final OnPictureSavedListener listener) { saveToPictures(mCurrentBitmap, folderName, fileName, listener); } /** * Apply and save the given bitmap with applied filter to Pictures. It will * be stored on the default Picture folder on the phone below the given * folerName and fileName.
* This method is async and will notify when the image was saved through the * listener. * * @param bitmap the bitmap * @param folderName the folder name * @param fileName the file name * @param listener the listener */ public void saveToPictures(final Bitmap bitmap, final String folderName, final String fileName, final OnPictureSavedListener listener) { new SaveTask(bitmap, folderName, fileName, listener).execute(); } private class SaveTask extends AsyncTask { private final Bitmap mBitmap; private final String mFolderName; private final String mFileName; private final OnPictureSavedListener mListener; private final Handler mHandler; public SaveTask(final Bitmap bitmap, final String folderName, final String fileName, final OnPictureSavedListener listener) { mBitmap = bitmap; mFolderName = folderName; mFileName = fileName; mListener = listener; mHandler = new Handler(); } @Override protected Void doInBackground(final Void... params) { Bitmap result = getBitmapWithFilterApplied(mBitmap); saveImage(mFolderName, mFileName, result); return null; } private void saveImage(final String folderName, final String fileName, final Bitmap image) { File path = Environment .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); File file = new File(path, folderName + "/" + fileName); try { file.getParentFile().mkdirs(); image.compress(CompressFormat.JPEG, 80, new FileOutputStream(file)); MediaScannerConnection.scanFile(mContext, new String[] { file.toString() }, null, new MediaScannerConnection.OnScanCompletedListener() { @Override public void onScanCompleted(final String path, final Uri uri) { if (mListener != null) { mHandler.post(new Runnable() { @Override public void run() { mListener.onPictureSaved(uri); } }); } } }); } catch (FileNotFoundException e) { e.printStackTrace(); } } } public interface OnPictureSavedListener { void onPictureSaved(Uri uri); } private class LoadImageTask implements Runnable { private final GPUImage mGPUImage; private final File mImageFile; private final int mMaxWidth; private final int mMaxHeight; @SuppressWarnings("deprecation") public LoadImageTask(final GPUImage gpuImage, final File file) { mImageFile = file; mGPUImage = gpuImage; WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); Display display = windowManager.getDefaultDisplay(); mMaxWidth = display.getWidth(); mMaxHeight = display.getHeight(); } @Override public void run() { Bitmap bitmap = loadResizedImage(mImageFile); mGPUImage.setImage(bitmap); } private Bitmap loadResizedImage(final File imageFile) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options); int scale = 1; while (options.outWidth / scale > mMaxWidth || options.outHeight / scale > mMaxHeight) { scale++; } Bitmap bitmap = null; Bitmap scaledBitmap = null; if (scale > 1) { scale--; options = new BitmapFactory.Options(); options.inSampleSize = scale; options.inPreferredConfig = Bitmap.Config.RGB_565; options.inPurgeable = true; options.inTempStorage = new byte[32 * 1024]; bitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options); if (bitmap == null) { return null; } // resize to desired dimensions int width = bitmap.getWidth(); int height = bitmap.getHeight(); double newWidth; double newHeight; if ((double) width / mMaxWidth < (double) height / mMaxHeight) { newHeight = mMaxHeight; newWidth = (newHeight / height) * width; } else { newWidth = mMaxWidth; newHeight = (newWidth / width) * height; } scaledBitmap = Bitmap.createScaledBitmap(bitmap, Math.round((float) newWidth), Math.round((float) newHeight), true); bitmap.recycle(); bitmap = scaledBitmap; System.gc(); } else { bitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath()); } return rotateImage(bitmap, imageFile); } private Bitmap rotateImage(final Bitmap bitmap, final File fileWithExifInfo) { if (bitmap == null) { return null; } Bitmap rotatedBitmap = bitmap; int orientation = 0; try { orientation = getImageOrientation(fileWithExifInfo.getAbsolutePath()); if (orientation != 0) { Matrix matrix = new Matrix(); matrix.postRotate(orientation); rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); bitmap.recycle(); } } catch (IOException e) { e.printStackTrace(); } return rotatedBitmap; } private int getImageOrientation(final String file) throws IOException { ExifInterface exif = new ExifInterface(file); int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1); switch (orientation) { case ExifInterface.ORIENTATION_NORMAL: return 0; case ExifInterface.ORIENTATION_ROTATE_90: return 90; case ExifInterface.ORIENTATION_ROTATE_180: return 180; case ExifInterface.ORIENTATION_ROTATE_270: return 270; default: return 0; } } } public interface ResponseListener { void response(T item); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy