1227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks/*
2227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * Copyright (C) 2013 The Android Open Source Project
3227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks *
4227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * Licensed under the Apache License, Version 2.0 (the "License");
5227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * you may not use this file except in compliance with the License.
6227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * You may obtain a copy of the License at
7227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks *
8227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks *      http://www.apache.org/licenses/LICENSE-2.0
9227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks *
10227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * Unless required by applicable law or agreed to in writing, software
11227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * distributed under the License is distributed on an "AS IS" BASIS,
12227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * See the License for the specific language governing permissions and
14227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * limitations under the License.
15227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks */
16227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
17227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "colorspace.h"
18227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
19227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include <jni.h>
20227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include <stdint.h>
21227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
22227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendrickstypedef uint8_t uint8;
23227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendrickstypedef uint32_t uint32;
24227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendrickstypedef int32_t int32;
25227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
26227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks// RGBA helper struct allows access as int and individual channels
27227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks// WARNING: int value depends on endianness and should not be used to analyze individual channels.
28227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksunion Rgba {
29227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  uint32 color;
30227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  uint8 channel[4];
31227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks};
32227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
33227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks// Channel index constants
34227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksstatic const uint8 kRed = 0;
35227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksstatic const uint8 kGreen = 1;
36227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksstatic const uint8 kBlue = 2;
37227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksstatic const uint8 kAlpha = 3;
38227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
39227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks// Clamp to range 0-255
40227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksstatic inline uint32 clamp(int32 x) {
41227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  return x > 255 ? 255 : (x < 0 ? 0 : x);
42227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks}
43227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
44227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks// Convert YUV to RGBA
45227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks// This uses the ITU-R BT.601 coefficients.
46227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksstatic inline Rgba convertYuvToRgba(int32 y, int32 u, int32 v) {
47227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  Rgba color;
48227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  color.channel[kRed] = clamp(y + static_cast<int>(1.402 * v));
49227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  color.channel[kGreen] = clamp(y - static_cast<int>(0.344 * u + 0.714 * v));
50227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  color.channel[kBlue] = clamp(y + static_cast<int>(1.772 * u));
51227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  color.channel[kAlpha] = 0xFF;
52227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  return color;
53227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks}
54227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
55227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks// Colorspace conversion functions /////////////////////////////////////////////////////////////////
56227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksvoid JNI_COLORSPACE_METHOD(nativeYuv420pToRgba8888)(
57227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height) {
58227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  uint8* const pInput = static_cast<uint8*>(env->GetDirectBufferAddress(input));
59227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  Rgba* const pOutput = static_cast<Rgba*>(env->GetDirectBufferAddress(output));
60227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
61227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  const int size = width * height;
62227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
63227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  uint8* pInY = pInput;
64227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  uint8* pInU = pInput + size;
65227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  uint8* pInV = pInput + size + size / 4;
66227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  Rgba* pOutColor = pOutput;
67227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
68227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  for (int y = 0; y < height; y += 2) {
69227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    for (int x = 0; x < width; x += 2) {
70227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      int u, v, y1, y2, y3, y4;
71227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
72227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      y1 = pInY[0];
73227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      y2 = pInY[1];
74227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      y3 = pInY[width];
75227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      y4 = pInY[width + 1];
76227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
77227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      u = *pInU - 128;
78227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      v = *pInV - 128;
79227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
80227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      pOutColor[0] = convertYuvToRgba(y1, u, v);
81227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      pOutColor[1] = convertYuvToRgba(y2, u, v);
82227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      pOutColor[width] = convertYuvToRgba(y3, u, v);
83227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      pOutColor[width + 1] = convertYuvToRgba(y4, u, v);
84227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
85227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      pInY += 2;
86227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      pInU++;
87227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      pInV++;
88227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      pOutColor += 2;
89227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    }
90227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    pInY += width;
91227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    pOutColor += width;
92227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  }
93227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks}
94227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
95227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksvoid JNI_COLORSPACE_METHOD(nativeArgb8888ToRgba8888)(
96227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height) {
97227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  Rgba* pInput = static_cast<Rgba*>(env->GetDirectBufferAddress(input));
98227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  Rgba* pOutput = static_cast<Rgba*>(env->GetDirectBufferAddress(output));
99227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
100227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  for (int i = 0; i < width * height; ++i) {
101227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    Rgba color_in = *pInput++;
102227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    Rgba& color_out = *pOutput++;
103227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    color_out.channel[kRed] = color_in.channel[kGreen];
104227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    color_out.channel[kGreen] = color_in.channel[kBlue];
105227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    color_out.channel[kBlue] = color_in.channel[kAlpha];
106227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    color_out.channel[kAlpha] = color_in.channel[kRed];
107227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  }
108227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks}
109227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
110227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksvoid JNI_COLORSPACE_METHOD(nativeRgba8888ToHsva8888)(
111227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height) {
112227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  Rgba* pInput = static_cast<Rgba*>(env->GetDirectBufferAddress(input));
113227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  Rgba* pOutput = static_cast<Rgba*>(env->GetDirectBufferAddress(output));
114227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
115227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  int r, g, b, a, h, s, v, c_max, c_min;
116227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  float delta;
117227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  for (int i = 0; i < width * height; ++i) {
118227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    Rgba color_in = *pInput++;
119227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    Rgba& color_out = *pOutput++;
120227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    r = color_in.channel[kRed];
121227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    g = color_in.channel[kGreen];
122227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    b = color_in.channel[kBlue];
123227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    a = color_in.channel[kAlpha];
124227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
125227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    if (r > g) {
126227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      c_min = (g > b) ? b : g;
127227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      c_max = (r > b) ? r : b;
128227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    } else {
129227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      c_min = (r > b) ? b : r;
130227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      c_max = (g > b) ? g : b;
131227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    }
132227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    delta = c_max -c_min;
133227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
134227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    float scaler = 255 * 60 / 360.0f;
135227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    if (c_max == r) {
136227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      h = (g > b) ? static_cast<int>(scaler * (g - b) / delta) :
137227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks          static_cast<int>(scaler * ((g - b) / delta + 6));
138227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    } else if (c_max == g) {
139227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      h = static_cast<int>(scaler * ((b - r) / delta + 2));
140227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    } else {  // Cmax == b
141227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      h = static_cast<int>(scaler * ((r - g) / delta + 4));
142227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    }
143227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    s = (delta == 0.0f) ? 0 : static_cast<unsigned char>(delta / c_max * 255);
144227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    v = c_max;
145227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
146227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    color_out.channel[kRed] = h;
147227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    color_out.channel[kGreen] = s;
148227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    color_out.channel[kBlue] = v;
149227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    color_out.channel[kAlpha] = a;
150227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  }
151227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks}
152227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
153227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksvoid JNI_COLORSPACE_METHOD(nativeRgba8888ToYcbcra8888)(
154227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height) {
155227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  Rgba* pInput = static_cast<Rgba*>(env->GetDirectBufferAddress(input));
156227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  Rgba* pOutput = static_cast<Rgba*>(env->GetDirectBufferAddress(output));
157227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
158227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  int r, g, b;
159227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  for (int i = 0; i < width * height; ++i) {
160227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    Rgba color_in = *pInput++;
161227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    Rgba& color_out = *pOutput++;
162227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    r = color_in.channel[kRed];
163227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    g = color_in.channel[kGreen];
164227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    b = color_in.channel[kBlue];
165227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
166227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    color_out.channel[kRed] =
167227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        static_cast<unsigned char>((65.738 * r + 129.057 * g + 25.064 * b) / 256 + 16);
168227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    color_out.channel[kGreen] =
169227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        static_cast<unsigned char>((-37.945 * r - 74.494 * g + 112.439 * b) / 256 + 128);
170227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    color_out.channel[kBlue] =
171227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        static_cast<unsigned char>((112.439 * r - 94.154 * g - 18.285 * b) / 256 + 128);
172227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    color_out.channel[kAlpha] = color_in.channel[kAlpha];
173227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  }
174227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks}
175