1a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling/* 2a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * Copyright (C) 2012 The Android Open Source Project 3a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * 4a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * Licensed under the Apache License, Version 2.0 (the "License"); 5a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * you may not use this file except in compliance with the License. 6a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * You may obtain a copy of the License at 7a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * 8a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * http://www.apache.org/licenses/LICENSE-2.0 9a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * 10a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * Unless required by applicable law or agreed to in writing, software 11a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * distributed under the License is distributed on an "AS IS" BASIS, 12a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * See the License for the specific language governing permissions and 14a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * limitations under the License. 15a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling */ 16a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 17a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling#include <jni.h> 18a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling#include <math.h> 19a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling#include <android/bitmap.h> 20a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 21a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling#ifdef __cplusplus 22a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingextern "C" { 23a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling#endif 24a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 25a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 26a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling#define PI_F 3.141592653589f 27a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 28a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingclass ImageRGBA { 29a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling public: 30a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling ImageRGBA(unsigned char* image, int width, int height) 31a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling : image_(image), width_(width), height_(height) { 32a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling width_step_ = width * 4; 33a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling } 34a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 35a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling int Width() const { 36a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling return width_; 37a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling } 38a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 39a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling int Height() const { 40a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling return height_; 41a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling } 42a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 43a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling // Pixel accessor. 44a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling unsigned char* operator()(int x, int y) { 45a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling return image_ + y * width_step_ + x * 4; 46a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling } 47a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling const unsigned char* operator()(int x, int y) const { 48a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling return image_ + y * width_step_ + x * 4; 49a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling } 50a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 51a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling private: 52a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling unsigned char* image_; 53a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling int width_; 54a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling int height_; 55a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling int width_step_; 56a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling}; 57a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 58a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling// Interpolate a pixel in a 3 channel image. 59a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlinginline void InterpolatePixel(const ImageRGBA &image, float x, float y, 60a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling unsigned char* dest) { 61a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling // Get pointers and scale factors for the source pixels. 62a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling float ax = x - floor(x); 63a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling float ay = y - floor(y); 64a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling float axn = 1.0f - ax; 65a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling float ayn = 1.0f - ay; 66a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling const unsigned char *p = image(x, y); 67a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling const unsigned char *p2 = image(x, y + 1); 68a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 69a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling // Interpolate each image color plane. 70a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling dest[0] = static_cast<unsigned char>(axn * ayn * p[0] + ax * ayn * p[4] + 71a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling ax * ay * p2[4] + axn * ay * p2[0] + 0.5f); 72a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling p++; 73a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling p2++; 74a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 75a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling dest[1] = static_cast<unsigned char>(axn * ayn * p[0] + ax * ayn * p[4] + 76a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling ax * ay * p2[4] + axn * ay * p2[0] + 0.5f); 77a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling p++; 78a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling p2++; 79a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 80a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling dest[2] = static_cast<unsigned char>(axn * ayn * p[0] + ax * ayn * p[4] + 81a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling ax * ay * p2[4] + axn * ay * p2[0] + 0.5f); 82a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling p++; 83a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling p2++; 84a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling dest[3] = 0xFF; 85a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling} 86a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 87a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling// Wrap circular coordinates around the globe 88a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlinginline float wrap(float value, float dimension) { 89a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling return value - (dimension * floor(value/dimension)); 90a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling} 91a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 92a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingvoid StereographicProjection(float scale, float angle, unsigned char* input_image, 93a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling int input_width, int input_height, 94a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling unsigned char* output_image, int output_width, 95a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling int output_height) { 96a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling ImageRGBA input(input_image, input_width, input_height); 97a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling ImageRGBA output(output_image, output_width, output_height); 98a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 99a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling const float image_scale = output_width * scale; 100a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 101a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling for (int x = 0; x < output_width; x++) { 102a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling // Center and scale x 103a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling float xf = (x - output_width / 2.0f) / image_scale; 104a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 105a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling for (int y = 0; y < output_height; y++) { 106a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling // Center and scale y 107a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling float yf = (y - output_height / 2.0f) / image_scale; 108a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 109a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling // Convert to polar 110a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling float r = hypotf(xf, yf); 111a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling float theta = angle+atan2(yf, xf); 112a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling if (theta>PI_F) theta-=2*PI_F; 113a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 114a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling // Project onto plane 115a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling float phi = 2 * atan(1 / r); 116a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling // (theta stays the same) 117a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 118a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling // Map to panorama image 119a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling float px = (theta / (2 * PI_F)) * input_width; 120a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling float py = (phi / PI_F) * input_height; 121a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 122a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling // Wrap around the globe 123a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling px = wrap(px, input_width); 124a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling py = wrap(py, input_height); 125a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 126a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling // Write the interpolated pixel 127a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling InterpolatePixel(input, px, py, output(x, y)); 128a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling } 129a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling } 130a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling} 131a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 132a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 133a5aa01058465e55324dacaa3915b5d8f5cd66e78Tomasz WasilczykJNIEXPORT void JNICALL Java_com_android_camera_tinyplanet_TinyPlanetNative_process(JNIEnv* env, 134a5aa01058465e55324dacaa3915b5d8f5cd66e78Tomasz Wasilczyk jobject obj __unused, jobject bitmap_in, jint width, jint height, jobject bitmap_out, 135a5aa01058465e55324dacaa3915b5d8f5cd66e78Tomasz Wasilczyk jint output_size, jfloat scale, jfloat angle) 136a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling{ 137a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling char* source = 0; 138a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling char* destination = 0; 139a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling AndroidBitmap_lockPixels(env, bitmap_in, (void**) &source); 140a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling AndroidBitmap_lockPixels(env, bitmap_out, (void**) &destination); 141a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling unsigned char * rgb_in = (unsigned char * )source; 142a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling unsigned char * rgb_out = (unsigned char * )destination; 143a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 144a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling StereographicProjection(scale, angle, rgb_in, width, height, rgb_out, output_size, output_size); 145a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling AndroidBitmap_unlockPixels(env, bitmap_in); 146a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling AndroidBitmap_unlockPixels(env, bitmap_out); 147a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling} 148a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 149a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling#ifdef __cplusplus 150a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling} 151a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling#endif 152a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 153a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling 154