1 /* 2 * Copyright (C) 2013 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 "colorspace.h" 18 19 #include <jni.h> 20 #include <stdint.h> 21 22 typedef uint8_t uint8; 23 typedef uint32_t uint32; 24 typedef int32_t int32; 25 26 // RGBA helper struct allows access as int and individual channels 27 // WARNING: int value depends on endianness and should not be used to analyze individual channels. 28 union Rgba { 29 uint32 color; 30 uint8 channel[4]; 31 }; 32 33 // Channel index constants 34 static const uint8 kRed = 0; 35 static const uint8 kGreen = 1; 36 static const uint8 kBlue = 2; 37 static const uint8 kAlpha = 3; 38 39 // Clamp to range 0-255 40 static inline uint32 clamp(int32 x) { 41 return x > 255 ? 255 : (x < 0 ? 0 : x); 42 } 43 44 // Convert YUV to RGBA 45 // This uses the ITU-R BT.601 coefficients. 46 static inline Rgba convertYuvToRgba(int32 y, int32 u, int32 v) { 47 Rgba color; 48 color.channel[kRed] = clamp(y + static_cast<int>(1.402 * v)); 49 color.channel[kGreen] = clamp(y - static_cast<int>(0.344 * u + 0.714 * v)); 50 color.channel[kBlue] = clamp(y + static_cast<int>(1.772 * u)); 51 color.channel[kAlpha] = 0xFF; 52 return color; 53 } 54 55 // Colorspace conversion functions ///////////////////////////////////////////////////////////////// 56 void JNI_COLORSPACE_METHOD(nativeYuv420pToRgba8888)( 57 JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height) { 58 uint8* const pInput = static_cast<uint8*>(env->GetDirectBufferAddress(input)); 59 Rgba* const pOutput = static_cast<Rgba*>(env->GetDirectBufferAddress(output)); 60 61 const int size = width * height; 62 63 uint8* pInY = pInput; 64 uint8* pInU = pInput + size; 65 uint8* pInV = pInput + size + size / 4; 66 Rgba* pOutColor = pOutput; 67 68 for (int y = 0; y < height; y += 2) { 69 for (int x = 0; x < width; x += 2) { 70 int u, v, y1, y2, y3, y4; 71 72 y1 = pInY[0]; 73 y2 = pInY[1]; 74 y3 = pInY[width]; 75 y4 = pInY[width + 1]; 76 77 u = *pInU - 128; 78 v = *pInV - 128; 79 80 pOutColor[0] = convertYuvToRgba(y1, u, v); 81 pOutColor[1] = convertYuvToRgba(y2, u, v); 82 pOutColor[width] = convertYuvToRgba(y3, u, v); 83 pOutColor[width + 1] = convertYuvToRgba(y4, u, v); 84 85 pInY += 2; 86 pInU++; 87 pInV++; 88 pOutColor += 2; 89 } 90 pInY += width; 91 pOutColor += width; 92 } 93 } 94 95 void JNI_COLORSPACE_METHOD(nativeArgb8888ToRgba8888)( 96 JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height) { 97 Rgba* pInput = static_cast<Rgba*>(env->GetDirectBufferAddress(input)); 98 Rgba* pOutput = static_cast<Rgba*>(env->GetDirectBufferAddress(output)); 99 100 for (int i = 0; i < width * height; ++i) { 101 Rgba color_in = *pInput++; 102 Rgba& color_out = *pOutput++; 103 color_out.channel[kRed] = color_in.channel[kGreen]; 104 color_out.channel[kGreen] = color_in.channel[kBlue]; 105 color_out.channel[kBlue] = color_in.channel[kAlpha]; 106 color_out.channel[kAlpha] = color_in.channel[kRed]; 107 } 108 } 109 110 void JNI_COLORSPACE_METHOD(nativeRgba8888ToHsva8888)( 111 JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height) { 112 Rgba* pInput = static_cast<Rgba*>(env->GetDirectBufferAddress(input)); 113 Rgba* pOutput = static_cast<Rgba*>(env->GetDirectBufferAddress(output)); 114 115 int r, g, b, a, h, s, v, c_max, c_min; 116 float delta; 117 for (int i = 0; i < width * height; ++i) { 118 Rgba color_in = *pInput++; 119 Rgba& color_out = *pOutput++; 120 r = color_in.channel[kRed]; 121 g = color_in.channel[kGreen]; 122 b = color_in.channel[kBlue]; 123 a = color_in.channel[kAlpha]; 124 125 if (r > g) { 126 c_min = (g > b) ? b : g; 127 c_max = (r > b) ? r : b; 128 } else { 129 c_min = (r > b) ? b : r; 130 c_max = (g > b) ? g : b; 131 } 132 delta = c_max -c_min; 133 134 float scaler = 255 * 60 / 360.0f; 135 if (c_max == r) { 136 h = (g > b) ? static_cast<int>(scaler * (g - b) / delta) : 137 static_cast<int>(scaler * ((g - b) / delta + 6)); 138 } else if (c_max == g) { 139 h = static_cast<int>(scaler * ((b - r) / delta + 2)); 140 } else { // Cmax == b 141 h = static_cast<int>(scaler * ((r - g) / delta + 4)); 142 } 143 s = (delta == 0.0f) ? 0 : static_cast<unsigned char>(delta / c_max * 255); 144 v = c_max; 145 146 color_out.channel[kRed] = h; 147 color_out.channel[kGreen] = s; 148 color_out.channel[kBlue] = v; 149 color_out.channel[kAlpha] = a; 150 } 151 } 152 153 void JNI_COLORSPACE_METHOD(nativeRgba8888ToYcbcra8888)( 154 JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height) { 155 Rgba* pInput = static_cast<Rgba*>(env->GetDirectBufferAddress(input)); 156 Rgba* pOutput = static_cast<Rgba*>(env->GetDirectBufferAddress(output)); 157 158 int r, g, b; 159 for (int i = 0; i < width * height; ++i) { 160 Rgba color_in = *pInput++; 161 Rgba& color_out = *pOutput++; 162 r = color_in.channel[kRed]; 163 g = color_in.channel[kGreen]; 164 b = color_in.channel[kBlue]; 165 166 color_out.channel[kRed] = 167 static_cast<unsigned char>((65.738 * r + 129.057 * g + 25.064 * b) / 256 + 16); 168 color_out.channel[kGreen] = 169 static_cast<unsigned char>((-37.945 * r - 74.494 * g + 112.439 * b) / 256 + 128); 170 color_out.channel[kBlue] = 171 static_cast<unsigned char>((112.439 * r - 94.154 * g - 18.285 * b) / 256 + 128); 172 color_out.channel[kAlpha] = color_in.channel[kAlpha]; 173 } 174 } 175