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.Paint; 22 import android.graphics.Rect; 23 import android.graphics.RectF; 24 25 import com.adobe.xmp.XMPException; 26 import com.adobe.xmp.XMPMeta; 27 import com.android.gallery3d.app.Log; 28 import com.android.gallery3d.filtershow.presets.ImagePreset; 29 30 /** 31 * An image filter which creates a tiny planet projection. 32 */ 33 public class ImageFilterTinyPlanet extends SimpleImageFilter { 34 35 36 private static final String LOGTAG = ImageFilterTinyPlanet.class.getSimpleName(); 37 public static final String GOOGLE_PANO_NAMESPACE = "http://ns.google.com/photos/1.0/panorama/"; 38 FilterTinyPlanetRepresentation mParameters = new FilterTinyPlanetRepresentation(); 39 40 public static final String CROPPED_AREA_IMAGE_WIDTH_PIXELS = 41 "CroppedAreaImageWidthPixels"; 42 public static final String CROPPED_AREA_IMAGE_HEIGHT_PIXELS = 43 "CroppedAreaImageHeightPixels"; 44 public static final String CROPPED_AREA_FULL_PANO_WIDTH_PIXELS = 45 "FullPanoWidthPixels"; 46 public static final String CROPPED_AREA_FULL_PANO_HEIGHT_PIXELS = 47 "FullPanoHeightPixels"; 48 public static final String CROPPED_AREA_LEFT = 49 "CroppedAreaLeftPixels"; 50 public static final String CROPPED_AREA_TOP = 51 "CroppedAreaTopPixels"; 52 53 public ImageFilterTinyPlanet() { 54 mName = "TinyPlanet"; 55 } 56 57 @Override 58 public void useRepresentation(FilterRepresentation representation) { 59 FilterTinyPlanetRepresentation parameters = (FilterTinyPlanetRepresentation) representation; 60 mParameters = parameters; 61 } 62 63 @Override 64 public FilterRepresentation getDefaultRepresentation() { 65 return new FilterTinyPlanetRepresentation(); 66 } 67 68 69 native protected void nativeApplyFilter( 70 Bitmap bitmapIn, int width, int height, Bitmap bitmapOut, int outSize, float scale, 71 float angle); 72 73 74 @Override 75 public Bitmap apply(Bitmap bitmapIn, float scaleFactor, int quality) { 76 int w = bitmapIn.getWidth(); 77 int h = bitmapIn.getHeight(); 78 int outputSize = (int) (w / 2f); 79 ImagePreset preset = getImagePreset(); 80 Bitmap mBitmapOut = null; 81 if (preset != null) { 82 XMPMeta xmp = preset.getImageLoader().getXmpObject(); 83 // Do nothing, just use bitmapIn as is if we don't have XMP. 84 if(xmp != null) { 85 bitmapIn = applyXmp(bitmapIn, xmp, w); 86 } 87 } 88 if (mBitmapOut != null) { 89 if (outputSize != mBitmapOut.getHeight()) { 90 mBitmapOut = null; 91 } 92 } 93 while (mBitmapOut == null) { 94 try { 95 mBitmapOut = getEnvironment().getBitmap(outputSize, outputSize); 96 } catch (java.lang.OutOfMemoryError e) { 97 System.gc(); 98 outputSize /= 2; 99 Log.v(LOGTAG, "No memory to create Full Tiny Planet create half"); 100 } 101 } 102 nativeApplyFilter(bitmapIn, bitmapIn.getWidth(), bitmapIn.getHeight(), mBitmapOut, 103 outputSize, mParameters.getZoom() / 100f, mParameters.getAngle()); 104 105 return mBitmapOut; 106 } 107 108 private Bitmap applyXmp(Bitmap bitmapIn, XMPMeta xmp, int intermediateWidth) { 109 try { 110 int croppedAreaWidth = 111 getInt(xmp, CROPPED_AREA_IMAGE_WIDTH_PIXELS); 112 int croppedAreaHeight = 113 getInt(xmp, CROPPED_AREA_IMAGE_HEIGHT_PIXELS); 114 int fullPanoWidth = 115 getInt(xmp, CROPPED_AREA_FULL_PANO_WIDTH_PIXELS); 116 int fullPanoHeight = 117 getInt(xmp, CROPPED_AREA_FULL_PANO_HEIGHT_PIXELS); 118 int left = getInt(xmp, CROPPED_AREA_LEFT); 119 int top = getInt(xmp, CROPPED_AREA_TOP); 120 121 if (fullPanoWidth == 0 || fullPanoHeight == 0) { 122 return bitmapIn; 123 } 124 // Make sure the intermediate image has the similar size to the 125 // input. 126 Bitmap paddedBitmap = null; 127 float scale = intermediateWidth / (float) fullPanoWidth; 128 while (paddedBitmap == null) { 129 try { 130 paddedBitmap = Bitmap.createBitmap( 131 (int) (fullPanoWidth * scale), (int) (fullPanoHeight * scale), 132 Bitmap.Config.ARGB_8888); 133 } catch (java.lang.OutOfMemoryError e) { 134 System.gc(); 135 scale /= 2; 136 } 137 } 138 Canvas paddedCanvas = new Canvas(paddedBitmap); 139 140 int right = left + croppedAreaWidth; 141 int bottom = top + croppedAreaHeight; 142 RectF destRect = new RectF(left * scale, top * scale, right * scale, bottom * scale); 143 paddedCanvas.drawBitmap(bitmapIn, null, destRect, null); 144 bitmapIn = paddedBitmap; 145 } catch (XMPException ex) { 146 // Do nothing, just use bitmapIn as is. 147 } 148 return bitmapIn; 149 } 150 151 private static int getInt(XMPMeta xmp, String key) throws XMPException { 152 if (xmp.doesPropertyExist(GOOGLE_PANO_NAMESPACE, key)) { 153 return xmp.getPropertyInteger(GOOGLE_PANO_NAMESPACE, key); 154 } else { 155 return 0; 156 } 157 } 158 } 159