| /* |
| * Copyright (C) 2010 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 <android/bitmap.h> |
| #include <jni.h> |
| |
| #include <cmath> |
| #include <cstdlib> |
| #include <cstring> |
| |
| #include "utils.h" |
| #include "_jni.h" |
| |
| using android::apps::photoeditor::utils::clamp; |
| using android::apps::photoeditor::utils::LockBitmaps; |
| using android::apps::photoeditor::utils::pixel32_t; |
| using android::apps::photoeditor::utils::UnlockBitmaps; |
| |
| namespace { |
| |
| // Array of approximated CDF of normal distribution. |
| // 1024 Entries in total. The array is approximated by sigmoid function and |
| // the exact command in "octave" is: |
| // x = [2/1029:1/1029:1025/1029]; |
| // y = (-1/11.5*log(1./x-0.9999)+0.5)*766*0.9+766*0.05; |
| const int kCDFEntries = 1024; |
| const uint32_t normal_cdf[] = { |
| 9, 33, 50, 64, 75, 84, 92, 99, 106, 112, 117, 122, 126, 130, 134, 138, 142, |
| 145, 148, 150, 154, 157, 159, 162, 164, 166, 169, 170, 173, 175, 177, 179, |
| 180, 182, 184, 186, 188, 189, 190, 192, 194, 195, 197, 198, 199, 200, 202, |
| 203, 205, 206, 207, 208, 209, 210, 212, 213, 214, 215, 216, 217, 218, 219, |
| 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 229, 230, 231, 232, 233, |
| 234, 235, 236, 236, 237, 238, 239, 239, 240, 240, 242, 242, 243, 244, 245, |
| 245, 246, 247, 247, 248, 249, 249, 250, 250, 251, 252, 253, 253, 254, 255, |
| 255, 256, 256, 257, 258, 258, 259, 259, 259, 260, 261, 262, 262, 263, 263, |
| 264, 264, 265, 265, 266, 267, 267, 268, 268, 269, 269, 269, 270, 270, 271, |
| 272, 272, 273, 273, 274, 274, 275, 275, 276, 276, 277, 277, 277, 278, 278, |
| 279, 279, 279, 280, 280, 281, 282, 282, 282, 283, 283, 284, 284, 285, 285, |
| 285, 286, 286, 287, 287, 288, 288, 288, 289, 289, 289, 290, 290, 290, 291, |
| 292, 292, 292, 293, 293, 294, 294, 294, 295, 295, 296, 296, 296, 297, 297, |
| 297, 298, 298, 298, 299, 299, 299, 299, 300, 300, 301, 301, 302, 302, 302, |
| 303, 303, 304, 304, 304, 305, 305, 305, 306, 306, 306, 307, 307, 307, 308, |
| 308, 308, 309, 309, 309, 309, 310, 310, 310, 310, 311, 312, 312, 312, 313, |
| 313, 313, 314, 314, 314, 315, 315, 315, 315, 316, 316, 316, 317, 317, 317, |
| 318, 318, 318, 319, 319, 319, 319, 319, 320, 320, 320, 321, 321, 322, 322, |
| 322, 323, 323, 323, 323, 324, 324, 324, 325, 325, 325, 325, 326, 326, 326, |
| 327, 327, 327, 327, 328, 328, 328, 329, 329, 329, 329, 329, 330, 330, 330, |
| 330, 331, 331, 332, 332, 332, 333, 333, 333, 333, 334, 334, 334, 334, 335, |
| 335, 335, 336, 336, 336, 336, 337, 337, 337, 337, 338, 338, 338, 339, 339, |
| 339, 339, 339, 339, 340, 340, 340, 340, 341, 341, 342, 342, 342, 342, 343, |
| 343, 343, 344, 344, 344, 344, 345, 345, 345, 345, 346, 346, 346, 346, 347, |
| 347, 347, 347, 348, 348, 348, 348, 349, 349, 349, 349, 349, 349, 350, 350, |
| 350, 350, 351, 351, 352, 352, 352, 352, 353, 353, 353, 353, 354, 354, 354, |
| 354, 355, 355, 355, 355, 356, 356, 356, 356, 357, 357, 357, 357, 358, 358, |
| 358, 358, 359, 359, 359, 359, 359, 359, 359, 360, 360, 360, 360, 361, 361, |
| 362, 362, 362, 362, 363, 363, 363, 363, 364, 364, 364, 364, 365, 365, 365, |
| 365, 366, 366, 366, 366, 366, 367, 367, 367, 367, 368, 368, 368, 368, 369, |
| 369, 369, 369, 369, 369, 370, 370, 370, 370, 370, 371, 371, 372, 372, 372, |
| 372, 373, 373, 373, 373, 374, 374, 374, 374, 374, 375, 375, 375, 375, 376, |
| 376, 376, 376, 377, 377, 377, 377, 378, 378, 378, 378, 378, 379, 379, 379, |
| 379, 379, 379, 380, 380, 380, 380, 381, 381, 381, 382, 382, 382, 382, 383, |
| 383, 383, 383, 384, 384, 384, 384, 385, 385, 385, 385, 385, 386, 386, 386, |
| 386, 387, 387, 387, 387, 388, 388, 388, 388, 388, 389, 389, 389, 389, 389, |
| 389, 390, 390, 390, 390, 391, 391, 392, 392, 392, 392, 392, 393, 393, 393, |
| 393, 394, 394, 394, 394, 395, 395, 395, 395, 396, 396, 396, 396, 396, 397, |
| 397, 397, 397, 398, 398, 398, 398, 399, 399, 399, 399, 399, 399, 400, 400, |
| 400, 400, 400, 401, 401, 402, 402, 402, 402, 403, 403, 403, 403, 404, 404, |
| 404, 404, 405, 405, 405, 405, 406, 406, 406, 406, 406, 407, 407, 407, 407, |
| 408, 408, 408, 408, 409, 409, 409, 409, 409, 409, 410, 410, 410, 410, 411, |
| 411, 412, 412, 412, 412, 413, 413, 413, 413, 414, 414, 414, 414, 415, 415, |
| 415, 415, 416, 416, 416, 416, 417, 417, 417, 417, 418, 418, 418, 418, 419, |
| 419, 419, 419, 419, 419, 420, 420, 420, 420, 421, 421, 422, 422, 422, 422, |
| 423, 423, 423, 423, 424, 424, 424, 425, 425, 425, 425, 426, 426, 426, 426, |
| 427, 427, 427, 427, 428, 428, 428, 429, 429, 429, 429, 429, 429, 430, 430, |
| 430, 430, 431, 431, 432, 432, 432, 433, 433, 433, 433, 434, 434, 434, 435, |
| 435, 435, 435, 436, 436, 436, 436, 437, 437, 437, 438, 438, 438, 438, 439, |
| 439, 439, 439, 439, 440, 440, 440, 441, 441, 442, 442, 442, 443, 443, 443, |
| 443, 444, 444, 444, 445, 445, 445, 446, 446, 446, 446, 447, 447, 447, 448, |
| 448, 448, 449, 449, 449, 449, 449, 450, 450, 450, 451, 451, 452, 452, 452, |
| 453, 453, 453, 454, 454, 454, 455, 455, 455, 456, 456, 456, 457, 457, 457, |
| 458, 458, 458, 459, 459, 459, 459, 460, 460, 460, 461, 461, 462, 462, 462, |
| 463, 463, 463, 464, 464, 465, 465, 465, 466, 466, 466, 467, 467, 467, 468, |
| 468, 469, 469, 469, 469, 470, 470, 470, 471, 472, 472, 472, 473, 473, 474, |
| 474, 474, 475, 475, 476, 476, 476, 477, 477, 478, 478, 478, 479, 479, 479, |
| 480, 480, 480, 481, 482, 482, 483, 483, 484, 484, 484, 485, 485, 486, 486, |
| 487, 487, 488, 488, 488, 489, 489, 489, 490, 490, 491, 492, 492, 493, 493, |
| 494, 494, 495, 495, 496, 496, 497, 497, 498, 498, 499, 499, 499, 500, 501, |
| 502, 502, 503, 503, 504, 504, 505, 505, 506, 507, 507, 508, 508, 509, 509, |
| 510, 510, 511, 512, 513, 513, 514, 515, 515, 516, 517, 517, 518, 519, 519, |
| 519, 520, 521, 522, 523, 524, 524, 525, 526, 526, 527, 528, 529, 529, 530, |
| 531, 532, 533, 534, 535, 535, 536, 537, 538, 539, 539, 540, 542, 543, 544, |
| 545, 546, 547, 548, 549, 549, 550, 552, 553, 554, 555, 556, 558, 559, 559, |
| 561, 562, 564, 565, 566, 568, 569, 570, 572, 574, 575, 577, 578, 579, 582, |
| 583, 585, 587, 589, 590, 593, 595, 597, 599, 602, 604, 607, 609, 612, 615, |
| 618, 620, 624, 628, 631, 635, 639, 644, 649, 654, 659, 666, 673, 680, 690, |
| 700, 714}; |
| |
| extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeHEQ( |
| JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale) { |
| pHEQType f = (pHEQType)JNIFunc[JNI_HEQ].func_ptr; |
| return f(env, obj, src_bitmap, dst_bitmap, scale); |
| } |
| |
| extern "C" void HEQ( |
| JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale) { |
| AndroidBitmapInfo src_info; |
| AndroidBitmapInfo dst_info; |
| void* src_pixels; |
| void* dst_pixels; |
| |
| int ret = LockBitmaps( |
| env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels); |
| if (ret < 0) { |
| LOGE("LockBitmaps in HEQ failed, error=%d", ret); |
| return; |
| } |
| |
| const int kEnergyLevels = 766; // 255 * 3 + 1 |
| |
| // The energy has the range of [0, kEnergyLevels] |
| int accumulated_histogram[kEnergyLevels]; |
| memset(accumulated_histogram, 0, sizeof(accumulated_histogram)); |
| |
| // Store all the energy in the dst_pixels |
| pixel32_t* dst = reinterpret_cast<pixel32_t*>(dst_pixels); |
| pixel32_t const* src = reinterpret_cast<pixel32_t const*>(src_pixels); |
| pixel32_t const* src_end = reinterpret_cast<pixel32_t const*>( |
| reinterpret_cast<char const*>(src) + src_info.stride * src_info.height); |
| while (src < src_end) { |
| dst->rgba32 = src->rgba8[0] + src->rgba8[1] + src->rgba8[2]; |
| ++src; |
| ++dst; |
| } |
| |
| // Build up the accumulated histogram table by ignoring borders (1/20 = 5% width). |
| float border_thickness_ratio = 0.05; |
| int y_border_thickness = dst_info.height * border_thickness_ratio; |
| pixel32_t* dst_line = reinterpret_cast<pixel32_t*>( |
| reinterpret_cast<char*>(dst_pixels) + dst_info.stride * y_border_thickness); |
| pixel32_t* dst_line_end = reinterpret_cast<pixel32_t*>( |
| reinterpret_cast<char*>(dst_pixels) + dst_info.stride * |
| (dst_info.height - y_border_thickness)); |
| int x_border_thickness = dst_info.width * border_thickness_ratio; |
| int x_border_end = dst_info.width - x_border_thickness; |
| while (dst_line < dst_line_end) { |
| pixel32_t* dp = dst_line + x_border_thickness; |
| pixel32_t* dp_end = dst_line + x_border_end; |
| while (dp < dp_end) { |
| ++accumulated_histogram[dp->rgba32]; |
| ++dp; |
| } |
| dst_line = reinterpret_cast<pixel32_t*>( |
| reinterpret_cast<char*>(dst_line) + dst_info.stride); |
| } |
| |
| for (int i = 1; i < kEnergyLevels; i++) { |
| accumulated_histogram[i] += accumulated_histogram[i - 1]; |
| } |
| |
| uint32_t const* src_line = |
| reinterpret_cast<uint32_t const*>(src_pixels); |
| dst_line = reinterpret_cast<pixel32_t*>(dst_pixels); |
| dst_line_end = reinterpret_cast<pixel32_t*>(reinterpret_cast<char*>(dst_line) + |
| dst_info.height * dst_info.stride); |
| // The whole process is done by apply the HEQ result with a mask. |
| // The mask is a curve segmented by the energy_middle which could be tuned |
| // based on each bitmap. For the lower part, the mask tries to make the change |
| // significant for greater energy. For the smaller part, the mask does the |
| // contrary. The two curve should be continuous on the energy_middle so the |
| // final result is more natural. In this implementation, what I defined is two |
| // curve based on the energy 'e', for the smaller part, e^2 is used. For the |
| // greater part, e^1.5 is used. That is, for pixel with energy 'e', the mask |
| // is obtained by: |
| // if e > energy_middle |
| // (e - energy_middle)^1.5 / (765 - energy_middle)^1.5 |
| // else |
| // (e - energy_middle)^2 / (energy_middle)^2 |
| const int kShiftBits = 10; |
| const int kShiftValue = (1 << kShiftBits); |
| const int scale_shifted = scale * kShiftValue; |
| const int normalization_scale_shifted = (1.0 - scale) * kShiftValue; |
| const int energy_middle = 382; // 765 / 2 = 382.5 |
| const int normalization_low = 7481; // (765 - 382.5)^1.5 |
| const int normalization_high = 146307; // 382.5^2 |
| int total_pixels = accumulated_histogram[kEnergyLevels - 1]; |
| while (dst_line < dst_line_end) { |
| pixel32_t const* sp = reinterpret_cast<pixel32_t const*>(src_line); |
| pixel32_t* dp = dst_line; |
| pixel32_t* dp_end = dp + dst_info.width; |
| while (dp < dp_end) { |
| if (!dp->rgba32) { // the energy is 0, no changes will be made. |
| dp->rgba32 = sp->rgba32; |
| } else { |
| uint32_t energy = dp->rgba32; |
| int mask_normalization; |
| int mask_value = energy - energy_middle; |
| |
| if (mask_value > 0) { |
| mask_value = mask_value * sqrt(mask_value); |
| mask_normalization = normalization_low; |
| } else { |
| mask_value *= mask_value; |
| mask_normalization = normalization_high; |
| } |
| mask_value = ((mask_value * scale_shifted) + |
| (mask_normalization * (normalization_scale_shifted))) >> kShiftBits; |
| // The final picture is masked by the original energy. |
| // Assumption: Lower energy can result in low-confidence information and |
| // higher energy indicates good confidence. |
| // Therefore, pixels with low and high confidence should not be changed |
| // greatly. |
| uint32_t dst_energy = normal_cdf[ |
| kCDFEntries * accumulated_histogram[energy] / total_pixels]; |
| dst_energy = (energy * mask_value + dst_energy * (mask_normalization - mask_value)) / |
| mask_normalization; |
| |
| // Ensure there is no RGB value will be greater than 255. |
| uint32_t max_energy = energy * 255 / MAX3(sp->rgba8[0], sp->rgba8[1], sp->rgba8[2]); |
| if (dst_energy > max_energy) { |
| dst_energy = max_energy; |
| } |
| |
| dst_energy = (dst_energy << kShiftBits) / energy; |
| uint32_t dst_red = (sp->rgba8[0] * dst_energy) >> kShiftBits; |
| uint32_t dst_green = (sp->rgba8[1] * dst_energy) >> kShiftBits; |
| uint32_t dst_blue = (sp->rgba8[2] * dst_energy) >> kShiftBits; |
| dp->rgba32 = (sp->rgba8[3] << 24) | (dst_blue << 16) | (dst_green << 8) | dst_red; |
| } |
| dp++; |
| sp++; |
| } |
| src_line = reinterpret_cast<uint32_t const*>( |
| reinterpret_cast<char const*>(src_line) + src_info.stride); |
| dst_line = reinterpret_cast<pixel32_t*>( |
| reinterpret_cast<char*>(dst_line) + dst_info.stride); |
| } |
| |
| UnlockBitmaps(env, src_bitmap, dst_bitmap); |
| } |
| |
| } // namespace |