OSDN Git Service

Adds a tiny planet (stereographic projection) image filter.
authorSascha Haeberling <haeberling@google.com>
Fri, 19 Oct 2012 22:43:03 +0000 (15:43 -0700)
committerSascha Haeberling <haeberling@google.com>
Sat, 20 Oct 2012 00:04:37 +0000 (17:04 -0700)
 Bug: 7293391

Also fixes an issue where the preview value was statically set to 100.
This adds a preview parameter value that can be set for this purpose.

Change-Id: I68c4ec04e86ab8a29ef7f561e5f21a298003677e

jni/Android.mk
jni/filters/filters.h
jni/filters/tinyplanet.cc [new file with mode: 0644]
src/com/android/gallery3d/filtershow/FilterShowActivity.java
src/com/android/gallery3d/filtershow/filters/ImageFilter.java
src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java

index 85e867a..fb8ba86 100644 (file)
@@ -21,6 +21,7 @@ include $(BUILD_SHARED_LIBRARY)
 
 include $(CLEAR_VARS)
 
+LOCAL_CPP_EXTENSION := .cc
 LOCAL_LDFLAGS  := -llog -ljnigraphics
 LOCAL_SDK_VERSION := 9
 LOCAL_MODULE    := libjni_filtershow_filters
@@ -38,7 +39,8 @@ LOCAL_SRC_FILES := filters/bw.c \
                    filters/redEyeMath.c \
                    filters/fx.c \
                    filters/wbalance.c \
-                   filters/redeye.c
+                   filters/redeye.c \
+                   filters/tinyplanet.cc
 
 LOCAL_CFLAGS    += -ffast-math -O3 -funroll-loops
 LOCAL_ARM_MODE := arm
index d8728f0..d518b63 100644 (file)
@@ -35,7 +35,7 @@ typedef unsigned int Color;
 
 #define LOG(msg...) __android_log_print(ANDROID_LOG_VERBOSE, "NativeFilters", msg)
 
-#define JNIFUNCF(cls, name, vars...) Java_com_android_gallery3d_filtershow_filters_ ## cls ## _ ## name(JNIEnv* env, jobject this, vars)
+#define JNIFUNCF(cls, name, vars...) Java_com_android_gallery3d_filtershow_filters_ ## cls ## _ ## name(JNIEnv* env, jobject obj, vars)
 
 #define RED i
 #define GREEN i+1
