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 17 package com.android.gallery3d.filtershow.filters; 18 19 import android.graphics.Bitmap; 20 import android.graphics.Canvas; 21 import android.graphics.RectF; 22 23 import com.adobe.xmp.XMPException; 24 import com.adobe.xmp.XMPMeta; 25 import com.android.gallery3d.app.Log; 26 import com.android.gallery3d.filtershow.cache.BitmapCache; 27 import com.android.gallery3d.filtershow.cache.ImageLoader; 28 import com.android.gallery3d.filtershow.imageshow.MasterImage; 29 import com.android.gallery3d.filtershow.pipeline.ImagePreset; 30 31 /** 32 * An image filter which creates a tiny planet projection. 33 */ 34 public 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