1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.gallery3d.filtershow.filters;
18
19import android.graphics.Bitmap;
20import android.graphics.Canvas;
21import android.graphics.RectF;
22
23import com.adobe.xmp.XMPException;
24import com.adobe.xmp.XMPMeta;
25import com.android.gallery3d.app.Log;
26import com.android.gallery3d.filtershow.cache.BitmapCache;
27import com.android.gallery3d.filtershow.cache.ImageLoader;
28import com.android.gallery3d.filtershow.imageshow.MasterImage;
29import com.android.gallery3d.filtershow.pipeline.ImagePreset;
30
31/**
32 * An image filter which creates a tiny planet projection.
33 */
34public class ImageFilterTinyPlanet extends SimpleImageFilter {
35
36
37    private static final String LOGTAG = ImageFilterTinyPlanet.class.getSimpleName();
38    public static final String GOOGLE_PANO_NAMESPACE = "http://ns.google.com/photos/1.0/panorama/";
39    FilterTinyPlanetRepresentation mParameters = new FilterTinyPlanetRepresentation();
40
41    public static final String CROPPED_AREA_IMAGE_WIDTH_PIXELS =
42            "CroppedAreaImageWidthPixels";
43    public static final String CROPPED_AREA_IMAGE_HEIGHT_PIXELS =
44            "CroppedAreaImageHeightPixels";
45    public static final String CROPPED_AREA_FULL_PANO_WIDTH_PIXELS =
46            "FullPanoWidthPixels";
47    public static final String CROPPED_AREA_FULL_PANO_HEIGHT_PIXELS =
48            "FullPanoHeightPixels";
49    public static final String CROPPED_AREA_LEFT =
50            "CroppedAreaLeftPixels";
51    public static final String CROPPED_AREA_TOP =
52            "CroppedAreaTopPixels";
53
54    public ImageFilterTinyPlanet() {
55        mName = "TinyPlanet";
56    }
57
58    @Override
59    public void useRepresentation(FilterRepresentation representation) {
60        FilterTinyPlanetRepresentation parameters = (FilterTinyPlanetRepresentation) representation;
61        mParameters = parameters;
62    }
63
64    @Override
65    public FilterRepresentation getDefaultRepresentation() {
66        return new FilterTinyPlanetRepresentation();
67    }
68
69
70    native protected void nativeApplyFilter(
71            Bitmap bitmapIn, int width, int height, Bitmap bitmapOut, int outSize, float scale,
72            float angle);
73
74
75    @Override
76    public Bitmap apply(Bitmap bitmapIn, float scaleFactor, int quality) {
77        int w = bitmapIn.getWidth();
78        int h = bitmapIn.getHeight();
79        int outputSize = (int) (w / 2f);
80        ImagePreset preset = getEnvironment().getImagePreset();
81        Bitmap mBitmapOut = null;
82        if (preset != null) {
83            XMPMeta xmp = ImageLoader.getXmpObject(MasterImage.getImage().getActivity());
84            // Do nothing, just use bitmapIn as is if we don't have XMP.
85            if(xmp != null) {
86                bitmapIn = applyXmp(bitmapIn, xmp, w);
87            }
88        }
89        if (mBitmapOut != null) {
90            if (outputSize != mBitmapOut.getHeight()) {
91                mBitmapOut = null;
92            }
93        }
94        while (mBitmapOut == null) {
95            try {
96                mBitmapOut = getEnvironment().getBitmap(outputSize,
97                        outputSize, BitmapCache.TINY_PLANET);
98            } catch (java.lang.OutOfMemoryError e) {
99                System.gc();
100                outputSize /= 2;
101                Log.v(LOGTAG, "No memory to create Full Tiny Planet create half");
102            }
103        }
104        nativeApplyFilter(bitmapIn, bitmapIn.getWidth(), bitmapIn.getHeight(), mBitmapOut,
105                outputSize, mParameters.getZoom() / 100f, mParameters.getAngle());
106
107        return mBitmapOut;
108    }
109
110    private Bitmap applyXmp(Bitmap bitmapIn, XMPMeta xmp, int intermediateWidth) {
111        try {
112            int croppedAreaWidth =
113                    getInt(xmp, CROPPED_AREA_IMAGE_WIDTH_PIXELS);
114            int croppedAreaHeight =
115                    getInt(xmp, CROPPED_AREA_IMAGE_HEIGHT_PIXELS);
116            int fullPanoWidth =
117                    getInt(xmp, CROPPED_AREA_FULL_PANO_WIDTH_PIXELS);
118            int fullPanoHeight =
119                    getInt(xmp, CROPPED_AREA_FULL_PANO_HEIGHT_PIXELS);
120            int left = getInt(xmp, CROPPED_AREA_LEFT);
121            int top = getInt(xmp, CROPPED_AREA_TOP);
122
123            if (fullPanoWidth == 0 || fullPanoHeight == 0) {
124                return bitmapIn;
125            }
126            // Make sure the intermediate image has the similar size to the
127            // input.
128            Bitmap paddedBitmap = null;
129            float scale = intermediateWidth / (float) fullPanoWidth;
130            while (paddedBitmap == null) {
131                try {
132                    paddedBitmap = Bitmap.createBitmap(
133                            (int) (fullPanoWidth * scale), (int) (fullPanoHeight * scale),
134                            Bitmap.Config.ARGB_8888);
135                } catch (java.lang.OutOfMemoryError e) {
136                    System.gc();
137                    scale /= 2;
138                }
139            }
140            Canvas paddedCanvas = new Canvas(paddedBitmap);
141
142            int right = left + croppedAreaWidth;
143            int bottom = top + croppedAreaHeight;
144            RectF destRect = new RectF(left * scale, top * scale, right * scale, bottom * scale);
145            paddedCanvas.drawBitmap(bitmapIn, null, destRect, null);
146            bitmapIn = paddedBitmap;
147        } catch (XMPException ex) {
148            // Do nothing, just use bitmapIn as is.
149        }
150        return bitmapIn;
151    }
152
153    private static int getInt(XMPMeta xmp, String key) throws XMPException {
154        if (xmp.doesPropertyExist(GOOGLE_PANO_NAMESPACE, key)) {
155            return xmp.getPropertyInteger(GOOGLE_PANO_NAMESPACE, key);
156        } else {
157            return 0;
158        }
159    }
160}
161