diff --git a/jni/filters/tinyplanet.cc b/jni/filters/tinyplanet.cc
new file mode 100644 (file)
index 0000000..bc12c32
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "filters.h"
+#include <math.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define PI_F 3.141592653589f
+
+class ImageRGBA {
+ public:
+  ImageRGBA(unsigned char* image, int width, int height)
+   : image_(image), width_(width), height_(height) {
+    width_step_ = width * 4;
+  }
+
+  int Width() const {
+    return width_;
+  }
+
+  int Height() const {
+    return height_;
+  }
+
+  // Pixel accessor.
+  unsigned char* operator()(int x, int y) {
+    return image_ + y * width_step_ + x * 4;
+  }
+  const unsigned char* operator()(int x, int y) const {
+    return image_ + y * width_step_ + x * 4;
+  }
+
+ private:
+  unsigned char* image_;
+  int width_;
+  int height_;
+  int width_step_;
+};
+
+// Interpolate a pixel in a 3 channel image.
+inline void InterpolatePixel(const ImageRGBA &image, float x, float y,
+                             unsigned char* dest) {
+  // Get pointers and scale factors for the source pixels.
+  float ax = x - floor(x);
+  float ay = y - floor(y);
+  float axn = 1.0f - ax;
+  float ayn = 1.0f - ay;
+  const unsigned char *p = image(x, y);
+  const unsigned char *p2 = image(x, y + 1);
+
+  // Interpolate each image color plane.
+  dest[0] = static_cast<unsigned char>(axn * ayn * p[0] + ax * ayn * p[4] +
+             ax * ay * p2[4] + axn * ay * p2[0] + 0.5f);
+  p++;
+  p2++;
+
+  dest[1] = static_cast<unsigned char>(axn * ayn * p[0] + ax * ayn * p[4] +
+             ax * ay * p2[4] + axn * ay * p2[0] + 0.5f);
+  p++;
+  p2++;
+
+  dest[2] = static_cast<unsigned char>(axn * ayn * p[0] + ax * ayn * p[4] +
+             ax * ay * p2[4] + axn * ay * p2[0] + 0.5f);
+  p++;
+  p2++;
+}
+
+// Wrap circular coordinates around the globe
+inline float wrap(float value, float dimension) {
+  return value - (dimension * floor(value/dimension));
+}
+
+void StereographicProjection(float scale, unsigned char* input_image,
+                             int input_width, int input_height,
+                             unsigned char* output_image, int output_width,
+                             int output_height) {
+  ImageRGBA input(input_image, input_width, input_height);
+  ImageRGBA output(output_image, output_width, output_height);
+
+  const float image_scale = output_width * scale;
+
+  for (int x = 0; x < output_width; x++) {
+    // Center and scale x
+    float xf = (x - output_width / 2.0f) / image_scale;
+
+    for (int y = 0; y < output_height; y++) {
+      // Center and scale y
+      float yf = (y - output_height / 2.0f) / image_scale;
+
+      // Convert to polar
+      float r = hypotf(xf, yf);
+      float theta = atan2(yf, xf);
+
+      // Project onto plane
+      float phi = 2 * atan(1 / r);
+      // (theta stays the same)
+
+      // Map to panorama image
+      float px = (theta / (2 * PI_F)) * input_width;
+      float py = (phi / PI_F) * input_height;
+
+      // Wrap around the globe
+      px = wrap(px, input_width);
+      py = wrap(py, input_height);
+
+      // Write the interpolated pixel
+      InterpolatePixel(input, px, py, output(x, y));
+    }
+  }
+}
+
+
+void JNIFUNCF(ImageFilterTinyPlanet, nativeApplyFilter, jobject bitmap_in, jint width, jint height, jobject bitmap_out, jint output_size, jfloat scale)
+{
+    char* source = 0;
+    char* destination = 0;
+    AndroidBitmap_lockPixels(env, bitmap_in, (void**) &source);
+    AndroidBitmap_lockPixels(env, bitmap_out, (void**) &destination);
+    unsigned char * rgb_in = (unsigned char * )source;
+    unsigned char * rgb_out = (unsigned char * )destination;
+
+    StereographicProjection(scale, rgb_in, width, height, rgb_out, output_size, output_size);
+    AndroidBitmap_unlockPixels(env, bitmap_in);
+    AndroidBitmap_unlockPixels(env, bitmap_out);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+
index 760532a..2cdc22e 100644 (file)
@@ -238,7 +238,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
                 R.id.vibranceButton,
                 R.id.contrastButton,
                 R.id.saturationButton,
-                R.id.shadowRecoveryButton,
                 R.id.wbalanceButton,
                 R.id.hueButton,
                 R.id.exposureButton,
@@ -257,13 +256,12 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
         };
 
         for (int i = 0; i < filters.length; i++) {
-
             ImageSmallFilter fView = new ImageSmallFilter(this);
             View v = listColors.findViewById(recastIDs[i]);
             int pos = listColors.indexOfChild(v);
             listColors.removeView(v);
 
-            filters[i].setParameter(filters[i].getMaxParameter());
+            filters[i].setParameter(filters[i].getPreviewParameter());
             if(v instanceof ImageButtonTitle)
                 filters[i].setName(((ImageButtonTitle) v).getText());
             fView.setImageFilter(filters[i]);
@@ -353,7 +351,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
         } else if (action.equalsIgnoreCase(TINY_PLANET_ACTION)) {
             mPanelController.showComponent(findViewById(R.id.tinyplanetButton));
         }
