| /* |
| * 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 <jni.h> |
| #include <math.h> |
| #include <android/bitmap.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++; |
| dest[3] = 0xFF; |
| } |
| |
| // Wrap circular coordinates around the globe |
| inline float wrap(float value, float dimension) { |
| return value - (dimension * floor(value/dimension)); |
| } |
| |
| void StereographicProjection(float scale, float angle, 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 = angle+atan2(yf, xf); |
| if (theta>PI_F) theta-=2*PI_F; |
| |
| // 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)); |
| } |
| } |
| } |
| |
| |
| JNIEXPORT void JNICALL Java_com_android_camera_tinyplanet_TinyPlanetNative_process(JNIEnv* env, jobject obj, jobject bitmap_in, jint width, jint height, jobject bitmap_out, jint output_size, jfloat scale, jfloat angle) |
| { |
| (void)obj; |
| 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, angle, rgb_in, width, height, rgb_out, output_size, output_size); |
| AndroidBitmap_unlockPixels(env, bitmap_in); |
| AndroidBitmap_unlockPixels(env, bitmap_out); |
| } |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| |