Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2010 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 #include <android/bitmap.h>
     18 #include <jni.h>
     19 
     20 #include <cmath>
     21 #include <cstdlib>
     22 
     23 #include "utils.h"
     24 #include "_jni.h"
     25 
     26 using android::apps::photoeditor::utils::LockBitmaps;
     27 using android::apps::photoeditor::utils::pixel32_t;
     28 using android::apps::photoeditor::utils::UnlockBitmaps;
     29 
     30 namespace {
     31 
     32 const uint32_t kShiftBits = 10;
     33 const uint32_t kShiftValue = (1 << kShiftBits);
     34 
     35 /*
     36  * Convolution matrix of distance 2 with fixed point of 'kShiftBits' bits
     37  * shifted. Thus the sum of this matrix should be 'kShiftValue'. Entries of
     38  * small values are not calculated to gain efficiency.
     39  * The order ot pixels represented in this matrix is:
     40  *  1  2  3
     41  *  4  0  5
     42  *  6  7  8
     43  *  and the matrix should be: {230, 56, 114, 56, 114, 114, 56, 114, 56}.
     44  *  However, since most of the valus are identical, we only use the first three
     45  *  entries and the entries corresponding to the pixels is:
     46  *  1  2  1
     47  *  2  0  2
     48  *  1  2  1
     49  */
     50 const uint32_t convolution_matrix[3] = {230, 56, 114};
     51 
     52 /*
     53  * Generate a blurred random noise bitmap.
     54  */
     55 void GenerateBlurredNoise(void* dst_pixels, AndroidBitmapInfo* dst_info,
     56       float noise_scale) {
     57   uint32_t fixed_noise_scale = noise_scale * kShiftValue;
     58 
     59   // Clear dst bitmap to 0 for storing generated random  noise.
     60   memset(dst_pixels, 0, dst_info->stride * dst_info->height);
     61 
     62   // 0.5 is a empirical value and could be tuned.
     63   int random_threshold = RAND_MAX * 0.5;
     64   for (uint32_t y = 0; y < dst_info->height; y++) {
     65     uint32_t* dp_line = reinterpret_cast<uint32_t*>(
     66         reinterpret_cast<char*>(dst_pixels) + y * dst_info->stride);
     67 
     68     for (uint32_t x = 0; x < dst_info->width; x++) {
     69       if (rand() < random_threshold) {
     70         uint32_t* dp = dp_line + x;
     71         uint32_t* dp_prev = (y == 0) ? dp : (dp - dst_info->width);
     72         uint32_t* dp_next = (y == dst_info->height - 1) ? dp : (dp + dst_info->width);
     73 
     74         /*
     75          * 1  2  3
     76          * 4  0  5
     77          * 6  7  8
     78          */
     79         uint32_t* n[9];
     80         n[0] = dp;
     81         n[2] = dp_prev;
     82         n[7] = dp_next;
     83         if (x == 0) {
     84           n[1] = n[2];
     85           n[4] = n[0];
     86           n[6] = n[7];
     87         } else {
     88           n[1] = n[2] - 1;
     89           n[4] = n[0] - 1;
     90           n[6] = n[7] - 1;
     91         }
     92         if (x == dst_info->width - 1) {
     93           n[3] = n[2];
     94           n[5] = n[0];
     95           n[8] = n[7];
     96         } else {
     97           n[3] = n[2] + 1;
     98           n[5] = n[0] + 1;
     99           n[8] = n[7] + 1;
    100         }
    101 
    102         // noise randomness uniformly distributed between 0.5 to 1.5,
    103         // 0.5 is an empirical value.
    104         uint32_t random_noise_scale = fixed_noise_scale
    105             * (static_cast<double>(rand()) / RAND_MAX + 0.5);
    106 
    107         *n[0] = *n[0] + ((convolution_matrix[0] * random_noise_scale) >> kShiftBits);
    108         // The value in convolution_matrix is identical (56) for indexes 1, 3, 6, 8.
    109         uint32_t normal_scaled_noise = (convolution_matrix[1] * random_noise_scale) >> kShiftBits;
    110         *n[1] += normal_scaled_noise;
    111         *n[3] += normal_scaled_noise;
    112         *n[6] += normal_scaled_noise;
    113         *n[8] += normal_scaled_noise;
    114         // Likewise, the computation could be saved for indexes 2, 4, 5, 7;
    115         normal_scaled_noise = (convolution_matrix[2] * random_noise_scale) >> kShiftBits;
    116         *n[2] += normal_scaled_noise;
    117         *n[4] += normal_scaled_noise;
    118         *n[5] += normal_scaled_noise;
    119         *n[7] += normal_scaled_noise;
    120       }
    121     }
    122   }
    123 }
    124 
    125 extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeGrain(
    126     JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat noise_scale) {
    127    pGrainType f = (pGrainType)JNIFunc[JNI_Grain].func_ptr;
    128    return f(env, obj, src_bitmap, dst_bitmap, noise_scale);
    129 }
    130 
    131 extern "C" void Grain(
    132     JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat noise_scale) {
    133   AndroidBitmapInfo src_info;
    134   AndroidBitmapInfo dst_info;
    135   void* src_pixels;
    136   void* dst_pixels;
    137 
    138   int ret = LockBitmaps(
    139       env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
    140   if (ret < 0) {
    141     LOGE("LockBitmaps in grain failed, error=%d", ret);
    142     return;
    143   }
    144 
    145   GenerateBlurredNoise(dst_pixels, &dst_info, noise_scale);
    146 
    147   for (uint32_t scan_line = 0; scan_line < src_info.height; scan_line++) {
    148     uint32_t* src = reinterpret_cast<uint32_t*>(
    149         reinterpret_cast<char*>(src_pixels) + src_info.stride * scan_line);
    150     uint32_t* dst = reinterpret_cast<uint32_t*>(
    151         reinterpret_cast<char*>(dst_pixels) + dst_info.stride * scan_line);
    152 
    153     uint32_t* src_line_end = src + src_info.width;
    154     while (src < src_line_end) {
    155       pixel32_t* sp = reinterpret_cast<pixel32_t*>(src);
    156       pixel32_t* dp = reinterpret_cast<pixel32_t*>(dst);
    157 
    158       // energy_mask is used to constrain the noise according to the energy
    159       // level. Film grain appear more in dark part.
    160       // The energy level (from 0 to 765) is square-rooted and should in the
    161       // range from 0 to 27.659 (sqrt(765)), so 28 is used for normalization.
    162       uint32_t energy_level = sp->rgba8[0] + sp->rgba8[1] + sp->rgba8[2];
    163       uint32_t energy_mask = 28 - static_cast<uint32_t>(sqrtf(energy_level));
    164 
    165       // The intensity of each channel of RGB is affected by the random
    166       // noise previously produced and stored in dp->pixel.
    167       // dp->pixel should be in the range of [1.3 * noise_scale * kShiftValue,
    168       // 0]. Therefore 'scale' should be in the range of
    169       // [kShiftValue, kShiftValue - 1.3 * noise_scale * kShiftValue]
    170       uint32_t scale = (kShiftValue - dp->rgba32 * energy_mask / 28);
    171       uint32_t red = (sp->rgba8[0] * scale) >> kShiftBits;
    172       uint32_t green = (sp->rgba8[1] * scale) >> kShiftBits;
    173       uint32_t blue = (sp->rgba8[2] * scale) >> kShiftBits;
    174       dp->rgba32 = (sp->rgba8[3] << 24) | (blue << 16) | (green << 8) | red;
    175 
    176       dst++;
    177       src++;
    178     }
    179   }
    180 
    181   UnlockBitmaps(env, src_bitmap, dst_bitmap);
    182 }
    183 
    184 }  // namespace
    185