-
     }
 
     private int getScreenImageSize() {
index f63286c..21a1dff 100644 (file)
@@ -9,6 +9,7 @@ public class ImageFilter implements Cloneable {
 
     protected int mMaxParameter = 100;
     protected int mMinParameter = -100;
+    protected int mPreviewParameter = mMaxParameter;
     protected int mDefaultParameter = 0;
     protected int mParameter = 0;
     private ImagePreset mImagePreset;
@@ -41,6 +42,7 @@ public class ImageFilter implements Cloneable {
         filter.mMinParameter = mMinParameter;
         filter.mImagePreset = mImagePreset;
         filter.mDefaultParameter = mDefaultParameter;
+        filter.mPreviewParameter = mPreviewParameter;
         return filter;
     }
 
@@ -81,6 +83,14 @@ public class ImageFilter implements Cloneable {
     }
 
     /**
+     * The parameter value to be used in previews.
+     * @return parameter value to be used to preview the filter
+     */
+    public int getPreviewParameter(){
+        return mPreviewParameter;
+    }
+
+    /**
      * The minimum allowed value (inclusive)
      * @return minimum value allowed as input to this filter
      */
index e742bb2..cba1cd2 100644 (file)
@@ -2,47 +2,51 @@
 package com.android.gallery3d.filtershow.filters;
 
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.util.Log;
 
 import com.android.gallery3d.filtershow.presets.ImagePreset;
 
+/**
+ * An image filter which creates a tiny planet projection.
+ */
 public class ImageFilterTinyPlanet extends ImageFilter {
     private static final String TAG = ImageFilterTinyPlanet.class.getSimpleName();
 
     public ImageFilterTinyPlanet() {
         setFilterType(TYPE_TINYPLANET);
         mName = "TinyPlanet";
+
+        mMinParameter = 10;
+        mMaxParameter = 60;
+        mDefaultParameter = 20;
+        mPreviewParameter = 20;
+        mParameter = 20;
     }
 
+    native protected void nativeApplyFilter(
+            Bitmap bitmapIn, int width, int height, Bitmap bitmapOut, int outSize, float scale);
+
     @Override
-    public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) {
-        Log.d(TAG, "Applying tiny planet.");
-        String str = "TinyPlanet";
+    public Bitmap apply(Bitmap bitmapIn, float scaleFactor, boolean highQuality) {
         ImagePreset preset = getImagePreset();
         if (preset != null) {
-            if (!preset.isPanoramaSafe()) {
-                str = "NO TP";
-
-            } else {
+            if (preset.isPanoramaSafe()) {
+                // TODO(haeberling): Get XMPMeta object.
                 Object xmp = preset.getImageLoader().getXmpObject();
-                str = "TP got Xmp";
+            } else {
+                // TODO(haeberling): What should we do for:
+                // !preset.isPanoramaSafe()?
             }
         }
-        int w = bitmap.getWidth();
-        int h = bitmap.getHeight();
-
-        // Print TinyPlanet as text on the image as a placeholder
-        // TODO(haeberling): Implement the real deal.
-        Canvas canvas = new Canvas(bitmap);
-        Paint paint = new Paint();
-        paint.setColor(Color.RED);
-        paint.setTextSize((int) (((mParameter + 100) / 200f) * 100));
-        paint.setTextAlign(Align.CENTER);
-        canvas.drawText(str, w / 2, h / 2, paint);
-        return super.apply(bitmap, scaleFactor, highQuality);
+
+        int w = bitmapIn.getWidth();
+        int h = bitmapIn.getHeight();
+        int outputSize = Math.min(w, h);
+
+        Bitmap mBitmapOut = Bitmap.createBitmap(
+                outputSize, outputSize, Bitmap.Config.ARGB_8888);
+
+        // TODO(haeberling): Add the padding back in based on the meta-data.
+        nativeApplyFilter(bitmapIn, w, h, mBitmapOut, outputSize, mParameter / 100f);
+        return mBitmapOut;
     }
 }