1a9f280f938b5fd5891c5cfe0999f8f1d4945d7a1nicolasroard/*
2a9f280f938b5fd5891c5cfe0999f8f1d4945d7a1nicolasroard * Copyright (C) 2012 The Android Open Source Project
3a9f280f938b5fd5891c5cfe0999f8f1d4945d7a1nicolasroard *
4a9f280f938b5fd5891c5cfe0999f8f1d4945d7a1nicolasroard * Licensed under the Apache License, Version 2.0 (the "License");
5a9f280f938b5fd5891c5cfe0999f8f1d4945d7a1nicolasroard * you may not use this file except in compliance with the License.
6a9f280f938b5fd5891c5cfe0999f8f1d4945d7a1nicolasroard * You may obtain a copy of the License at
7a9f280f938b5fd5891c5cfe0999f8f1d4945d7a1nicolasroard *
8a9f280f938b5fd5891c5cfe0999f8f1d4945d7a1nicolasroard *      http://www.apache.org/licenses/LICENSE-2.0
9a9f280f938b5fd5891c5cfe0999f8f1d4945d7a1nicolasroard *
10a9f280f938b5fd5891c5cfe0999f8f1d4945d7a1nicolasroard * Unless required by applicable law or agreed to in writing, software
11a9f280f938b5fd5891c5cfe0999f8f1d4945d7a1nicolasroard * distributed under the License is distributed on an "AS IS" BASIS,
12a9f280f938b5fd5891c5cfe0999f8f1d4945d7a1nicolasroard * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a9f280f938b5fd5891c5cfe0999f8f1d4945d7a1nicolasroard * See the License for the specific language governing permissions and
14a9f280f938b5fd5891c5cfe0999f8f1d4945d7a1nicolasroard * limitations under the License.
15a9f280f938b5fd5891c5cfe0999f8f1d4945d7a1nicolasroard */
162576a29cccf1d96edeef071914e8b775c230e8e8Sascha Haeberling
172576a29cccf1d96edeef071914e8b775c230e8e8Sascha Haeberlingpackage com.android.gallery3d.filtershow.filters;
182576a29cccf1d96edeef071914e8b775c230e8e8Sascha Haeberling
192576a29cccf1d96edeef071914e8b775c230e8e8Sascha Haeberlingimport android.graphics.Bitmap;
20c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberlingimport android.graphics.Canvas;
21044513931d31a4b86f622b1af36e9ce947672be7Sascha Haeberlingimport android.graphics.RectF;
222576a29cccf1d96edeef071914e8b775c230e8e8Sascha Haeberling
23c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberlingimport com.adobe.xmp.XMPException;
24c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberlingimport com.adobe.xmp.XMPMeta;
254cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hofordimport com.android.gallery3d.app.Log;
26ec1e009a7faea0478e361bc2d48d856ab48a0209nicolasroardimport com.android.gallery3d.filtershow.cache.BitmapCache;
270c1b4c6422a4d2d9b81cc0946d1c9675440a94e2Ruben Brunkimport com.android.gallery3d.filtershow.cache.ImageLoader;
280c1b4c6422a4d2d9b81cc0946d1c9675440a94e2Ruben Brunkimport com.android.gallery3d.filtershow.imageshow.MasterImage;
29ce9ceff5776a9b0479c30cbeb2a9388b44df1865nicolasroardimport com.android.gallery3d.filtershow.pipeline.ImagePreset;
30e9c55b28a4a155c2e1c1f5aec615a0c56e38ade4John Hoford
312f879102d630815df64636b8d37e3a7eea8e8b65Sascha Haeberling/**
322f879102d630815df64636b8d37e3a7eea8e8b65Sascha Haeberling * An image filter which creates a tiny planet projection.
332f879102d630815df64636b8d37e3a7eea8e8b65Sascha Haeberling */
3471f04cbaedbb89e313e0b86b531640db2d3f6016nicolasroardpublic class ImageFilterTinyPlanet extends SimpleImageFilter {
352c6ea941a80ea22317d664e329aed51f5f7417b9nicolasroard
3644a499a64158cd5a12f8e44655f82b68c33bf3b2John Hoford
3744a499a64158cd5a12f8e44655f82b68c33bf3b2John Hoford    private static final String LOGTAG = ImageFilterTinyPlanet.class.getSimpleName();
38c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling    public static final String GOOGLE_PANO_NAMESPACE = "http://ns.google.com/photos/1.0/panorama/";
3944a499a64158cd5a12f8e44655f82b68c33bf3b2John Hoford    FilterTinyPlanetRepresentation mParameters = new FilterTinyPlanetRepresentation();
40c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling
41c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling    public static final String CROPPED_AREA_IMAGE_WIDTH_PIXELS =
42c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling            "CroppedAreaImageWidthPixels";
43c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling    public static final String CROPPED_AREA_IMAGE_HEIGHT_PIXELS =
44c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling            "CroppedAreaImageHeightPixels";
45c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling    public static final String CROPPED_AREA_FULL_PANO_WIDTH_PIXELS =
46c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling            "FullPanoWidthPixels";
47c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling    public static final String CROPPED_AREA_FULL_PANO_HEIGHT_PIXELS =
48c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling            "FullPanoHeightPixels";
49c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling    public static final String CROPPED_AREA_LEFT =
50c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling            "CroppedAreaLeftPixels";
51c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling    public static final String CROPPED_AREA_TOP =
52c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling            "CroppedAreaTopPixels";
532576a29cccf1d96edeef071914e8b775c230e8e8Sascha Haeberling
542576a29cccf1d96edeef071914e8b775c230e8e8Sascha Haeberling    public ImageFilterTinyPlanet() {
552576a29cccf1d96edeef071914e8b775c230e8e8Sascha Haeberling        mName = "TinyPlanet";
562c6ea941a80ea22317d664e329aed51f5f7417b9nicolasroard    }
572c6ea941a80ea22317d664e329aed51f5f7417b9nicolasroard
588cc3b55d615b349b4fcdff0eeeefe6907d4950ffnicolasroard    @Override
5944a499a64158cd5a12f8e44655f82b68c33bf3b2John Hoford    public void useRepresentation(FilterRepresentation representation) {
6044a499a64158cd5a12f8e44655f82b68c33bf3b2John Hoford        FilterTinyPlanetRepresentation parameters = (FilterTinyPlanetRepresentation) representation;
6144a499a64158cd5a12f8e44655f82b68c33bf3b2John Hoford        mParameters = parameters;
6244a499a64158cd5a12f8e44655f82b68c33bf3b2John Hoford    }
6344a499a64158cd5a12f8e44655f82b68c33bf3b2John Hoford
6444a499a64158cd5a12f8e44655f82b68c33bf3b2John Hoford    @Override
6544a499a64158cd5a12f8e44655f82b68c33bf3b2John Hoford    public FilterRepresentation getDefaultRepresentation() {
666900cad45d240c9a54b92991538b6a33652e766cnicolasroard        return new FilterTinyPlanetRepresentation();
67dffe6a6f8bbf4565df211b07bcfc23d6864a48b6nicolasroard    }
68dffe6a6f8bbf4565df211b07bcfc23d6864a48b6nicolasroard
6991d26f6c3b183862eeffc1856e2d758e800d13f4John Hoford
702f879102d630815df64636b8d37e3a7eea8e8b65Sascha Haeberling    native protected void nativeApplyFilter(
7167fefdb9b77644c955e1c0c5b1fcd51e2374f212John Hoford            Bitmap bitmapIn, int width, int height, Bitmap bitmapOut, int outSize, float scale,
7267fefdb9b77644c955e1c0c5b1fcd51e2374f212John Hoford            float angle);
732f879102d630815df64636b8d37e3a7eea8e8b65Sascha Haeberling
7491d26f6c3b183862eeffc1856e2d758e800d13f4John Hoford
752576a29cccf1d96edeef071914e8b775c230e8e8Sascha Haeberling    @Override
7699baf61387ab1ef15bb9db5fa3b2b55591e87059John Hoford    public Bitmap apply(Bitmap bitmapIn, float scaleFactor, int quality) {
77c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling        int w = bitmapIn.getWidth();
78c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling        int h = bitmapIn.getHeight();
79044513931d31a4b86f622b1af36e9ce947672be7Sascha Haeberling        int outputSize = (int) (w / 2f);
8036e567afff815bc821c2859ebdeec86b1fca1ef6nicolasroard        ImagePreset preset = getEnvironment().getImagePreset();
8191d26f6c3b183862eeffc1856e2d758e800d13f4John Hoford        Bitmap mBitmapOut = null;
82769d38db5736cd690ab7837c0824572739c39184Sascha Haeberling        if (preset != null) {
830c1b4c6422a4d2d9b81cc0946d1c9675440a94e2Ruben Brunk            XMPMeta xmp = ImageLoader.getXmpObject(MasterImage.getImage().getActivity());
84769d38db5736cd690ab7837c0824572739c39184Sascha Haeberling            // Do nothing, just use bitmapIn as is if we don't have XMP.
85769d38db5736cd690ab7837c0824572739c39184Sascha Haeberling            if(xmp != null) {
8691d26f6c3b183862eeffc1856e2d758e800d13f4John Hoford                bitmapIn = applyXmp(bitmapIn, xmp, w);
8791d26f6c3b183862eeffc1856e2d758e800d13f4John Hoford            }
8891d26f6c3b183862eeffc1856e2d758e800d13f4John Hoford        }
8991d26f6c3b183862eeffc1856e2d758e800d13f4John Hoford        if (mBitmapOut != null) {
9091d26f6c3b183862eeffc1856e2d758e800d13f4John Hoford            if (outputSize != mBitmapOut.getHeight()) {
9191d26f6c3b183862eeffc1856e2d758e800d13f4John Hoford                mBitmapOut = null;
92769d38db5736cd690ab7837c0824572739c39184Sascha Haeberling            }
93e9c55b28a4a155c2e1c1f5aec615a0c56e38ade4John Hoford        }
944cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford        while (mBitmapOut == null) {
954cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford            try {
96ec1e009a7faea0478e361bc2d48d856ab48a0209nicolasroard                mBitmapOut = getEnvironment().getBitmap(outputSize,
97ec1e009a7faea0478e361bc2d48d856ab48a0209nicolasroard                        outputSize, BitmapCache.TINY_PLANET);
984cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford            } catch (java.lang.OutOfMemoryError e) {
994cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford                System.gc();
1004cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford                outputSize /= 2;
10144a499a64158cd5a12f8e44655f82b68c33bf3b2John Hoford                Log.v(LOGTAG, "No memory to create Full Tiny Planet create half");
1024cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford            }
1034cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford        }
104c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling        nativeApplyFilter(bitmapIn, bitmapIn.getWidth(), bitmapIn.getHeight(), mBitmapOut,
10544a499a64158cd5a12f8e44655f82b68c33bf3b2John Hoford                outputSize, mParameters.getZoom() / 100f, mParameters.getAngle());
10644a499a64158cd5a12f8e44655f82b68c33bf3b2John Hoford
1072f879102d630815df64636b8d37e3a7eea8e8b65Sascha Haeberling        return mBitmapOut;
1082576a29cccf1d96edeef071914e8b775c230e8e8Sascha Haeberling    }
109c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling
110769d38db5736cd690ab7837c0824572739c39184Sascha Haeberling    private Bitmap applyXmp(Bitmap bitmapIn, XMPMeta xmp, int intermediateWidth) {
111d49d53206a197b08ec4754d8751b6860e33d9f48John Reck        try {
112d49d53206a197b08ec4754d8751b6860e33d9f48John Reck            int croppedAreaWidth =
113d49d53206a197b08ec4754d8751b6860e33d9f48John Reck                    getInt(xmp, CROPPED_AREA_IMAGE_WIDTH_PIXELS);
114d49d53206a197b08ec4754d8751b6860e33d9f48John Reck            int croppedAreaHeight =
115d49d53206a197b08ec4754d8751b6860e33d9f48John Reck                    getInt(xmp, CROPPED_AREA_IMAGE_HEIGHT_PIXELS);
116d49d53206a197b08ec4754d8751b6860e33d9f48John Reck            int fullPanoWidth =
117d49d53206a197b08ec4754d8751b6860e33d9f48John Reck                    getInt(xmp, CROPPED_AREA_FULL_PANO_WIDTH_PIXELS);
118d49d53206a197b08ec4754d8751b6860e33d9f48John Reck            int fullPanoHeight =
119d49d53206a197b08ec4754d8751b6860e33d9f48John Reck                    getInt(xmp, CROPPED_AREA_FULL_PANO_HEIGHT_PIXELS);
120d49d53206a197b08ec4754d8751b6860e33d9f48John Reck            int left = getInt(xmp, CROPPED_AREA_LEFT);
121d49d53206a197b08ec4754d8751b6860e33d9f48John Reck            int top = getInt(xmp, CROPPED_AREA_TOP);
122d49d53206a197b08ec4754d8751b6860e33d9f48John Reck
1234ea01ce27d0aecd1b63d7e99b747c9858fa93b02nicolasroard            if (fullPanoWidth == 0 || fullPanoHeight == 0) {
1244ea01ce27d0aecd1b63d7e99b747c9858fa93b02nicolasroard                return bitmapIn;
1254ea01ce27d0aecd1b63d7e99b747c9858fa93b02nicolasroard            }
126044513931d31a4b86f622b1af36e9ce947672be7Sascha Haeberling            // Make sure the intermediate image has the similar size to the
127044513931d31a4b86f622b1af36e9ce947672be7Sascha Haeberling            // input.
1284cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford            Bitmap paddedBitmap = null;
129044513931d31a4b86f622b1af36e9ce947672be7Sascha Haeberling            float scale = intermediateWidth / (float) fullPanoWidth;
1304cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford            while (paddedBitmap == null) {
1314cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford                try {
1324cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford                    paddedBitmap = Bitmap.createBitmap(
13391d26f6c3b183862eeffc1856e2d758e800d13f4John Hoford                            (int) (fullPanoWidth * scale), (int) (fullPanoHeight * scale),
13491d26f6c3b183862eeffc1856e2d758e800d13f4John Hoford                            Bitmap.Config.ARGB_8888);
1354cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford                } catch (java.lang.OutOfMemoryError e) {
1364cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford                    System.gc();
1374cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford                    scale /= 2;
1384cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford                }
1394cdb22512f322d2749aa57e07abeebf65b47c26fJohn Hoford            }
140d49d53206a197b08ec4754d8751b6860e33d9f48John Reck            Canvas paddedCanvas = new Canvas(paddedBitmap);
141d49d53206a197b08ec4754d8751b6860e33d9f48John Reck
142d49d53206a197b08ec4754d8751b6860e33d9f48John Reck            int right = left + croppedAreaWidth;
143d49d53206a197b08ec4754d8751b6860e33d9f48John Reck            int bottom = top + croppedAreaHeight;
144044513931d31a4b86f622b1af36e9ce947672be7Sascha Haeberling            RectF destRect = new RectF(left * scale, top * scale, right * scale, bottom * scale);
145d49d53206a197b08ec4754d8751b6860e33d9f48John Reck            paddedCanvas.drawBitmap(bitmapIn, null, destRect, null);
146d49d53206a197b08ec4754d8751b6860e33d9f48John Reck            bitmapIn = paddedBitmap;
147d49d53206a197b08ec4754d8751b6860e33d9f48John Reck        } catch (XMPException ex) {
148d49d53206a197b08ec4754d8751b6860e33d9f48John Reck            // Do nothing, just use bitmapIn as is.
149d49d53206a197b08ec4754d8751b6860e33d9f48John Reck        }
150d49d53206a197b08ec4754d8751b6860e33d9f48John Reck        return bitmapIn;
151d49d53206a197b08ec4754d8751b6860e33d9f48John Reck    }
152d49d53206a197b08ec4754d8751b6860e33d9f48John Reck
153c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling    private static int getInt(XMPMeta xmp, String key) throws XMPException {
154c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling        if (xmp.doesPropertyExist(GOOGLE_PANO_NAMESPACE, key)) {
155c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling            return xmp.getPropertyInteger(GOOGLE_PANO_NAMESPACE, key);
156c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling        } else {
157c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling            return 0;
158c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling        }
159c60752b28bcdc859b24040f77883568e0838ed74Sascha Haeberling    }
1602576a29cccf1d96edeef071914e8b775c230e8e8Sascha Haeberling}
161