blob: 5e7be3c4c19e164bfb53a637e3ef51c12ad87b69 [file] [log] [blame]
/*
* 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 <math.h>
#include "filters.h"
void JNIFUNCF(ImageFilterEdge, nativeApplyFilter, jobject bitmap, jint width, jint height, jfloat p)
{
uint8_t* destination = 0;
AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
// using contrast function:
// f(v) = exp(-alpha * v^beta)
// use beta ~ 1
float const alpha = 5.0f;
float const beta = p;
float const c_min = 100.0f;
float const c_max = 500.0f;
// pixels must be 4 bytes
uint8_t* dst = destination;
int j, k;
uint8_t* ptr = destination;
int row_stride = 4 * width;
// set 2 row buffer (avoids bitmap copy)
int buf_len = 2 * row_stride;
uint8_t buf[buf_len];
int buf_row_ring = 0;
// set initial buffer to black
memset(buf, 0, buf_len * sizeof(char));
for (j = 3; j < buf_len; j+=4) {
*(buf + j) = 255; // set initial alphas
}
// apply sobel filter
for (j = 1; j < height - 1; j++) {
for (k = 1; k < width - 1; k++){
int loc = j * row_stride + k * 4;
float bestx = 0.0f;
int l;
for (l = 0; l < 3; l++) {
float tmp = 0.0f;
tmp += *(ptr + (loc - row_stride + 4 + l));
tmp += *(ptr + (loc + 4 + l)) * 2.0f;
tmp += *(ptr + (loc + row_stride + 4 + l));
tmp -= *(ptr + (loc - row_stride - 4 + l));
tmp -= *(ptr + (loc - 4 + l)) * 2.0f;
tmp -= *(ptr + (loc + row_stride - 4 + l));
if (fabs(tmp) > fabs(bestx)) {
bestx = tmp;
}
}
float besty = 0.0f;
for (l = 0; l < 3; l++) {
float tmp = 0.0f;
tmp -= *(ptr + (loc - row_stride - 4 + l));
tmp -= *(ptr + (loc - row_stride + l)) * 2.0f;
tmp -= *(ptr + (loc - row_stride + 4 + l));
tmp += *(ptr + (loc + row_stride - 4 + l));
tmp += *(ptr + (loc + row_stride + l)) * 2.0f;
tmp += *(ptr + (loc + row_stride + 4 + l));
if (fabs(tmp) > fabs(besty)) {
besty = tmp;
}
}
// compute gradient magnitude
float mag = sqrt(bestx * bestx + besty * besty);
// clamp
mag = MIN(MAX(c_min, mag), c_max);
// scale to [0, 1]
mag = (mag - c_min) / (c_max - c_min);
float ret = 1.0f - exp (- alpha * pow(mag, beta));
ret = 255 * ret;
int off = k * 4;
*(buf + buf_row_ring + off) = ret;
*(buf + buf_row_ring + off + 1) = ret;
*(buf + buf_row_ring + off + 2) = ret;
*(buf + buf_row_ring + off + 3) = *(ptr + loc + 3);
}
buf_row_ring += row_stride;
buf_row_ring %= buf_len;
if (j - 1 >= 0) {
memcpy((dst + row_stride * (j - 1)), (buf + buf_row_ring), row_stride * sizeof(char));
}
}
buf_row_ring += row_stride;
buf_row_ring %= buf_len;
int second_last_row = row_stride * (height - 2);
memcpy((dst + second_last_row), (buf + buf_row_ring), row_stride * sizeof(char));
// set last row to black
int last_row = row_stride * (height - 1);
memset((dst + last_row), 0, row_stride * sizeof(char));
for (j = 3; j < row_stride; j+=4) {
*(dst + last_row + j) = 255; // set alphas
}
AndroidBitmap_unlockPixels(env, bitmap);
}