blob: a785540d237cb6510feff41d2c2b06e748f2fef2 [file] [log] [blame]
/*
* Copyright (C) 2011 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#elif _DARWIN_C_SOURCE
#else
#include <linux/videodev2.h>
#endif
#include "android/camera/camera-format-converters.h"
#define E(...) derror(__VA_ARGS__)
#define W(...) dwarning(__VA_ARGS__)
#define D(...) VERBOSE_PRINT(camera,__VA_ARGS__)
#define D_ACTIVE VERBOSE_CHECK(camera)
/*
* NOTE: RGB and big/little endian considerations. Wherewer in this code RGB
* pixels are represented as WORD, or DWORD, the color order inside the
* WORD / DWORD matches the one that would occur if that WORD / DWORD would have
* been read from the typecasted framebuffer:
*
* const uint32_t rgb = *reinterpret_cast<const uint32_t*>(framebuffer);
*
* So, if this code runs on the little endian CPU, red color in 'rgb' would be
* masked as 0x000000ff, and blue color would be masked as 0x00ff0000, while if
* the code runs on a big endian CPU, the red color in 'rgb' would be masked as
* 0xff000000, and blue color would be masked as 0x0000ff00,
*/
/*
* RGB565 color masks
*/
#ifndef HOST_WORDS_BIGENDIAN
static const uint16_t kRed5 = 0x001f;
static const uint16_t kGreen6 = 0x07e0;
static const uint16_t kBlue5 = 0xf800;
#else // !HOST_WORDS_BIGENDIAN
static const uint16_t kRed5 = 0xf800;
static const uint16_t kGreen6 = 0x07e0;
static const uint16_t kBlue5 = 0x001f;
#endif // !HOST_WORDS_BIGENDIAN
/*
* RGB32 color masks
*/
#ifndef HOST_WORDS_BIGENDIAN
static const uint32_t kRed8 = 0x000000ff;
static const uint32_t kGreen8 = 0x0000ff00;
static const uint32_t kBlue8 = 0x00ff0000;
#else // !HOST_WORDS_BIGENDIAN
static const uint32_t kRed8 = 0x00ff0000;
static const uint32_t kGreen8 = 0x0000ff00;
static const uint32_t kBlue8 = 0x000000ff;
#endif // !HOST_WORDS_BIGENDIAN
/*
* Extracting, and saving color bytes from / to WORD / DWORD RGB.
*/
#ifndef HOST_WORDS_BIGENDIAN
/* Extract red, green, and blue bytes from RGB565 word. */
#define R16(rgb) (uint8_t)((rgb) & kRed5)
#define G16(rgb) (uint8_t)(((rgb) & kGreen6) >> 5)
#define B16(rgb) (uint8_t)(((rgb) & kBlue5) >> 11)
/* Make 8 bits red, green, and blue, extracted from RGB565 word. */
#define R16_32(rgb) (uint8_t)((((rgb) & kRed5) << 3) | (((rgb) & kRed5) >> 2))
#define G16_32(rgb) (uint8_t)((((rgb) & kGreen6) >> 3) | (((rgb) & kGreen6) >> 9))
#define B16_32(rgb) (uint8_t)((((rgb) & kBlue5) >> 8) | (((rgb) & kBlue5) >> 14))
/* Extract red, green, and blue bytes from RGB32 dword. */
#define R32(rgb) (uint8_t)((rgb) & kRed8)
#define G32(rgb) (uint8_t)((((rgb) & kGreen8) >> 8) & 0xff)
#define B32(rgb) (uint8_t)((((rgb) & kBlue8) >> 16) & 0xff)
/* Build RGB565 word from red, green, and blue bytes. */
#define RGB565(r, g, b) (uint16_t)(((((uint16_t)(b) << 6) | (g)) << 5) | (r))
/* Build RGB32 dword from red, green, and blue bytes. */
#define RGB32(r, g, b) (uint32_t)(((((uint32_t)(b) << 8) | (g)) << 8) | (r))
#else // !HOST_WORDS_BIGENDIAN
/* Extract red, green, and blue bytes from RGB565 word. */
#define R16(rgb) (uint8_t)(((rgb) & kRed5) >> 11)
#define G16(rgb) (uint8_t)(((rgb) & kGreen6) >> 5)
#define B16(rgb) (uint8_t)((rgb) & kBlue5)
/* Make 8 bits red, green, and blue, extracted from RGB565 word. */
#define R16_32(rgb) (uint8_t)((((rgb) & kRed5) >> 8) | (((rgb) & kRed5) >> 14))
#define G16_32(rgb) (uint8_t)((((rgb) & kGreen6) >> 3) | (((rgb) & kGreen6) >> 9))
#define B16_32(rgb) (uint8_t)((((rgb) & kBlue5) << 3) | (((rgb) & kBlue5) >> 2))
/* Extract red, green, and blue bytes from RGB32 dword. */
#define R32(rgb) (uint8_t)(((rgb) & kRed8) >> 16)
#define G32(rgb) (uint8_t)(((rgb) & kGreen8) >> 8)
#define B32(rgb) (uint8_t)((rgb) & kBlue8)
/* Build RGB565 word from red, green, and blue bytes. */
#define RGB565(r, g, b) (uint16_t)(((((uint16_t)(r) << 6) | (g)) << 5) | (b))
/* Build RGB32 dword from red, green, and blue bytes. */
#define RGB32(r, g, b) (uint32_t)(((((uint32_t)(r) << 8) | (g)) << 8) | (b))
#endif // !HOST_WORDS_BIGENDIAN
/*
* BAYER bitmasks
*/
/* Bitmask for 8-bits BAYER pixel. */
#define kBayer8 0xff
/* Bitmask for 10-bits BAYER pixel. */
#define kBayer10 0x3ff
/* Bitmask for 12-bits BAYER pixel. */
#define kBayer12 0xfff
/* An union that simplifies breaking 32 bit RGB into separate R, G, and B colors.
*/
typedef union RGB32_t {
uint32_t color;
struct {
#ifndef HOST_WORDS_BIGENDIAN
uint8_t r; uint8_t g; uint8_t b; uint8_t a;
#else // !HOST_WORDS_BIGENDIAN
uint8_t a; uint8_t b; uint8_t g; uint8_t r;
#endif // HOST_WORDS_BIGENDIAN
};
} RGB32_t;
/* Clips a value to the unsigned 0-255 range, treating negative values as zero.
*/
static __inline__ int
clamp(int x)
{
if (x > 255) return 255;
if (x < 0) return 0;
return x;
}
/********************************************************************************
* Basics of RGB -> YUV conversion
*******************************************************************************/
/*
* RGB -> YUV conversion macros
*/
#define RGB2Y(r, g, b) (uint8_t)(((66 * (r) + 129 * (g) + 25 * (b) + 128) >> 8) + 16)
#define RGB2U(r, g, b) (uint8_t)(((-38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128)
#define RGB2V(r, g, b) (uint8_t)(((112 * (r) - 94 * (g) - 18 * (b) + 128) >> 8) + 128)
/* Converts R8 G8 B8 color to YUV. */
static __inline__ void
R8G8B8ToYUV(uint8_t r, uint8_t g, uint8_t b, uint8_t* y, uint8_t* u, uint8_t* v)
{
*y = RGB2Y((int)r, (int)g, (int)b);
*u = RGB2U((int)r, (int)g, (int)b);
*v = RGB2V((int)r, (int)g, (int)b);
}
/* Converts RGB565 color to YUV. */
static __inline__ void
RGB565ToYUV(uint16_t rgb, uint8_t* y, uint8_t* u, uint8_t* v)
{
R8G8B8ToYUV(R16_32(rgb), G16_32(rgb), B16_32(rgb), y, u, v);
}
/* Converts RGB32 color to YUV. */
static __inline__ void
RGB32ToYUV(uint32_t rgb, uint8_t* y, uint8_t* u, uint8_t* v)
{
RGB32_t rgb_c;
rgb_c.color = rgb;
R8G8B8ToYUV(rgb_c.r, rgb_c.g, rgb_c.b, y, u, v);
}
/********************************************************************************
* Basics of YUV -> RGB conversion.
*******************************************************************************/
/*
* YUV -> RGB conversion macros
*/
/* "Optimized" macros that take specialy prepared Y, U, and V values:
* C = Y - 16
* D = U - 128
* E = V - 128
*/
#define YUV2RO(C, D, E) clamp((298 * (C) + 409 * (E) + 128) >> 8)
#define YUV2GO(C, D, E) clamp((298 * (C) - 100 * (D) - 208 * (E) + 128) >> 8)
#define YUV2BO(C, D, E) clamp((298 * (C) + 516 * (D) + 128) >> 8)
/*
* Main macros that take the original Y, U, and V values
*/
#define YUV2R(y, u, v) clamp((298 * ((y)-16) + 409 * ((v)-128) + 128) >> 8)
#define YUV2G(y, u, v) clamp((298 * ((y)-16) - 100 * ((u)-128) - 208 * ((v)-128) + 128) >> 8)
#define YUV2B(y, u, v) clamp((298 * ((y)-16) + 516 * ((u)-128) + 128) >> 8)
/* Converts YUV color to RGB565. */
static __inline__ uint16_t
YUVToRGB565(int y, int u, int v)
{
/* Calculate C, D, and E values for the optimized macro. */
y -= 16; u -= 128; v -= 128;
const uint16_t r = YUV2RO(y,u,v) >> 3;
const uint16_t g = YUV2GO(y,u,v) >> 2;
const uint16_t b = YUV2BO(y,u,v) >> 3;
return RGB565(r, g, b);
}
/* Converts YUV color to RGB32. */
static __inline__ uint32_t
YUVToRGB32(int y, int u, int v)
{
/* Calculate C, D, and E values for the optimized macro. */
y -= 16; u -= 128; v -= 128;
RGB32_t rgb;
rgb.r = YUV2RO(y,u,v);
rgb.g = YUV2GO(y,u,v);
rgb.b = YUV2BO(y,u,v);
return rgb.color;
}
/* Converts YUV color to separated RGB32 colors. */
static __inline__ void
YUVToRGBPix(int y, int u, int v, uint8_t* r, uint8_t* g, uint8_t* b)
{
/* Calculate C, D, and E values for the optimized macro. */
y -= 16; u -= 128; v -= 128;
*r = (uint8_t)YUV2RO(y,u,v);
*g = (uint8_t)YUV2GO(y,u,v);
*b = (uint8_t)YUV2BO(y,u,v);
}
/* Computes a luminance value after taking the exposure compensation.
* value into account.
*
* Param:
* inputY - The input luminance value.
* Return:
* The luminance value after adjusting the exposure compensation.
*/
static __inline__ uint8_t
_change_exposure(uint8_t inputY, float exp_comp)
{
return (uint8_t)clamp((float)inputY * exp_comp);
}
/* Adjusts an RGB pixel for the given exposure compensation. */
static __inline__ void
_change_exposure_RGB(uint8_t* r, uint8_t* g, uint8_t* b, float exp_comp)
{
uint8_t y, u, v;
R8G8B8ToYUV(*r, *g, *b, &y, &u, &v);
YUVToRGBPix(_change_exposure(y, exp_comp), u, v, r, g, b);
}
/* Adjusts an RGB pixel for the given exposure compensation. */
static __inline__ void
_change_exposure_RGB_i(int* r, int* g, int* b, float exp_comp)
{
uint8_t y, u, v;
R8G8B8ToYUV(*r, *g, *b, &y, &u, &v);
y = _change_exposure(y, exp_comp);
*r = YUV2RO(y,u,v);
*g = YUV2GO(y,u,v);
*b = YUV2BO(y,u,v);
}
/* Computes the pixel value after adjusting the white balance to the current
* one. The input the y, u, v channel of the pixel and the adjusted value will
* be stored in place. The adjustment is done in RGB space.
*/
static __inline__ void
_change_white_balance_YUV(uint8_t* y,
uint8_t* u,
uint8_t* v,
float r_scale,
float g_scale,
float b_scale)
{
int r = (float)(YUV2R((int)*y, (int)*u, (int)*v)) / r_scale;
int g = (float)(YUV2G((int)*y, (int)*u, (int)*v)) / g_scale;
int b = (float)(YUV2B((int)*y, (int)*u, (int)*v)) / b_scale;
*y = RGB2Y(r, g, b);
*u = RGB2U(r, g, b);
*v = RGB2V(r, g, b);
}
/* Computes the pixel value after adjusting the white balance to the current
* one. The input the r, and b channels of the pixel and the adjusted value will
* be stored in place.
*/
static __inline__ void
_change_white_balance_RGB(int* r,
int* g,
int* b,
float r_scale,
float g_scale,
float b_scale)
{
*r = (float)*r / r_scale;
*g = (float)*g / g_scale;
*b = (float)*b / b_scale;
}
/* Computes the pixel value after adjusting the white balance to the current
* one. The input the r, and b channels of the pixel and the adjusted value will
* be stored in place.
*/
static __inline__ void
_change_white_balance_RGB_b(uint8_t* r,
uint8_t* g,
uint8_t* b,
float r_scale,
float g_scale,
float b_scale)
{
*r = (float)*r / r_scale;
*g = (float)*g / g_scale;
*b = (float)*b / b_scale;
}
/********************************************************************************
* Generic converters between YUV and RGB formats
*******************************************************************************/
/*
* The converters go line by line, convering one frame format to another.
* It's pretty much straight forward for RGB/BRG, where all colors are
* grouped next to each other in memory. The only two things that differ one RGB
* format from another are:
* - Is it an RGB, or BRG (i.e. color ordering)
* - Is it 16, 24, or 32 bits format.
* All these differences are addressed by load_rgb / save_rgb routines, provided
* for each format in the RGB descriptor to load / save RGB color bytes from / to
* the buffer. As far as moving from one RGB pixel to the next, there
* are two question to consider:
* - How many bytes it takes to encode one RGB pixel (could be 2, 3, or 4)
* - How many bytes it takes to encode a line (i.e. line alignment issue, which
* makes sence only for 24-bit formats, since 16, and 32 bit formats provide
* automatic word alignment.)
* The first question is answered with the 'rgb_inc' field of the RGB descriptor,
* and the second one is done by aligning rgb pointer up to the nearest 16 bit
* boundaries at the end of each line.
* YUV format has much greater complexity for conversion. in YUV color encoding
* is divided into three separate panes that can be mixed together in any way
* imaginable. Fortunately, there are still some consistent patterns in different
* YUV formats that can be abstracted through a descriptor:
* - At the line-by-line level, colors are always groupped aroud pairs of pixels,
* where each pixel in the pair has its own Y value, and each pair of pixels
* share thesame U, and V values.
* - Position of Y, U, and V is the same for each pair, so the distance between
* Ys, and U/V for adjacent pairs is the same.
* - Inside the pair, the distance between two Ys is always the same.
* Moving between the lines in YUV can also be easily formalized. Essentially,
* there are three ways how color panes are arranged:
* 1. All interleaved, where all three Y, U, and V values are encoded together in
* one block:
* 1,2 3,4 5,6 n,n+1
* YUVY YUVY YUVY .... YUVY
*
* This type is used to encode YUV 4:2:2 formats.
*
* 2. One separate block of memory for Y pane, and one separate block of memory
* containing interleaved U, and V panes.
*
* YY | YY | YY | YY
* YY | YY | YY | YY
* -----------------
* UV | UV | UV | UV
* -----------------
*
* This type is used to encode 4:2:0 formats.
*
* 3. Three separate blocks of memory for each pane.
*
* YY | YY | YY | YY
* YY | YY | YY | YY
* -----------------
* U | U | U | U
* V | V | V | V
* -----------------
*
* This type is also used to encode 4:2:0 formats.
*
* Note that in cases 2, and 3 each pair of U and V is shared among four pixels,
* grouped together as they are groupped in the framebeffer: divide the frame's
* rectangle into 2x2 pixels squares, starting from 0,0 corner - and each square
* represents the group of pixels that share same pair of UV values. So, each
* line in the U/V panes table is shared between two adjacent lines in Y pane,
* which provides a pattern on how to move between U/V lines as we move between
* Y lines.
*
* So, all these patterns can be coded in a YUV format descriptor, so there can
* just one generic way of walking YUV frame.
*
* BAYER format.
* We don't use BAYER inside the guest system, so there is no need to have a
* converter to the BAYER formats, only from it. The color approximation used in
* the BAYER format converters implemented here simply averages corresponded
* color values found in pixels sorrounding the one for which RGB colors are
* calculated.
*
* Performance considerations:
* Since converters implemented here are intended to work as part of the camera
* emulation, making the code super performant is not a priority at all. There
* will be enough loses in other parts of the emultion to overlook any slight
* inefficiences in the conversion algorithm as neglectable.
*/
typedef struct RGBDesc RGBDesc;
typedef struct YUVDesc YUVDesc;
typedef struct BayerDesc BayerDesc;
/* Prototype for a routine that loads RGB colors from an RGB/BRG stream.
* Param:
* rgb - Pointer to a pixel inside the stream where to load colors from.
* r, g, b - Upon return will contain red, green, and blue colors for the pixel
* addressed by 'rgb' pointer.
* Return:
* Pointer to the next pixel in the stream.
*/
typedef const void* (*load_rgb_func)(const void* rgb,
uint8_t* r,
uint8_t* g,
uint8_t* b);
/* Prototype for a routine that saves RGB colors to an RGB/BRG stream.
* Param:
* rgb - Pointer to a pixel inside the stream where to save colors.
* r, g, b - Red, green, and blue colors to save to the pixel addressed by
* 'rgb' pointer.
* Return:
* Pointer to the next pixel in the stream.
*/
typedef void* (*save_rgb_func)(void* rgb, uint8_t r, uint8_t g, uint8_t b);
/* Prototype for a routine that calculates an offset of the first U value for the
* given line in a YUV framebuffer.
* Param:
* desc - Descriptor for the YUV frame for which the offset is being calculated.
* line - Zero-based line number for which to calculate the offset.
* width, height - Frame dimensions.
* Return:
* Offset of the first U value for the given frame line. The offset returned
* here is relative to the beginning of the YUV framebuffer.
*/
typedef int (*u_offset_func)(const YUVDesc* desc, int line, int width, int height);
/* Prototype for a routine that calculates an offset of the first V value for the
* given line in a YUV framebuffer.
* Param:
* desc - Descriptor for the YUV frame for which the offset is being calculated.
* line - Zero-based line number for which to calculate the offset.
* width, height - Frame dimensions.
* Return:
* Offset of the first V value for the given frame line. The offset returned
* here is relative to the beginning of the YUV framebuffer.
*/
typedef int (*v_offset_func)(const YUVDesc* desc, int line, int width, int height);
/* RGB/BRG format descriptor. */
struct RGBDesc {
/* Routine that loads RGB colors from a buffer. */
load_rgb_func load_rgb;
/* Routine that saves RGB colors into a buffer. */
save_rgb_func save_rgb;
/* Byte size of an encoded RGB pixel. */
int rgb_inc;
};
/* YUV format descriptor. */
struct YUVDesc {
/* Offset of the first Y value in a fully interleaved YUV framebuffer. */
int Y_offset;
/* Distance between two Y values inside a pair of pixels in a fully
* interleaved YUV framebuffer. */
int Y_inc;
/* Distance between first Y values of the adjacent pixel pairs in a fully
* interleaved YUV framebuffer. */
int Y_next_pair;
/* Increment between adjacent U/V values in a YUV framebuffer. */
int UV_inc;
/* Controls location of the first U value in YUV framebuffer. Depending on
* the actual YUV format can mean three things:
* - For fully interleaved YUV formats contains offset of the first U value
* in each line.
* - For YUV format that use separate, but interleaved UV pane, this field
* contains an offset of the first U value in the UV pane.
* - For YUV format that use fully separated Y, U, and V panes this field
* defines order of U and V panes in the framebuffer:
* = 1 - U pane comes first, right after Y pane.
* = 0 - U pane follows V pane that startes right after Y pane. */
int U_offset;
/* Controls location of the first V value in YUV framebuffer.
* See comments to U_offset for more info. */
int V_offset;
/* Routine that calculates an offset of the first U value for the given line
* in a YUV framebuffer. */
u_offset_func u_offset;
/* Routine that calculates an offset of the first V value for the given line
* in a YUV framebuffer. */
v_offset_func v_offset;
};
/* Bayer format descriptor. */
struct BayerDesc {
/* Defines color ordering in the BAYER framebuffer. Can be one of the four:
* - "GBRG" for GBGBGB / RGRGRG
* - "GRBG" for GRGRGR / BGBGBG
* - "RGGB" for RGRGRG / GBGBGB
* - "BGGR" for BGBGBG / GRGRGR
*/
const char* color_order;
/* Bitmask for valid bits in the pixel:
* - 0xff For a 8-bit BAYER format
* - 0x3ff For a 10-bit BAYER format
* - 0xfff For a 12-bit BAYER format
*/
int mask;
};
/********************************************************************************
* RGB/BRG load / save routines.
*******************************************************************************/
/* Loads R, G, and B colors from a RGB32 framebuffer. */
static const void*
_load_RGB32(const void* rgb, uint8_t* r, uint8_t* g, uint8_t* b)
{
const uint8_t* rgb_ptr = (const uint8_t*)rgb;
*r = rgb_ptr[0]; *g = rgb_ptr[1]; *b = rgb_ptr[2];
return rgb_ptr + 4;
}
/* Saves R, G, and B colors to a RGB32 framebuffer. */
static void*
_save_RGB32(void* rgb, uint8_t r, uint8_t g, uint8_t b)
{
uint8_t* rgb_ptr = (uint8_t*)rgb;
rgb_ptr[0] = r; rgb_ptr[1] = g; rgb_ptr[2] = b;
return rgb_ptr + 4;
}
/* Loads R, G, and B colors from a BRG32 framebuffer. */
static const void*
_load_BRG32(const void* rgb, uint8_t* r, uint8_t* g, uint8_t* b)
{
const uint8_t* rgb_ptr = (const uint8_t*)rgb;
*r = rgb_ptr[2]; *g = rgb_ptr[1]; *b = rgb_ptr[0];
return rgb_ptr + 4;
}
/* Saves R, G, and B colors to a BRG32 framebuffer. */
static void*
_save_BRG32(void* rgb, uint8_t r, uint8_t g, uint8_t b)
{
uint8_t* rgb_ptr = (uint8_t*)rgb;
rgb_ptr[2] = r; rgb_ptr[1] = g; rgb_ptr[0] = b;
return rgb_ptr + 4;
}
/* Loads R, G, and B colors from a RGB24 framebuffer.
* Note that it's the caller's responsibility to ensure proper alignment of the
* returned pointer at the line's break. */
static const void*
_load_RGB24(const void* rgb, uint8_t* r, uint8_t* g, uint8_t* b)
{
const uint8_t* rgb_ptr = (const uint8_t*)rgb;
*r = rgb_ptr[0]; *g = rgb_ptr[1]; *b = rgb_ptr[2];
return rgb_ptr + 3;
}
/* Saves R, G, and B colors to a RGB24 framebuffer.
* Note that it's the caller's responsibility to ensure proper alignment of the
* returned pointer at the line's break. */
static void*
_save_RGB24(void* rgb, uint8_t r, uint8_t g, uint8_t b)
{
uint8_t* rgb_ptr = (uint8_t*)rgb;
rgb_ptr[0] = r; rgb_ptr[1] = g; rgb_ptr[2] = b;
return rgb_ptr + 3;
}
/* Loads R, G, and B colors from a BRG32 framebuffer.
* Note that it's the caller's responsibility to ensure proper alignment of the
* returned pointer at the line's break. */
static const void*
_load_BRG24(const void* rgb, uint8_t* r, uint8_t* g, uint8_t* b)
{
const uint8_t* rgb_ptr = (const uint8_t*)rgb;
*r = rgb_ptr[2]; *g = rgb_ptr[1]; *b = rgb_ptr[0];
return rgb_ptr + 3;
}
/* Saves R, G, and B colors to a BRG24 framebuffer.
* Note that it's the caller's responsibility to ensure proper alignment of the
* returned pointer at the line's break. */
static void*
_save_BRG24(void* rgb, uint8_t r, uint8_t g, uint8_t b)
{
uint8_t* rgb_ptr = (uint8_t*)rgb;
rgb_ptr[2] = r; rgb_ptr[1] = g; rgb_ptr[0] = b;
return rgb_ptr + 3;
}
/* Loads R, G, and B colors from a RGB565 framebuffer. */
static const void*
_load_RGB16(const void* rgb, uint8_t* r, uint8_t* g, uint8_t* b)
{
const uint16_t rgb16 = *(const uint16_t*)rgb;
*r = R16(rgb16); *g = G16(rgb16); *b = B16(rgb16);
return (const uint8_t*)rgb + 2;
}
/* Saves R, G, and B colors to a RGB565 framebuffer. */
static void*
_save_RGB16(void* rgb, uint8_t r, uint8_t g, uint8_t b)
{
*(uint16_t*)rgb = RGB565(r & 0x1f, g & 0x3f, b & 0x1f);
return (uint8_t*)rgb + 2;
}
/* Loads R, G, and B colors from a BRG565 framebuffer. */
static const void*
_load_BRG16(const void* rgb, uint8_t* r, uint8_t* g, uint8_t* b)
{
const uint16_t rgb16 = *(const uint16_t*)rgb;
*r = B16(rgb16); *g = G16(rgb16); *b = R16(rgb16);
return (const uint8_t*)rgb + 2;
}
/* Saves R, G, and B colors to a BRG565 framebuffer. */
static void*
_save_BRG16(void* rgb, uint8_t r, uint8_t g, uint8_t b)
{
*(uint16_t*)rgb = RGB565(b & 0x1f, g & 0x3f, r & 0x1f);
return (uint8_t*)rgb + 2;
}
/********************************************************************************
* YUV's U/V offset calculation routines.
*******************************************************************************/
/* U offset in a fully interleaved YUV 4:2:2 */
static int
_UOffIntrlYUV(const YUVDesc* desc, int line, int width, int height)
{
/* In interleaved YUV 4:2:2 each pair of pixels is encoded with 4 consecutive
* bytes (or 2 bytes per pixel). So line size in a fully interleaved YUV 4:2:2
* is twice its width. */
return line * width * 2 + desc->U_offset;
}
/* V offset in a fully interleaved YUV 4:2:2 */
static int
_VOffIntrlYUV(const YUVDesc* desc, int line, int width, int height)
{
/* See _UOffIntrlYUV comments. */
return line * width * 2 + desc->V_offset;
}
/* U offset in an interleaved UV pane of YUV 4:2:0 */
static int
_UOffIntrlUV(const YUVDesc* desc, int line, int width, int height)
{
/* UV pane starts right after the Y pane, that occupies 'height * width'
* bytes. Eacht line in UV pane contains width / 2 'UV' pairs, which makes UV
* lane to contain as many bytes, as the width is.
* Each line in the UV pane is shared between two Y lines. So, final formula
* for the beggining of the UV pane's line for the given line in YUV
* framebuffer is:
*
* height * width + (line / 2) * width = (height + line / 2) * width
*/
return (height + line / 2) * width + desc->U_offset;
}
/* V offset in an interleaved UV pane of YUV 4:2:0 */
static int
_VOffIntrlUV(const YUVDesc* desc, int line, int width, int height)
{
/* See comments in _UOffIntrlUV. */
return (height + line / 2) * width + desc->V_offset;
}
/* U offset in a 3-pane YUV 4:2:0 */
static int
_UOffSepYUV(const YUVDesc* desc, int line, int width, int height)
{
/* U, or V pane starts right after the Y pane, that occupies 'height * width'
* bytes. Eacht line in each of U and V panes contains width / 2 elements.
* Also, each line in each of U and V panes is shared between two Y lines.
* So, final formula for the beggining of a line in the U/V pane is:
*
* <Y pane size> + (line / 2) * width / 2
*
* for the pane that follows right after the Y pane, or
*
* <Y pane size> + <Y pane size> / 4 + (line / 2) * width / 2
*
* for the second pane.
*/
const int y_pane_size = height * width;
if (desc->U_offset) {
/* U pane comes right after the Y pane. */
return y_pane_size + (line / 2) * width / 2;
} else {
/* U pane follows V pane. */
return y_pane_size + y_pane_size / 4 + (line / 2) * width / 2;
}
}
/* V offset in a 3-pane YUV 4:2:0 */
static int
_VOffSepYUV(const YUVDesc* desc, int line, int width, int height)
{
/* See comment for _UOffSepYUV. */
const int y_pane_size = height * width;
if (desc->V_offset) {
/* V pane comes right after the Y pane. */
return y_pane_size + (line / 2) * width / 2;
} else {
/* V pane follows U pane. */
return y_pane_size + y_pane_size / 4 + (line / 2) * width / 2;
}
}
/********************************************************************************
* Bayer routines.
*******************************************************************************/
/* Gets a color value for the given pixel in a bayer framebuffer.
* Param:
* desc - Bayer framebuffer descriptor.
* buf - Beginning of the framebuffer.
* x, y - Coordinates of the pixel inside the framebuffer to get the color for.
* width - Number of pixel in a line inside the framebuffer.
* Return:
* Given pixel color.
*/
static __inline__ int
_get_bayer_color(const BayerDesc* desc, const void* buf, int x, int y, int width)
{
if (desc->mask == kBayer8) {
/* Each pixel is represented with one byte. */
return *((const uint8_t*)buf + y * width + x);
} else {
#ifndef HOST_WORDS_BIGENDIAN
return *((const int16_t*)buf + y * width + x) & desc->mask;
#else
const uint8_t* pixel = (const uint8_t*)buf + (y * width + x) * 2;
return (((uint16_t)pixel[1] << 8) | pixel[0]) & desc->mask;
#endif /* !HOST_WORDS_BIGENDIAN */
}
}
/* Gets an average value of colors that are horisontally adjacent to a pixel in
* a bayer framebuffer.
* Param:
* desc - Bayer framebuffer descriptor.
* buf - Beginning of the framebuffer.
* x, y - Coordinates of the pixel inside the framebuffer that is the center for
* the calculation.
* width, height - Framebuffer dimensions.
* Return:
* Average color for horisontally adjacent pixels.
*/
static int
_get_bayer_ave_hor(const BayerDesc* desc,
const void* buf,
int x,
int y,
int width,
int height)
{
if (x == 0) {
return _get_bayer_color(desc, buf, x + 1, y, width);
} else if (x == (width - 1)) {
return _get_bayer_color(desc, buf, x - 1, y, width);
} else {
return (_get_bayer_color(desc, buf, x - 1, y, width) +
_get_bayer_color(desc, buf, x + 1, y, width)) / 2;
}
}
/* Gets an average value of colors that are vertically adjacent to a pixel in
* a bayer framebuffer.
* Param:
* desc - Bayer framebuffer descriptor.
* buf - Beginning of the framebuffer.
* x, y - Coordinates of the pixel inside the framebuffer that is the center for
* the calculation.
* width, height - Framebuffer dimensions.
* Return:
* Average color for vertically adjacent pixels.
*/
static int
_get_bayer_ave_vert(const BayerDesc* desc,
const void* buf,
int x,
int y,
int width,
int height)
{
if (y == 0) {
return _get_bayer_color(desc, buf, x, y + 1, width);
} else if (y == (height - 1)) {
return _get_bayer_color(desc, buf, x, y - 1, width);
} else {
return (_get_bayer_color(desc, buf, x, y - 1, width) +
_get_bayer_color(desc, buf, x, y + 1, width)) / 2;
}
}
/* Gets an average value of colors that are horisontally and vertically adjacent
* to a pixel in a bayer framebuffer.
* Param:
* desc - Bayer framebuffer descriptor.
* buf - Beginning of the framebuffer.
* x, y - Coordinates of the pixel inside the framebuffer that is the center for
* the calculation.
* width, height - Framebuffer dimensions.
* Return:
* Average color for horisontally and vertically adjacent pixels.
*/
static int
_get_bayer_ave_cross(const BayerDesc* desc,
const void* buf,
int x,
int y,
int width,
int height)
{
if (x > 0 && x < (width - 1) && y > 0 && y < (height - 1)) {
/* Most of the time the code will take this path. So it makes sence to
* special case it for performance reasons. */
return (_get_bayer_color(desc, buf, x - 1, y, width) +
_get_bayer_color(desc, buf, x + 1, y, width) +
_get_bayer_color(desc, buf, x, y - 1, width) +
_get_bayer_color(desc, buf, x, y + 1, width)) / 4;
} else {
int sum = 0;
int num = 0;
/* Horisontal sum */
if (x == 0) {
sum += _get_bayer_color(desc, buf, x + 1, y, width);
num++;
} else if (x == (width - 1)) {
sum += _get_bayer_color(desc, buf, x - 1, y, width);
num++;
} else {
sum += _get_bayer_color(desc, buf, x - 1, y, width) +
_get_bayer_color(desc, buf, x + 1, y, width);
num += 2;
}
/* Vertical sum */
if (y == 0) {
sum += _get_bayer_color(desc, buf, x, y + 1, width);
num++;
} else if (y == (height - 1)) {
sum += _get_bayer_color(desc, buf, x, y - 1, width);
num++;
} else {
sum += _get_bayer_color(desc, buf, x, y - 1, width) +
_get_bayer_color(desc, buf, x, y + 1, width);
num += 2;
}
return sum / num;
}
}
/* Gets an average value of colors that are diagonally adjacent to a pixel in a
* bayer framebuffer.
* Param:
* desc - Bayer framebuffer descriptor.
* buf - Beginning of the framebuffer.
* x, y - Coordinates of the pixel inside the framebuffer that is the center for
* the calculation.
* width, height - Framebuffer dimensions.
* Return:
* Average color for diagonally adjacent pixels.
*/
static int
_get_bayer_ave_diag(const BayerDesc* desc,
const void* buf,
int x,
int y,
int width,
int height)
{
if (x > 0 && x < (width - 1) && y > 0 && y < (height - 1)) {
/* Most of the time the code will take this path. So it makes sence to
* special case it for performance reasons. */
return (_get_bayer_color(desc, buf, x - 1, y - 1, width) +
_get_bayer_color(desc, buf, x + 1, y - 1, width) +
_get_bayer_color(desc, buf, x - 1, y + 1, width) +
_get_bayer_color(desc, buf, x + 1, y + 1, width)) / 4;
} else {
int sum = 0;
int num = 0;
int xx, yy;
for (xx = x - 1; xx < (x + 2); xx += 2) {
for (yy = y - 1; yy < (y + 2); yy += 2) {
if (xx >= 0 && yy >= 0 && xx < width && yy < height) {
sum += _get_bayer_color(desc, buf, xx, yy, width);
num++;
}
}
}
return sum / num;
}
}
/* Gets pixel color selector for the given pixel in a bayer framebuffer.
* Param:
* desc - Bayer framebuffer descriptor.
* x, y - Coordinates of the pixel inside the framebuffer to get the color
* selector for.
* Return:
* Pixel color selector:
* - 'R' - pixel is red.
* - 'G' - pixel is green.
* - 'B' - pixel is blue.
*/
static __inline__ char
_get_bayer_color_sel(const BayerDesc* desc, int x, int y)
{
return desc->color_order[((y & 1) << 1) | (x & 1)];
}
/* Calculates RGB colors for a pixel in a bayer framebuffer.
* Param:
* desc - Bayer framebuffer descriptor.
* buf - Beginning of the framebuffer.
* x, y - Coordinates of the pixel inside the framebuffer to get the colors for.
* width, height - Framebuffer dimensions.
* red, green bluu - Upon return will contain RGB colors calculated for the pixel.
*/
static void
_get_bayerRGB(const BayerDesc* desc,
const void* buf,
int x,
int y,
int width,
int height,
int* red,
int* green,
int* blue)
{
const char pixel_color = _get_bayer_color_sel(desc, x, y);
if (pixel_color == 'G') {
/* This is a green pixel. */
const char next_pixel_color = _get_bayer_color_sel(desc, x + 1, y);
*green = _get_bayer_color(desc, buf, x, y, width);
if (next_pixel_color == 'R') {
*red = _get_bayer_ave_hor(desc, buf, x, y, width, height);
*blue = _get_bayer_ave_vert(desc, buf, x, y, width, height);
} else {
*red = _get_bayer_ave_vert(desc, buf, x, y, width, height);
*blue = _get_bayer_ave_hor(desc, buf, x, y, width, height);
}
} else if (pixel_color == 'R') {
/* This is a red pixel. */
*red = _get_bayer_color(desc, buf, x, y, width);
*green = _get_bayer_ave_cross(desc, buf, x, y, width, height);
*blue = _get_bayer_ave_diag(desc, buf, x, y, width, height);
} else {
/* This is a blue pixel. */
*blue = _get_bayer_color(desc, buf, x, y, width);
*green = _get_bayer_ave_cross(desc, buf, x, y, width, height);
*red = _get_bayer_ave_diag(desc, buf, x, y, width, height);
}
}
/********************************************************************************
* Generic YUV/RGB/BAYER converters
*******************************************************************************/
/* Generic converter from an RGB/BRG format to a YUV format. */
static void
RGBToYUV(const RGBDesc* rgb_fmt,
const YUVDesc* yuv_fmt,
const void* rgb,
void* yuv,
int width,
int height,
float r_scale,
float g_scale,
float b_scale,
float exp_comp)
{
int y, x;
const int Y_Inc = yuv_fmt->Y_inc;
const int UV_inc = yuv_fmt->UV_inc;
const int Y_next_pair = yuv_fmt->Y_next_pair;
uint8_t* pY = (uint8_t*)yuv + yuv_fmt->Y_offset;
for (y = 0; y < height; y++) {
uint8_t* pU =
(uint8_t*)yuv + yuv_fmt->u_offset(yuv_fmt, y, width, height);
uint8_t* pV =
(uint8_t*)yuv + yuv_fmt->v_offset(yuv_fmt, y, width, height);
for (x = 0; x < width; x += 2,
pY += Y_next_pair, pU += UV_inc, pV += UV_inc) {
uint8_t r, g, b;
rgb = rgb_fmt->load_rgb(rgb, &r, &g, &b);
_change_white_balance_RGB_b(&r, &g, &b, r_scale, g_scale, b_scale);
_change_exposure_RGB(&r, &g, &b, exp_comp);
R8G8B8ToYUV(r, g, b, pY, pU, pV);
rgb = rgb_fmt->load_rgb(rgb, &r, &g, &b);
_change_white_balance_RGB_b(&r, &g, &b, r_scale, g_scale, b_scale);
_change_exposure_RGB(&r, &g, &b, exp_comp);
pY[Y_Inc] = RGB2Y((int)r, (int)g, (int)b);
}
/* Aling rgb_ptr to 16 bit */
if (((uintptr_t)rgb & 1) != 0) rgb = (const uint8_t*)rgb + 1;
}
}
/* Generic converter from one RGB/BRG format to another RGB/BRG format. */
static void
RGBToRGB(const RGBDesc* src_rgb_fmt,
const RGBDesc* dst_rgb_fmt,
const void* src_rgb,
void* dst_rgb,
int width,
int height,
float r_scale,
float g_scale,
float b_scale,
float exp_comp)
{
int x, y;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
uint8_t r, g, b;
src_rgb = src_rgb_fmt->load_rgb(src_rgb, &r, &g, &b);
_change_white_balance_RGB_b(&r, &g, &b, r_scale, g_scale, b_scale);
_change_exposure_RGB(&r, &g, &b, exp_comp);
dst_rgb = dst_rgb_fmt->save_rgb(dst_rgb, r, g, b);
}
/* Aling rgb pinters to 16 bit */
if (((uintptr_t)src_rgb & 1) != 0) src_rgb = (uint8_t*)src_rgb + 1;
if (((uintptr_t)dst_rgb & 1) != 0) dst_rgb = (uint8_t*)dst_rgb + 1;
}
}
/* Generic converter from a YUV format to an RGB/BRG format. */
static void
YUVToRGB(const YUVDesc* yuv_fmt,
const RGBDesc* rgb_fmt,
const void* yuv,
void* rgb,
int width,
int height,
float r_scale,
float g_scale,
float b_scale,
float exp_comp)
{
int y, x;
const int Y_Inc = yuv_fmt->Y_inc;
const int UV_inc = yuv_fmt->UV_inc;
const int Y_next_pair = yuv_fmt->Y_next_pair;
const uint8_t* pY = (const uint8_t*)yuv + yuv_fmt->Y_offset;
for (y = 0; y < height; y++) {
const uint8_t* pU =
(const uint8_t*)yuv + yuv_fmt->u_offset(yuv_fmt, y, width, height);
const uint8_t* pV =
(const uint8_t*)yuv + yuv_fmt->v_offset(yuv_fmt, y, width, height);
for (x = 0; x < width; x += 2,
pY += Y_next_pair, pU += UV_inc, pV += UV_inc) {
uint8_t r, g, b;
const uint8_t U = *pU;
const uint8_t V = *pV;
YUVToRGBPix(*pY, U, V, &r, &g, &b);
_change_white_balance_RGB_b(&r, &g, &b, r_scale, g_scale, b_scale);
_change_exposure_RGB(&r, &g, &b, exp_comp);
rgb = rgb_fmt->save_rgb(rgb, r, g, b);
YUVToRGBPix(pY[Y_Inc], U, V, &r, &g, &b);
_change_white_balance_RGB_b(&r, &g, &b, r_scale, g_scale, b_scale);
_change_exposure_RGB(&r, &g, &b, exp_comp);
rgb = rgb_fmt->save_rgb(rgb, r, g, b);
}
/* Aling rgb_ptr to 16 bit */
if (((uintptr_t)rgb & 1) != 0) rgb = (uint8_t*)rgb + 1;
}
}
/* Generic converter from one YUV format to another YUV format. */
static void
YUVToYUV(const YUVDesc* src_fmt,
const YUVDesc* dst_fmt,
const void* src,
void* dst,
int width,
int height,
float r_scale,
float g_scale,
float b_scale,
float exp_comp)
{
int y, x;
const int Y_Inc_src = src_fmt->Y_inc;
const int UV_inc_src = src_fmt->UV_inc;
const int Y_next_pair_src = src_fmt->Y_next_pair;
const int Y_Inc_dst = dst_fmt->Y_inc;
const int UV_inc_dst = dst_fmt->UV_inc;
const int Y_next_pair_dst = dst_fmt->Y_next_pair;
const uint8_t* pYsrc = (const uint8_t*)src + src_fmt->Y_offset;
uint8_t* pYdst = (uint8_t*)dst + dst_fmt->Y_offset;
for (y = 0; y < height; y++) {
const uint8_t* pUsrc =
(const uint8_t*)src + src_fmt->u_offset(src_fmt, y, width, height);
const uint8_t* pVsrc =
(const uint8_t*)src + src_fmt->v_offset(src_fmt, y, width, height);
uint8_t* pUdst =
(uint8_t*)dst + dst_fmt->u_offset(dst_fmt, y, width, height);
uint8_t* pVdst =
(uint8_t*)dst + dst_fmt->v_offset(dst_fmt, y, width, height);
for (x = 0; x < width; x += 2, pYsrc += Y_next_pair_src,
pUsrc += UV_inc_src,
pVsrc += UV_inc_src,
pYdst += Y_next_pair_dst,
pUdst += UV_inc_dst,
pVdst += UV_inc_dst) {
*pYdst = *pYsrc; *pUdst = *pUsrc; *pVdst = *pVsrc;
_change_white_balance_YUV(pYdst, pUdst, pVdst, r_scale, g_scale, b_scale);
*pYdst = _change_exposure(*pYdst, exp_comp);
pYdst[Y_Inc_dst] = _change_exposure(pYsrc[Y_Inc_src], exp_comp);
}
}
}
/* Generic converter from a BAYER format to an RGB/BRG format. */
static void
BAYERToRGB(const BayerDesc* bayer_fmt,
const RGBDesc* rgb_fmt,
const void* bayer,
void* rgb,
int width,
int height,
float r_scale,
float g_scale,
float b_scale,
float exp_comp)
{
int y, x;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
int r, g, b;
_get_bayerRGB(bayer_fmt, bayer, x, y, width, height, &r, &g, &b);
if (bayer_fmt->mask == kBayer10) {
r >>= 2; g >>= 2; b >>= 2;
} else if (bayer_fmt->mask == kBayer12) {
r >>= 4; g >>= 4; b >>= 4;
}
_change_white_balance_RGB(&r, &g, &b, r_scale, g_scale, b_scale);
_change_exposure_RGB_i(&r, &g, &b, exp_comp);
rgb = rgb_fmt->save_rgb(rgb, r, g, b);
}
/* Aling rgb_ptr to 16 bit */
if (((uintptr_t)rgb & 1) != 0) rgb = (uint8_t*)rgb + 1;
}
}
/* Generic converter from a BAYER format to a YUV format. */
static void
BAYERToYUV(const BayerDesc* bayer_fmt,
const YUVDesc* yuv_fmt,
const void* bayer,
void* yuv,
int width,
int height,
float r_scale,
float g_scale,
float b_scale,
float exp_comp)
{
int y, x;
const int Y_Inc = yuv_fmt->Y_inc;
const int UV_inc = yuv_fmt->UV_inc;
const int Y_next_pair = yuv_fmt->Y_next_pair;
uint8_t* pY = (uint8_t*)yuv + yuv_fmt->Y_offset;
for (y = 0; y < height; y++) {
uint8_t* pU =
(uint8_t*)yuv + yuv_fmt->u_offset(yuv_fmt, y, width, height);
uint8_t* pV =
(uint8_t*)yuv + yuv_fmt->v_offset(yuv_fmt, y, width, height);
for (x = 0; x < width; x += 2,
pY += Y_next_pair, pU += UV_inc, pV += UV_inc) {
int r, g, b;
_get_bayerRGB(bayer_fmt, bayer, x, y, width, height, &r, &g, &b);
_change_white_balance_RGB(&r, &g, &b, r_scale, g_scale, b_scale);
_change_exposure_RGB_i(&r, &g, &b, exp_comp);
R8G8B8ToYUV(r, g, b, pY, pU, pV);
_get_bayerRGB(bayer_fmt, bayer, x + 1, y, width, height, &r, &g, &b);
_change_white_balance_RGB(&r, &g, &b, r_scale, g_scale, b_scale);
_change_exposure_RGB_i(&r, &g, &b, exp_comp);
pY[Y_Inc] = RGB2Y(r, g, b);
}
}
}
/********************************************************************************
* RGB format descriptors.
*/
/* Describes RGB32 format. */
static const RGBDesc _RGB32 =
{
.load_rgb = _load_RGB32,
.save_rgb = _save_RGB32,
.rgb_inc = 4
};
/* Describes BRG32 format. */
static const RGBDesc _BRG32 =
{
.load_rgb = _load_BRG32,
.save_rgb = _save_BRG32,
.rgb_inc = 4
};
/* Describes RGB24 format. */
static const RGBDesc _RGB24 =
{
.load_rgb = _load_RGB24,
.save_rgb = _save_RGB24,
.rgb_inc = 3
};
/* Describes BRG24 format. */
static const RGBDesc _BRG24 =
{
.load_rgb = _load_BRG24,
.save_rgb = _save_BRG24,
.rgb_inc = 3
};
/* Describes RGB16 format. */
static const RGBDesc _RGB16 =
{
.load_rgb = _load_RGB16,
.save_rgb = _save_RGB16,
.rgb_inc = 2
};
/* Describes BRG16 format. */
static const RGBDesc _BRG16 =
{
.load_rgb = _load_BRG16,
.save_rgb = _save_BRG16,
.rgb_inc = 2
};
/********************************************************************************
* YUV 4:2:2 format descriptors.
*/
/* YUYV: 4:2:2, YUV are interleaved. */
static const YUVDesc _YUYV =
{
.Y_offset = 0,
.Y_inc = 2,
.Y_next_pair = 4,
.UV_inc = 4,
.U_offset = 1,
.V_offset = 3,
.u_offset = &_UOffIntrlYUV,
.v_offset = &_VOffIntrlYUV
};
/* UYVY: 4:2:2, YUV are interleaved. */
static const YUVDesc _UYVY =
{
.Y_offset = 1,
.Y_inc = 2,
.Y_next_pair = 4,
.UV_inc = 4,
.U_offset = 0,
.V_offset = 2,
.u_offset = &_UOffIntrlYUV,
.v_offset = &_VOffIntrlYUV
};
/* YVYU: 4:2:2, YUV are interleaved. */
static const YUVDesc _YVYU =
{
.Y_offset = 0,
.Y_inc = 2,
.Y_next_pair = 4,
.UV_inc = 4,
.U_offset = 3,
.V_offset = 1,
.u_offset = &_UOffIntrlYUV,
.v_offset = &_VOffIntrlYUV
};
/* VYUY: 4:2:2, YUV are interleaved. */
static const YUVDesc _VYUY =
{
.Y_offset = 1,
.Y_inc = 2,
.Y_next_pair = 4,
.UV_inc = 4,
.U_offset = 2,
.V_offset = 0,
.u_offset = &_UOffIntrlYUV,
.v_offset = &_VOffIntrlYUV
};
/* YYUV (also YUY2, YUNV, V422) : 4:2:2, YUV are interleaved. */
static const YUVDesc _YYUV =
{
.Y_offset = 0,
.Y_inc = 1,
.Y_next_pair = 4,
.UV_inc = 4,
.U_offset = 2,
.V_offset = 3,
.u_offset = &_UOffIntrlYUV,
.v_offset = &_VOffIntrlYUV
};
/* YYVU: 4:2:2, YUV are interleaved. */
static const YUVDesc _YYVU =
{
.Y_offset = 0,
.Y_inc = 1,
.Y_next_pair = 4,
.UV_inc = 4,
.U_offset = 3,
.V_offset = 2,
.u_offset = &_UOffIntrlYUV,
.v_offset = &_VOffIntrlYUV
};
/********************************************************************************
* YUV 4:2:0 descriptors.
*/
/* YV12: 4:2:0, YUV are fully separated, U pane follows V pane */
static const YUVDesc _YV12 =
{
.Y_offset = 0,
.Y_inc = 1,
.Y_next_pair = 2,
.UV_inc = 1,
.U_offset = 0,
.V_offset = 1,
.u_offset = &_UOffSepYUV,
.v_offset = &_VOffSepYUV
};
/* YU12: 4:2:0, YUV are fully separated, V pane follows U pane */
static const YUVDesc _YU12 =
{
.Y_offset = 0,
.Y_inc = 1,
.Y_next_pair = 2,
.UV_inc = 1,
.U_offset = 1,
.V_offset = 0,
.u_offset = &_UOffSepYUV,
.v_offset = &_VOffSepYUV
};
/* NV12: 4:2:0, UV are interleaved, V follows U in UV pane */
static const YUVDesc _NV12 =
{
.Y_offset = 0,
.Y_inc = 1,
.Y_next_pair = 2,
.UV_inc = 2,
.U_offset = 0,
.V_offset = 1,
.u_offset = &_UOffIntrlUV,
.v_offset = &_VOffIntrlUV
};
/* NV21: 4:2:0, UV are interleaved, U follows V in UV pane */
static const YUVDesc _NV21 =
{
.Y_offset = 0,
.Y_inc = 1,
.Y_next_pair = 2,
.UV_inc = 2,
.U_offset = 1,
.V_offset = 0,
.u_offset = &_UOffIntrlUV,
.v_offset = &_VOffIntrlUV
};
/********************************************************************************
* RGB bayer format descriptors.
*/
/* Descriptor for a 8-bit GBGB / RGRG format. */
static const BayerDesc _GB8 =
{
.mask = kBayer8,
.color_order = "GBRG"
};
/* Descriptor for a 8-bit GRGR / BGBG format. */
static const BayerDesc _GR8 =
{
.mask = kBayer8,
.color_order = "GRBG"
};
/* Descriptor for a 8-bit BGBG / GRGR format. */
static const BayerDesc _BG8 =
{
.mask = kBayer8,
.color_order = "BGGR"
};
/* Descriptor for a 8-bit RGRG / GBGB format. */
static const BayerDesc _RG8 =
{
.mask = kBayer8,
.color_order = "RGGB"
};
/* Descriptor for a 10-bit GBGB / RGRG format. */
static const BayerDesc _GB10 =
{
.mask = kBayer10,
.color_order = "GBRG"
};
/* Descriptor for a 10-bit GRGR / BGBG format. */
static const BayerDesc _GR10 =
{
.mask = kBayer10,
.color_order = "GRBG"
};
/* Descriptor for a 10-bit BGBG / GRGR format. */
static const BayerDesc _BG10 =
{
.mask = kBayer10,
.color_order = "BGGR"
};
/* Descriptor for a 10-bit RGRG / GBGB format. */
static const BayerDesc _RG10 =
{
.mask = kBayer10,
.color_order = "RGGB"
};
/* Descriptor for a 12-bit GBGB / RGRG format. */
static const BayerDesc _GB12 =
{
.mask = kBayer12,
.color_order = "GBRG"
};
/* Descriptor for a 12-bit GRGR / BGBG format. */
static const BayerDesc _GR12 =
{
.mask = kBayer12,
.color_order = "GRBG"
};
/* Descriptor for a 12-bit BGBG / GRGR format. */
static const BayerDesc _BG12 =
{
.mask = kBayer12,
.color_order = "BGGR"
};
/* Descriptor for a 12-bit RGRG / GBGB format. */
static const BayerDesc _RG12 =
{
.mask = kBayer12,
.color_order = "RGGB"
};
/********************************************************************************
* List of descriptors for supported formats.
*******************************************************************************/
/* Enumerates pixel formats supported by converters. */
typedef enum PIXFormatSel {
/* Pixel format is RGB/BGR */
PIX_FMT_RGB,
/* Pixel format is YUV */
PIX_FMT_YUV,
/* Pixel format is BAYER */
PIX_FMT_BAYER
} PIXFormatSel;
/* Formats entry in the list of descriptors for supported formats. */
typedef struct PIXFormat {
/* "FOURCC" (V4L2_PIX_FMT_XXX) format type. */
uint32_t fourcc_type;
/* RGB/YUV/BAYER format selector */
PIXFormatSel format_sel;
union {
/* References RGB format descriptor for that format. */
const RGBDesc* rgb_desc;
/* References YUV format descriptor for that format. */
const YUVDesc* yuv_desc;
/* References BAYER format descriptor for that format. */
const BayerDesc* bayer_desc;
} desc;
} PIXFormat;
/* Array of supported pixel format descriptors. */
static const PIXFormat _PIXFormats[] = {
/* RGB/BRG formats. */
{ V4L2_PIX_FMT_RGB32, PIX_FMT_RGB, .desc.rgb_desc = &_RGB32 },
{ V4L2_PIX_FMT_BGR32, PIX_FMT_RGB, .desc.rgb_desc = &_BRG32 },
{ V4L2_PIX_FMT_RGB565, PIX_FMT_RGB, .desc.rgb_desc = &_RGB16 },
{ V4L2_PIX_FMT_RGB24, PIX_FMT_RGB, .desc.rgb_desc = &_RGB24 },
{ V4L2_PIX_FMT_BGR24, PIX_FMT_RGB, .desc.rgb_desc = &_BRG24 },
/* YUV 4:2:0 formats. */
{ V4L2_PIX_FMT_YVU420, PIX_FMT_YUV, .desc.yuv_desc = &_YV12 },
{ V4L2_PIX_FMT_YUV420, PIX_FMT_YUV, .desc.yuv_desc = &_YU12 },
{ V4L2_PIX_FMT_NV12, PIX_FMT_YUV, .desc.yuv_desc = &_NV12 },
{ V4L2_PIX_FMT_NV21, PIX_FMT_YUV, .desc.yuv_desc = &_NV21 },
/* YUV 4:2:2 formats. */
{ V4L2_PIX_FMT_YUYV, PIX_FMT_YUV, .desc.yuv_desc = &_YUYV },
{ V4L2_PIX_FMT_YYUV, PIX_FMT_YUV, .desc.yuv_desc = &_YYUV },
{ V4L2_PIX_FMT_YVYU, PIX_FMT_YUV, .desc.yuv_desc = &_YVYU },
{ V4L2_PIX_FMT_UYVY, PIX_FMT_YUV, .desc.yuv_desc = &_UYVY },
{ V4L2_PIX_FMT_VYUY, PIX_FMT_YUV, .desc.yuv_desc = &_VYUY },
{ V4L2_PIX_FMT_YVYU, PIX_FMT_YUV, .desc.yuv_desc = &_YVYU },
{ V4L2_PIX_FMT_VYUY, PIX_FMT_YUV, .desc.yuv_desc = &_VYUY },
{ V4L2_PIX_FMT_YYVU, PIX_FMT_YUV, .desc.yuv_desc = &_YYVU },
{ V4L2_PIX_FMT_YUY2, PIX_FMT_YUV, .desc.yuv_desc = &_YUYV },
{ V4L2_PIX_FMT_YUNV, PIX_FMT_YUV, .desc.yuv_desc = &_YUYV },
{ V4L2_PIX_FMT_V422, PIX_FMT_YUV, .desc.yuv_desc = &_YUYV },
/* BAYER formats. */
{ V4L2_PIX_FMT_SBGGR8, PIX_FMT_BAYER, .desc.bayer_desc = &_BG8 },
{ V4L2_PIX_FMT_SGBRG8, PIX_FMT_BAYER, .desc.bayer_desc = &_GB8 },
{ V4L2_PIX_FMT_SGRBG8, PIX_FMT_BAYER, .desc.bayer_desc = &_GR8 },
{ V4L2_PIX_FMT_SRGGB8, PIX_FMT_BAYER, .desc.bayer_desc = &_RG8 },
{ V4L2_PIX_FMT_SBGGR10, PIX_FMT_BAYER, .desc.bayer_desc = &_BG10 },
{ V4L2_PIX_FMT_SGBRG10, PIX_FMT_BAYER, .desc.bayer_desc = &_GB10 },
{ V4L2_PIX_FMT_SGRBG10, PIX_FMT_BAYER, .desc.bayer_desc = &_GR10 },
{ V4L2_PIX_FMT_SRGGB10, PIX_FMT_BAYER, .desc.bayer_desc = &_RG10 },
{ V4L2_PIX_FMT_SBGGR12, PIX_FMT_BAYER, .desc.bayer_desc = &_BG12 },
{ V4L2_PIX_FMT_SGBRG12, PIX_FMT_BAYER, .desc.bayer_desc = &_GB12 },
{ V4L2_PIX_FMT_SGRBG12, PIX_FMT_BAYER, .desc.bayer_desc = &_GR12 },
{ V4L2_PIX_FMT_SRGGB12, PIX_FMT_BAYER, .desc.bayer_desc = &_RG12 },
};
static const int _PIXFormats_num = sizeof(_PIXFormats) / sizeof(*_PIXFormats);
/* Get an entry in the array of supported pixel format descriptors.
* Param:
* pixel_format - "fourcc" pixel format to lookup an entry for.
* Return"
* Pointer to the found entry, or NULL if no entry exists for the given pixel
* format.
*/
static const PIXFormat*
_get_pixel_format_descriptor(uint32_t pixel_format)
{
int f;
for (f = 0; f < _PIXFormats_num; f++) {
if (_PIXFormats[f].fourcc_type == pixel_format) {
return &_PIXFormats[f];
}
}
W("%s: Pixel format %.4s is unknown",
__FUNCTION__, (const char*)&pixel_format);
return NULL;
}
/********************************************************************************
* Public API
*******************************************************************************/
int
has_converter(uint32_t from, uint32_t to)
{
if (from == to) {
/* Same format: converter esists. */
return 1;
}
return _get_pixel_format_descriptor(from) != NULL &&
_get_pixel_format_descriptor(to) != NULL;
}
int
convert_frame(const void* frame,
uint32_t pixel_format,
size_t framebuffer_size,
int width,
int height,
ClientFrameBuffer* framebuffers,
int fbs_num,
float r_scale,
float g_scale,
float b_scale,
float exp_comp)
{
int n;
const PIXFormat* src_desc = _get_pixel_format_descriptor(pixel_format);
if (src_desc == NULL) {
E("%s: Source pixel format %.4s is unknown",
__FUNCTION__, (const char*)&pixel_format);
return -1;
}
for (n = 0; n < fbs_num; n++) {
/* Note that we need to apply white balance, exposure compensation, etc.
* when we transfer the captured frame to the user framebuffer. So, even
* if source and destination formats are the same, we will have to go
* thrugh the converters to apply these things. */
const PIXFormat* dst_desc =
_get_pixel_format_descriptor(framebuffers[n].pixel_format);
if (dst_desc == NULL) {
E("%s: Destination pixel format %.4s is unknown",
__FUNCTION__, (const char*)&framebuffers[n].pixel_format);
return -1;
}
switch (src_desc->format_sel) {
case PIX_FMT_RGB:
if (dst_desc->format_sel == PIX_FMT_RGB) {
RGBToRGB(src_desc->desc.rgb_desc, dst_desc->desc.rgb_desc,
frame, framebuffers[n].framebuffer, width, height,
r_scale, g_scale, b_scale, exp_comp);
} else if (dst_desc->format_sel == PIX_FMT_YUV) {
RGBToYUV(src_desc->desc.rgb_desc, dst_desc->desc.yuv_desc,
frame, framebuffers[n].framebuffer, width, height,
r_scale, g_scale, b_scale, exp_comp);
} else {
E("%s: Unexpected destination pixel format %d",
__FUNCTION__, dst_desc->format_sel);
return -1;
}
break;
case PIX_FMT_YUV:
if (dst_desc->format_sel == PIX_FMT_RGB) {
YUVToRGB(src_desc->desc.yuv_desc, dst_desc->desc.rgb_desc,
frame, framebuffers[n].framebuffer, width, height,
r_scale, g_scale, b_scale, exp_comp);
} else if (dst_desc->format_sel == PIX_FMT_YUV) {
YUVToYUV(src_desc->desc.yuv_desc, dst_desc->desc.yuv_desc,
frame, framebuffers[n].framebuffer, width, height,
r_scale, g_scale, b_scale, exp_comp);
} else {
E("%s: Unexpected destination pixel format %d",
__FUNCTION__, dst_desc->format_sel);
return -1;
}
break;
case PIX_FMT_BAYER:
if (dst_desc->format_sel == PIX_FMT_RGB) {
BAYERToRGB(src_desc->desc.bayer_desc, dst_desc->desc.rgb_desc,
frame, framebuffers[n].framebuffer, width, height,
r_scale, g_scale, b_scale, exp_comp);
} else if (dst_desc->format_sel == PIX_FMT_YUV) {
BAYERToYUV(src_desc->desc.bayer_desc, dst_desc->desc.yuv_desc,
frame, framebuffers[n].framebuffer, width, height,
r_scale, g_scale, b_scale, exp_comp);
} else {
E("%s: Unexpected destination pixel format %d",
__FUNCTION__, dst_desc->format_sel);
return -1;
}
break;
default:
E("%s: Unexpected source pixel format %d",
__FUNCTION__, dst_desc->format_sel);
return -1;
}
}
return 0;
}