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 22typedef uint8_t uint8; 23typedef uint32_t uint32; 24typedef 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. 28union Rgba { 29 uint32 color; 30 uint8 channel[4]; 31}; 32 33// Channel index constants 34static const uint8 kRed = 0; 35static const uint8 kGreen = 1; 36static const uint8 kBlue = 2; 37static const uint8 kAlpha = 3; 38 39// Clamp to range 0-255 40static 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. 46static 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 ///////////////////////////////////////////////////////////////// 56void 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 const int u_offset = size; 69 const int v_offset = u_offset + size / 4; 70 71 for (int y = 0; y < height; y += 2) { 72 for (int x = 0; x < width; x += 2) { 73 int u, v, y1, y2, y3, y4; 74 75 y1 = pInY[0]; 76 y2 = pInY[1]; 77 y3 = pInY[width]; 78 y4 = pInY[width + 1]; 79 80 u = *pInU - 128; 81 v = *pInV - 128; 82 83 pOutColor[0] = convertYuvToRgba(y1, u, v); 84 pOutColor[1] = convertYuvToRgba(y2, u, v); 85 pOutColor[width] = convertYuvToRgba(y3, u, v); 86 pOutColor[width + 1] = convertYuvToRgba(y4, u, v); 87 88 pInY += 2; 89 pInU++; 90 pInV++; 91 pOutColor += 2; 92 } 93 pInY += width; 94 pOutColor += width; 95 } 96} 97 98void JNI_COLORSPACE_METHOD(nativeArgb8888ToRgba8888)( 99 JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height) { 100 Rgba* pInput = static_cast<Rgba*>(env->GetDirectBufferAddress(input)); 101 Rgba* pOutput = static_cast<Rgba*>(env->GetDirectBufferAddress(output)); 102 103 for (int i = 0; i < width * height; ++i) { 104 Rgba color_in = *pInput++; 105 Rgba& color_out = *pOutput++; 106 color_out.channel[kRed] = color_in.channel[kGreen]; 107 color_out.channel[kGreen] = color_in.channel[kBlue]; 108 color_out.channel[kBlue] = color_in.channel[kAlpha]; 109 color_out.channel[kAlpha] = color_in.channel[kRed]; 110 } 111} 112 113void JNI_COLORSPACE_METHOD(nativeRgba8888ToHsva8888)( 114 JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height) { 115 Rgba* pInput = static_cast<Rgba*>(env->GetDirectBufferAddress(input)); 116 Rgba* pOutput = static_cast<Rgba*>(env->GetDirectBufferAddress(output)); 117 118 int r, g, b, a, h, s, v, c_max, c_min; 119 float delta; 120 for (int i = 0; i < width * height; ++i) { 121 Rgba color_in = *pInput++; 122 Rgba& color_out = *pOutput++; 123 r = color_in.channel[kRed]; 124 g = color_in.channel[kGreen]; 125 b = color_in.channel[kBlue]; 126 a = color_in.channel[kAlpha]; 127 128 if (r > g) { 129 c_min = (g > b) ? b : g; 130 c_max = (r > b) ? r : b; 131 } else { 132 c_min = (r > b) ? b : r; 133 c_max = (g > b) ? g : b; 134 } 135 delta = c_max -c_min; 136 137 float scaler = 255 * 60 / 360.0f; 138 if (c_max == r) { 139 h = (g > b) ? static_cast<int>(scaler * (g - b) / delta) : 140 static_cast<int>(scaler * ((g - b) / delta + 6)); 141 } else if (c_max == g) { 142 h = static_cast<int>(scaler * ((b - r) / delta + 2)); 143 } else { // Cmax == b 144 h = static_cast<int>(scaler * ((r - g) / delta + 4)); 145 } 146 s = (delta == 0.0f) ? 0 : static_cast<unsigned char>(delta / c_max * 255); 147 v = c_max; 148 149 color_out.channel[kRed] = h; 150 color_out.channel[kGreen] = s; 151 color_out.channel[kBlue] = v; 152 color_out.channel[kAlpha] = a; 153 } 154} 155 156void JNI_COLORSPACE_METHOD(nativeRgba8888ToYcbcra8888)( 157 JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height) { 158 Rgba* pInput = static_cast<Rgba*>(env->GetDirectBufferAddress(input)); 159 Rgba* pOutput = static_cast<Rgba*>(env->GetDirectBufferAddress(output)); 160 161 int r, g, b; 162 for (int i = 0; i < width * height; ++i) { 163 Rgba color_in = *pInput++; 164 Rgba& color_out = *pOutput++; 165 r = color_in.channel[kRed]; 166 g = color_in.channel[kGreen]; 167 b = color_in.channel[kBlue]; 168 169 color_out.channel[kRed] = 170 static_cast<unsigned char>((65.738 * r + 129.057 * g + 25.064 * b) / 256 + 16); 171 color_out.channel[kGreen] = 172 static_cast<unsigned char>((-37.945 * r - 74.494 * g + 112.439 * b) / 256 + 128); 173 color_out.channel[kBlue] = 174 static_cast<unsigned char>((112.439 * r - 94.154 * g - 18.285 * b) / 256 + 128); 175 color_out.channel[kAlpha] = color_in.channel[kAlpha]; 176 } 177} 178