1 /*
2  * Copyright (C) 2012 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 // Native function to extract histogram from image (handed down as ByteBuffer).
18 
19 #include "histogram.h"
20 
21 #include <string.h>
22 #include <jni.h>
23 #include <unistd.h>
24 #include <android/log.h>
25 
26 #include "imgprocutil.h"
27 
addPixelToHistogram(unsigned char * & pImg,int * pHist,int numBins)28 inline void addPixelToHistogram(unsigned char*& pImg, int* pHist, int numBins) {
29     int R = *(pImg++);
30     int G = *(pImg++);
31     int B = *(pImg++);
32     ++pImg;
33     int i = getIntensityFast(R, G, B);
34     int bin = clamp(0, static_cast<int>(static_cast<float>(i * numBins) / 255.0f), numBins - 1);
35     ++pHist[bin];
36 }
37 
Java_androidx_media_filterpacks_histogram_GrayHistogramFilter_extractHistogram(JNIEnv * env,jclass clazz,jobject imageBuffer,jobject maskBuffer,jobject histogramBuffer)38 void Java_androidx_media_filterpacks_histogram_GrayHistogramFilter_extractHistogram(
39     JNIEnv* env, jclass clazz, jobject imageBuffer, jobject maskBuffer, jobject histogramBuffer )
40 {
41     unsigned char* pImg = static_cast<unsigned char*>(env->GetDirectBufferAddress(imageBuffer));
42     int* pHist = static_cast<int*>(env->GetDirectBufferAddress(histogramBuffer));
43     int numPixels  = env->GetDirectBufferCapacity(imageBuffer) / 4;  // 4 bytes per pixel
44     int numBins    = env->GetDirectBufferCapacity(histogramBuffer);
45 
46     unsigned char* pMask = NULL;
47     if(maskBuffer != NULL) {
48         pMask = static_cast<unsigned char*>(env->GetDirectBufferAddress(maskBuffer));
49     }
50 
51     for(int i = 0; i < numBins; ++i) pHist[i] = 0;
52 
53     if(pMask == NULL) {
54         for( ; numPixels > 0; --numPixels) {
55             addPixelToHistogram(pImg, pHist, numBins);
56         }
57     } else {
58         for( ; numPixels > 0; --numPixels) {
59             if(*pMask == 0){
60                 pMask += 4;
61                 pImg  += 4;  // Note that otherwise addPixelToHistogram advances pImg by 4
62                 continue;
63             }
64             pMask += 4;
65             addPixelToHistogram(pImg, pHist, numBins);
66         }
67     }
68 }
69 
Java_androidx_media_filterpacks_histogram_ChromaHistogramFilter_extractChromaHistogram(JNIEnv * env,jclass clazz,jobject imageBuffer,jobject histogramBuffer,jint hBins,jint sBins)70 void Java_androidx_media_filterpacks_histogram_ChromaHistogramFilter_extractChromaHistogram(
71     JNIEnv* env, jclass clazz, jobject imageBuffer, jobject histogramBuffer, jint hBins, jint sBins)
72 {
73     unsigned char* pixelIn = static_cast<unsigned char*>(env->GetDirectBufferAddress(imageBuffer));
74     float* histOut = static_cast<float*>(env->GetDirectBufferAddress(histogramBuffer));
75     int numPixels  = env->GetDirectBufferCapacity(imageBuffer) / 4;  // 4 bytes per pixel
76 
77     for (int i = 0; i < hBins * sBins; ++i) histOut[i] = 0.0f;
78 
79     int h, s, v;
80     float hScaler = hBins / 256.0f;
81     float sScaler = sBins / 256.0f;
82     for( ; numPixels > 0; --numPixels) {
83       h = *(pixelIn++);
84       s = *(pixelIn++);
85       v = *(pixelIn++);
86       pixelIn++;
87 
88       int index = static_cast<int>(s * sScaler) * hBins + static_cast<int>(h * hScaler);
89       histOut[index] += 1.0f;
90     }
91 }
92 
Java_androidx_media_filterpacks_histogram_NewChromaHistogramFilter_extractChromaHistogram(JNIEnv * env,jclass clazz,jobject imageBuffer,jobject histogramBuffer,jint hueBins,jint saturationBins,jint valueBins,jint saturationThreshold,jint valueThreshold)93 void Java_androidx_media_filterpacks_histogram_NewChromaHistogramFilter_extractChromaHistogram(
94     JNIEnv* env, jclass clazz, jobject imageBuffer, jobject histogramBuffer,
95     jint hueBins, jint saturationBins, jint valueBins,
96     jint saturationThreshold, jint valueThreshold) {
97     unsigned char* pixelIn = static_cast<unsigned char*>(env->GetDirectBufferAddress(imageBuffer));
98     float* histOut = static_cast<float*>(env->GetDirectBufferAddress(histogramBuffer));
99     int numPixels  = env->GetDirectBufferCapacity(imageBuffer) / 4;  // 4 bytes per pixel
100 
101     // TODO: add check on the size of histOut
102     for (int i = 0; i < (hueBins * saturationBins + valueBins); ++i) {
103       histOut[i] = 0.0f;
104     }
105 
106     for( ; numPixels > 0; --numPixels) {
107       int h = *(pixelIn++);
108       int s = *(pixelIn++);
109       int v = *(pixelIn++);
110 
111       pixelIn++;
112       // If a pixel that is either too dark (less than valueThreshold) or colorless
113       // (less than saturationThreshold), if will be put in a 1-D value histogram instead.
114 
115       int index;
116       if (s > saturationThreshold && v > valueThreshold) {
117         int sIndex = s * saturationBins / 256;
118 
119         // Shifting hue index by 0.5 such that peaks of red, yellow, green, cyan, blue, pink
120         // will be at the center of some bins.
121         int hIndex = ((h * hueBins + 128) / 256) % hueBins;
122         index = sIndex * hueBins + hIndex;
123       } else {
124         index =  hueBins * saturationBins + (v * valueBins / 256);
125       }
126       histOut[index] += 1.0f;
127     }
128 }
129