openmv/lib/imlib/bayer.c
Kwabena W. Agyeman 0307402a57 ports/alif: Add gamma correction to PAG7936 video.
Gamma correction massively improves the PAG7936 image quality
by increasing the image brightness. Brightness and contrast adjustment
support is also exposed for future automatic control.
2025-11-02 22:35:57 -08:00

2472 lines
108 KiB
C

/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2013-2024 OpenMV, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Debayering Functions
*/
#include "imlib.h"
#include "simd.h"
#define VBAYER_Y_STRIDE (2)
#define VBAYER_X_STRIDE ((UINT8_VECTOR_SIZE) / 2)
#define VBAYER_X_STRIDE_2X2 ((VBAYER_X_STRIDE) * 4)
#define VBAYER_BUF_KSIZE ((VBAYER_Y_STRIDE) * 2)
#define VBAYER_BUF_BROWS ((VBAYER_Y_STRIDE) * 4)
pixformat_t imlib_bayer_shift(pixformat_t pixfmt, int x, int y, bool transpose) {
bool shift_right = x % 2;
bool shift_down = y % 2;
switch (pixfmt) {
case PIXFORMAT_BAYER_BGGR: {
if (shift_right && shift_down) {
return PIXFORMAT_BAYER_RGGB;
} else if (shift_right) {
return transpose ? PIXFORMAT_BAYER_GRBG : PIXFORMAT_BAYER_GBRG;
} else if (shift_down) {
return transpose ? PIXFORMAT_BAYER_GBRG : PIXFORMAT_BAYER_GRBG;
} else {
return pixfmt;
}
}
case PIXFORMAT_BAYER_GBRG: {
if (shift_right && shift_down) {
return transpose ? PIXFORMAT_BAYER_GBRG : PIXFORMAT_BAYER_GRBG;
} else if (shift_right) {
return PIXFORMAT_BAYER_BGGR;
} else if (shift_down) {
return PIXFORMAT_BAYER_RGGB;
} else {
return pixfmt;
}
}
case PIXFORMAT_BAYER_GRBG: {
if (shift_right && shift_down) {
return transpose ? PIXFORMAT_BAYER_GRBG : PIXFORMAT_BAYER_GBRG;
} else if (shift_right) {
return PIXFORMAT_BAYER_RGGB;
} else if (shift_down) {
return PIXFORMAT_BAYER_BGGR;
} else {
return pixfmt;
}
}
case PIXFORMAT_BAYER_RGGB: {
if (shift_right && shift_down) {
return PIXFORMAT_BAYER_BGGR;
} else if (shift_right) {
return transpose ? PIXFORMAT_BAYER_GBRG : PIXFORMAT_BAYER_GRBG;
} else if (shift_down) {
return transpose ? PIXFORMAT_BAYER_GRBG : PIXFORMAT_BAYER_GBRG;
} else {
return pixfmt;
}
}
default: {
__builtin_unreachable();
}
}
}
// Row vectors are loaded into memory and processed in little-endian order.
// row_0 stores MSB [G1, R1, G0, R0] LSB pixels where each pixel is 8-bits.
// row_1 stores MSB [B1, G3, B0, G2] LSB pixels where each pixel is 8-bits.
// row_2 stores MSB [G5, R3, G4, R2] LSB pixels where each pixel is 8-bits.
// In the case of vectors larger than 32-bits the pattern is repeated for every 32-bits.
//
// vdebayer_bggr produces two output pixels per 32-bits:
// pixels.r = MSB [0, R@G3, 0, R@B0] LSB pixels where each pixel is 8-bits.
// pixels.g = MSB [0, G@G3, 0, G@B0] LSB pixels where each pixel is 8-bits.
// pixels.b = MSB [0, B@G3, 0, B@B0] LSB pixels where each pixel is 8-bits.
static inline vrgb_pixels_t vdebayer_bggr(v128_t row_0, v128_t row_1, v128_t row_2) {
vrgb_pixels_t pixels;
v128_t row_02 = vhadd_u8(row_0, row_2);
// row_02 = [(G1+G5)/2, (R1+R3)/2, (G0+G4)/2, (R0+R2)/2]
v128_t row_11 = vhadd_u8(row_1, vpkhtb(row_1, row_1));
// row_11 = [(B1+B1)/2, (G3+G3)/2, (B0+B1)/2, (G2+G3)/2]
// row_11 = [B1, G3, (B0+B1)/2, (G2+G3)/2]
v128_t t_r_pixels = vhadd_u8(row_02, vpkhtb(row_02, row_02));
// t_r_pixels = [(G1+G5+G1+G5)/4, (R1+R3+R1+R3)/4, (G1+G5+G0+G4)/4, (R1+R3+R0+R2)/4]
// t_r_pixels = [(G1+G5)/2, R@G3, G@G3, R@B0]
pixels.r = vuxtb16(t_r_pixels);
// pixels.r = [0, R@G3, 0, R@B0]
v128_t t_g_pixels = vhadd_u8(row_11, vpkhtb_ror8(row_11, row_02));
// t_g_pixels = [(B1+B1)/2, (G3+G3)/2, (B0+B1+R1+R3)/4, (G2+G3+G0+G4)/4]
// t_g_pixels = [B@B1, G@G3, (B0+B1+R1+R3)/4, G@B0]
pixels.g = vuxtb16(t_g_pixels);
// pixels.g = [0, G@G3, 0, G@B0]
v128_t t_b_pixels = vhadd_u8(row_1, vpkhbt(row_1, row_1));
// t_b_pixels = [(B1+B0)/2, (G3+G2)/2, (B0+B0)/2, (G2+G2)/2]
// t_b_pixels = [B@G3, (G3+G2)/2, B@B0, G@G2]
pixels.b = vuxtb16_ror8(t_b_pixels);
// pixels.b = [0, B@G3, 0, B@B0]
return pixels;
}
// Row vectors are loaded into memory and processed in little-endian order.
// row_0 stores MSB [R1, G1, R0, G0] LSB pixels where each pixel is 8-bits.
// row_1 stores MSB [G3, B1, G2, B0] LSB pixels where each pixel is 8-bits.
// row_2 stores MSB [R3, G5, R2, G4] LSB pixels where each pixel is 8-bits.
// In the case of vectors larger than 32-bits the pattern is repeated for every 32-bits.
//
// vdebayer_gbrg produces two output pixels per 32-bits:
// pixels.r = MSB [0, R@B1, 0, R@G2] LSB pixels where each pixel is 8-bits.
// pixels.g = MSB [0, G@B1, 0, G@G2] LSB pixels where each pixel is 8-bits.
// pixels.b = MSB [0, B@B1, 0, B@G2] LSB pixels where each pixel is 8-bits.
static inline vrgb_pixels_t vdebayer_gbrg(v128_t row_0, v128_t row_1, v128_t row_2) {
vrgb_pixels_t pixels;
v128_t row_02 = vhadd_u8(row_0, row_2);
// row_02 = [(R1+R3)/2, (G1+G5)/2, (R0+R2)/2, (G0+G4)/2]
v128_t row_11 = vhadd_u8(row_1, vpkhbt(row_1, row_1));
// row_11 = [(G3+G2)/2, (B1+B0)/2, (G2+G2)/2, (B0+B0)/2]
// row_11 = [(G3+G2)/2, B@G2, G@G2, B@B0]
v128_t t_r_pixels = vhadd_u8(row_02, vpkhbt(row_02, row_02));
// t_r_pixels = [(R1+R3+R0+R2)/4, (G1+G5+G0+G4)/4, (R0+R2+R0+R2)/4, (G0+G4+G0+G4)/4]
// t_r_pixels = [R@B1, (G1+G5+G0+G4)/4, R@G2, (G0+G4)/2]
pixels.r = vuxtb16_ror8(t_r_pixels);
// pixels.r = [0, R@B1, 0, R@G2]
v128_t t_g_pixels = vhadd_u8(row_11, vpkhbt_ror8(row_11, row_02));
// t_g_pixels = [(G3+G2+G1+G5)/4, (B1+B0+R0+R2)/4, (G2+G2+G2+G2)/4, (B0+B0+B0+B0)/2]
// t_g_pixels = [G@B1, (B1+B0+R0+R2)/4, G@G2, B@B0]
pixels.g = vuxtb16_ror8(t_g_pixels);
// pixels.g = [0, G@B1, 0, G@G2]
v128_t t_b_pixels = vhadd_u8(row_1, vpkhtb(row_1, row_1));
// t_b_pixels = [(G3+G3)/2, (B1+B1)/2, (G2+G3)/2, (B0+B1)/2]
// t_b_pixels = [(G3+G3)/2, B@B1, (G2+G3)/2, B@G2]
pixels.b = vuxtb16(t_b_pixels);
// pixels.b = [0, B@B1, 0, B@G2]
return pixels;
}
// Row vectors are loaded into memory and processed in little-endian order.
// row_0 stores MSB [B1, G1, B0, G0] LSB pixels where each pixel is 8-bits.
// row_1 stores MSB [G3, R3, G2, R0] LSB pixels where each pixel is 8-bits.
// row_2 stores MSB [B3, G5, B2, G4] LSB pixels where each pixel is 8-bits.
// In the case of vectors larger than 32-bits the pattern is repeated for every 32-bits.
//
// vdebayer_grbg produces two output pixels per 32-bits:
// pixels.r = MSB [0, R@R3, 0, R@G2] LSB pixels where each pixel is 8-bits.
// pixels.g = MSB [0, G@R3, 0, G@G2] LSB pixels where each pixel is 8-bits.
// pixels.b = MSB [0, B@R3, 0, B@G2] LSB pixels where each pixel is 8-bits.
static inline vrgb_pixels_t vdebayer_grbg(v128_t row_0, v128_t row_1, v128_t row_2) {
vrgb_pixels_t pixels;
v128_t row_02 = vhadd_u8(row_0, row_2);
// row_02 = [(B1+B3)/2, (G1+G5)/2, (B0+B2)/2, (G0+G4)/2]
v128_t row_11 = vhadd_u8(row_1, vpkhbt(row_1, row_1));
// row_11 = [(G3+G2)/2, (R3+R0)/2, (G2+G2)/2, (R0+R0)/2]
// row_11 = [(G3+G2)/2, R@G2, G@G2, R@R0]
v128_t t_r_pixels = vhadd_u8(row_1, vpkhtb(row_1, row_1));
// t_r_pixels = [(G3+G3)/2, (R3+R3)/2, (G2+G3)/2, (R0+R3)/2]
// t_r_pixels = [(G3+G3)/2, R@R3, (G2+G3)/2, R@G2]
pixels.r = vuxtb16(t_r_pixels);
// pixels.r = [0, R@R3, 0, R@G2]
v128_t t_g_pixels = vhadd_u8(row_11, vpkhbt_ror8(row_11, row_02));
// t_g_pixels = [(G3+G2+G1+G5)/4, (R3+R0+B0+B2)/4, (G2+G2+G2+G2)/4, (R0+R0+R0+R0)/2]
// t_g_pixels = [G@R3, (R3+R0+B0+B2)/4, G@G2, R@R0]
pixels.g = vuxtb16_ror8(t_g_pixels);
// pixels.g = [0, G@R3, 0, G@G2]
v128_t t_b_pixels = vhadd_u8(row_02, vpkhbt(row_02, row_02));
// t_b_pixels = [(B1+B3+B0+B2)/4, (G1+G5+G0+G4)/4, (B0+B2+B0+B2)/4, (G0+G4+G0+G4)/2]
// t_b_pixels = [B@R3, (G1+G5+G0+G4)/4, B@G2, (G0+G4)/2]
pixels.b = vuxtb16_ror8(t_b_pixels);
// pixels.b = [0, B@R3, 0, B@G2]
return pixels;
}
// Row vectors are loaded into memory and processed in little-endian order.
// row_0 stores MSB [G1, B1, G0, B0] LSB pixels where each pixel is 8-bits.
// row_1 stores MSB [R1, G3, R0, G2] LSB pixels where each pixel is 8-bits.
// row_2 stores MSB [G5, B3, G4, B2] LSB pixels where each pixel is 8-bits.
// In the case of vectors larger than 32-bits the pattern is repeated for every 32-bits.
//
// vdebayer_rggb produces two output pixels per 32-bits:
// pixels.r = MSB [0, R@G3, 0, R@R0] LSB pixels where each pixel is 8-bits.
// pixels.g = MSB [0, G@G3, 0, G@R0] LSB pixels where each pixel is 8-bits.
// pixels.b = MSB [0, B@G3, 0, B@R0] LSB pixels where each pixel is 8-bits.
static inline vrgb_pixels_t vdebayer_rggb(v128_t row_0, v128_t row_1, v128_t row_2) {
vrgb_pixels_t pixels;
v128_t row_02 = vhadd_u8(row_0, row_2);
// row_02 = [(G1+G5)/2, (B1+B3)/2, (G0+G4)/2, (B0+B2)/2]
v128_t row_11 = vhadd_u8(row_1, vpkhtb(row_1, row_1));
// row_11 = [(R1+R1)/2, (G3+G3)/2, (R0+R1)/2, (G2+G3)/2]
// row_11 = [R@R1, G@G3, R@G3, (G2+G3)/2]
v128_t t_r_pixels = vhadd_u8(row_1, vpkhbt(row_1, row_1));
// t_r_pixels = [(R1+R0)/2, (G3+G2)/2, (R0+R0)/2, (G2+G2)/2]
// t_r_pixels = [R@G3, (G3+G2)/2, R@R0, G@G2]
pixels.r = vuxtb16_ror8(t_r_pixels);
// pixels.r = [0, R@G3, 0, R@R0]
v128_t t_g_pixels = vhadd_u8(row_11, vpkhtb_ror8(row_11, row_02));
// t_g_pixels = [(R1+R1+R1+R1)/4, (G3+G3+G3+G3)/4, (R0+R1+B1+B3)/4, (G2+G3+G0+G4)/4]
// t_g_pixels = [R@R1, G@G3, (R0+R1+B1+B3)/4, G@R0]
pixels.g = vuxtb16(t_g_pixels);
// pixels.g = [0, G@G3, 0, G@R0]
v128_t t_b_pixels = vhadd_u8(row_02, vpkhtb(row_02, row_02));
// t_b_pixels = [(G1+G5+G1+G5)/4, (B1+B3+B1+B3)/4, (G0+G4+G1+G5)/4, (B0+B2+B1+B3)/4]
// t_b_pixels = [(G1+G5)/2, B@G3, (G0+G4+G1+G5)/4, B@R0]
pixels.b = vuxtb16(t_b_pixels);
// pixels.b = [0, B@G3, 0, B@R0]
return pixels;
}
#if !defined(IMLIB_ENABLE_DEBAYER_OPTIMIZATION)
static inline vrgb_pixels_t vdebayer_all_0(image_t *src, v128_t row_0, v128_t row_1, v128_t row_2) {
switch (src->pixfmt) {
case PIXFORMAT_BAYER_BGGR: {
return vdebayer_bggr(row_0, row_1, row_2);
}
case PIXFORMAT_BAYER_GBRG: {
return vdebayer_gbrg(row_0, row_1, row_2);
}
case PIXFORMAT_BAYER_GRBG: {
return vdebayer_grbg(row_0, row_1, row_2);
}
case PIXFORMAT_BAYER_RGGB: {
return vdebayer_rggb(row_0, row_1, row_2);
}
default: {
__builtin_unreachable();
}
}
}
static inline vrgb_pixels_t vdebayer_all_1(image_t *src, v128_t row_0, v128_t row_1, v128_t row_2) {
switch (src->pixfmt) {
case PIXFORMAT_BAYER_BGGR: {
// PIXFORMAT_BAYER_BGGR shifted down by 1 becomes PIXFORMAT_BAYER_GRBG
return vdebayer_grbg(row_0, row_1, row_2);
}
case PIXFORMAT_BAYER_GBRG: {
// PIXFORMAT_BAYER_GBRG shifted down by 1 becomes PIXFORMAT_BAYER_RGGB
return vdebayer_rggb(row_0, row_1, row_2);
}
case PIXFORMAT_BAYER_GRBG: {
// PIXFORMAT_BAYER_GRBG shifted down by 1 becomes PIXFORMAT_BAYER_BGGR
return vdebayer_bggr(row_0, row_1, row_2);
}
case PIXFORMAT_BAYER_RGGB: {
// PIXFORMAT_BAYER_RGGB shifted down by 1 becomes PIXFORMAT_BAYER_GBRG
return vdebayer_gbrg(row_0, row_1, row_2);
}
default: {
__builtin_unreachable();
}
}
}
#endif // !IMLIB_ENABLE_DEBAYER_OPTIMIZATION
// Note that the loaded pointers are shifted to up by 1 to account for the offset
// created by debayering the image.
static inline v4x_row_ptrs_t vdebayer_rowptrs_init(const image_t *src, int32_t y) {
v4x_row_ptrs_t rowptrs;
// keep row pointers in bounds
if (y == 0) {
rowptrs.p1.u8 = src->data;
rowptrs.p2.u8 = rowptrs.p1.u8 + ((src->h >= 2) ? src->w : 0);
rowptrs.p3.u8 = rowptrs.p1.u8 + ((src->h >= 3) ? (src->w * 2) : 0);
rowptrs.p0.u8 = rowptrs.p2.u8;
} else if (y == (src->h - 2)) {
rowptrs.p0.u8 = src->data + ((y - 1) * src->w);
rowptrs.p1.u8 = rowptrs.p0.u8 + src->w;
rowptrs.p2.u8 = rowptrs.p1.u8 + src->w;
rowptrs.p3.u8 = rowptrs.p1.u8;
} else if (y == (src->h - 1)) {
rowptrs.p0.u8 = src->data + ((y - 1) * src->w);
rowptrs.p1.u8 = rowptrs.p0.u8 + src->w;
rowptrs.p2.u8 = rowptrs.p0.u8;
rowptrs.p3.u8 = rowptrs.p1.u8;
} else {
// get 4 neighboring rows
rowptrs.p0.u8 = src->data + ((y - 1) * src->w);
rowptrs.p1.u8 = rowptrs.p0.u8 + src->w;
rowptrs.p2.u8 = rowptrs.p1.u8 + src->w;
rowptrs.p3.u8 = rowptrs.p2.u8 + src->w;
}
// Shift loaded pointers up by 1 to account for the offset created by debayering the image.
rowptrs.p0.u8 -= 1;
rowptrs.p1.u8 -= 1;
rowptrs.p2.u8 -= 1;
rowptrs.p3.u8 -= 1;
return rowptrs;
}
static inline v2x_row_ptrs_t vdebayer_quarter_rowptrs_init(const image_t *src, int32_t y) {
v2x_row_ptrs_t rowptrs;
// keep row pointers in bounds
if (y == 0) {
rowptrs.p0.u8 = src->data;
rowptrs.p1.u8 = rowptrs.p0.u8 + ((src->h >= 2) ? src->w : 0);
} else if (y == (src->h - 1)) {
rowptrs.p0.u8 = src->data + (y * src->w);
rowptrs.p1.u8 = rowptrs.p0.u8 - src->w;
} else {
// get 2 neighboring rows
rowptrs.p0.u8 = src->data + (y * src->w);
rowptrs.p1.u8 = rowptrs.p0.u8 + src->w;
}
return rowptrs;
}
static inline v128_predicate_t vdebayer_load_pred(const image_t *src, int32_t x) {
// Load 1x to 4x 32-bit rows overlapping by 2 pixels.
return vpredicate_8(src->w - x + (VBAYER_X_STRIDE - 2));
}
static inline v128_predicate_t vdebayer_store_pred(int32_t width, int32_t x) {
// For 2x to 8x 16-bit lanes.
return vpredicate_16(width - x);
}
// Loads pixels from the image into the 4 row vectors and handles the boundary conditions.
// Note that the loaded pixels are shifted to the right by 1 to account for the offset
// created by debayering the image.
static v4x_rows_t vdebayer_load_rows_inner(v4x_row_ptrs_t rowptrs, uint32_t x, v128_t offsets, v128_predicate_t pred) {
bool x_is_0 = (x == 0);
// Start loading 1 pixel behind the x position and load 1 extra pixel.
if (!x_is_0) {
pred = vpredicate_8_add(pred, 1);
} else {
// Pointers are shifted back by 1 already so this undoes that shift.
x += 1;
}
v4x_rows_t rows = vldr_u32_gather_pred_x4_unaligned(rowptrs, x, offsets, pred);
// Vector lane access must use constant offsets so we handle the boundary conditions
// using a switch statement per boundary condition. In this case the get/set functions
// compile down into 1 instruction each.
switch (vpredicate_8_get_n(pred)) {
case 1:
// MSB [0, 0, 0, G0] LSB -> MSB [G0, G0, G0, G0] LSB
rows.r0 = vset_u32(rows.r0, 0, vget_u8(rows.r0, 0) * 0x01010101);
rows.r1 = vset_u32(rows.r1, 0, vget_u8(rows.r1, 0) * 0x01010101);
rows.r2 = vset_u32(rows.r2, 0, vget_u8(rows.r2, 0) * 0x01010101);
rows.r3 = vset_u32(rows.r3, 0, vget_u8(rows.r3, 0) * 0x01010101);
break;
case 2:
// MSB [0, 0, R0, G0] LSB -> MSB [R0, G0, R0, G0] LSB
rows.r0 = vset_u16(rows.r0, 1, vget_u16(rows.r0, 0));
rows.r1 = vset_u16(rows.r1, 1, vget_u16(rows.r1, 0));
rows.r2 = vset_u16(rows.r2, 1, vget_u16(rows.r2, 0));
rows.r3 = vset_u16(rows.r3, 1, vget_u16(rows.r3, 0));
break;
case 3:
// MSB [0, G1, R0, G0] LSB -> MSB [R0, G1, R0, G0] LSB
rows.r0 = vset_u8(rows.r0, 3, vget_u8(rows.r0, 2));
rows.r1 = vset_u8(rows.r1, 3, vget_u8(rows.r1, 2));
rows.r2 = vset_u8(rows.r2, 3, vget_u8(rows.r2, 2));
rows.r3 = vset_u8(rows.r3, 3, vget_u8(rows.r3, 2));
break;
#if UINT8_VECTOR_SIZE >= 8
case 5:
// MSB [0, 0, 0, G0] LSB -> MSB [G0, G0, G0, G0] LSB
rows.r0 = vset_u32(rows.r0, 1, vget_u8(rows.r0, 4) * 0x01010101);
rows.r1 = vset_u32(rows.r1, 1, vget_u8(rows.r1, 4) * 0x01010101);
rows.r2 = vset_u32(rows.r2, 1, vget_u8(rows.r2, 4) * 0x01010101);
rows.r3 = vset_u32(rows.r3, 1, vget_u8(rows.r3, 4) * 0x01010101);
break;
case 6:
// MSB [0, 0, R0, G0] LSB -> MSB [R0, G0, R0, G0] LSB
rows.r0 = vset_u16(rows.r0, 3, vget_u16(rows.r0, 2));
rows.r1 = vset_u16(rows.r1, 3, vget_u16(rows.r1, 2));
rows.r2 = vset_u16(rows.r2, 3, vget_u16(rows.r2, 2));
rows.r3 = vset_u16(rows.r3, 3, vget_u16(rows.r3, 2));
break;
case 7:
// MSB [0, G1, R0, G0] LSB -> MSB [R0, G1, R0, G0] LSB
rows.r0 = vset_u8(rows.r0, 7, vget_u8(rows.r0, 5));
rows.r1 = vset_u8(rows.r1, 7, vget_u8(rows.r1, 5));
rows.r2 = vset_u8(rows.r2, 7, vget_u8(rows.r2, 5));
rows.r3 = vset_u8(rows.r3, 7, vget_u8(rows.r3, 5));
break;
#endif
#if UINT8_VECTOR_SIZE >= 16
case 9:
// MSB [0, 0, 0, G0] LSB -> MSB [G0, G0, G0, G0] LSB
rows.r0 = vset_u32(rows.r0, 2, vget_u8(rows.r0, 8) * 0x01010101);
rows.r1 = vset_u32(rows.r1, 2, vget_u8(rows.r1, 8) * 0x01010101);
rows.r2 = vset_u32(rows.r2, 2, vget_u8(rows.r2, 8) * 0x01010101);
rows.r3 = vset_u32(rows.r3, 2, vget_u8(rows.r3, 8) * 0x01010101);
break;
case 10:
// MSB [0, 0, R0, G0] LSB -> MSB [R0, G0, R0, G0] LSB
rows.r0 = vset_u16(rows.r0, 5, vget_u16(rows.r0, 4));
rows.r1 = vset_u16(rows.r1, 5, vget_u16(rows.r1, 4));
rows.r2 = vset_u16(rows.r2, 5, vget_u16(rows.r2, 4));
rows.r3 = vset_u16(rows.r3, 5, vget_u16(rows.r3, 4));
break;
case 11:
// MSB [0, G1, R0, G0] LSB -> MSB [R0, G1, R0, G0] LSB
rows.r0 = vset_u8(rows.r0, 11, vget_u8(rows.r0, 9));
rows.r1 = vset_u8(rows.r1, 11, vget_u8(rows.r1, 9));
rows.r2 = vset_u8(rows.r2, 11, vget_u8(rows.r2, 9));
rows.r3 = vset_u8(rows.r3, 11, vget_u8(rows.r3, 9));
break;
case 13:
// MSB [0, 0, 0, G0] LSB -> MSB [G0, G0, G0, G0] LSB
rows.r0 = vset_u32(rows.r0, 3, vget_u8(rows.r0, 12) * 0x01010101);
rows.r1 = vset_u32(rows.r1, 3, vget_u8(rows.r1, 12) * 0x01010101);
rows.r2 = vset_u32(rows.r2, 3, vget_u8(rows.r2, 12) * 0x01010101);
rows.r3 = vset_u32(rows.r3, 3, vget_u8(rows.r3, 12) * 0x01010101);
break;
case 14:
// MSB [0, 0, R0, G0] LSB -> MSB [R0, G0, R0, G0] LSB
rows.r0 = vset_u16(rows.r0, 7, vget_u16(rows.r0, 6));
rows.r1 = vset_u16(rows.r1, 7, vget_u16(rows.r1, 6));
rows.r2 = vset_u16(rows.r2, 7, vget_u16(rows.r2, 6));
rows.r3 = vset_u16(rows.r3, 7, vget_u16(rows.r3, 6));
break;
case 15:
// MSB [0, G1, R0, G0] LSB -> MSB [R0, G1, R0, G0] LSB
rows.r0 = vset_u8(rows.r0, 15, vget_u8(rows.r0, 13));
rows.r1 = vset_u8(rows.r1, 15, vget_u8(rows.r1, 13));
rows.r2 = vset_u8(rows.r2, 15, vget_u8(rows.r2, 13));
rows.r3 = vset_u8(rows.r3, 15, vget_u8(rows.r3, 13));
break;
#endif
#if UINT8_VECTOR_SIZE >= 32
#error "Unsupported vector size"
#endif
default:
break;
}
if (x_is_0) {
// Shift loaded pixels left by 1 as if we started loading at x - 1.
// MSB [R1, G1, R0, G0] LSB -> MSB [G1, R0, G0, R0] LSB
uint32_t r0 = vget_u8(rows.r0, 1);
uint32_t r1 = vget_u8(rows.r1, 1);
uint32_t r2 = vget_u8(rows.r2, 1);
uint32_t r3 = vget_u8(rows.r3, 1);
rows.r0 = vshlc(rows.r0, &r0, 8);
rows.r1 = vshlc(rows.r1, &r1, 8);
rows.r2 = vshlc(rows.r2, &r2, 8);
rows.r3 = vshlc(rows.r3, &r3, 8);
}
return rows;
}
static inline v4x_rows_t vdebayer_load_rows(const image_t *src, v4x_row_ptrs_t rowptrs, uint32_t x, v128_t offsets) {
v128_predicate_t pred = vdebayer_load_pred(src, x);
// For the vast majority of cases we load vector size pixels at a time and exit quickly.
if ((x != 0) && vpredicate_8_all_lanes_active(pred)) {
// Start loading 1 pixel behind the x position and load 1 extra pixel.
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
rows.r3 = vldr_u32_gather_unaligned(rowptrs.p3.u8 + x, offsets);
return rows;
} else {
return vdebayer_load_rows_inner(rowptrs, x, offsets, pred);
}
}
// In the case of vectors larger than 32-bits the pattern is repeated for every 32-bits.
//
// pixels.r = MSB [0, R1, 0, R0] LSB pixels where each pixel is 8-bits.
// pixels.g = MSB [0, G1, 0, G0] LSB pixels where each pixel is 8-bits.
// pixels.b = MSB [0, B1, 0, B0] LSB pixels where each pixel is 8-bits.
//
// Y == ((R * 38) + (G * 75) + (B * 15)) / 128
//
// Returns 2x int8_t Y (MSB [garbage, Y1, garbage, Y0] LSB) pixels for every 32-bits.
static inline v128_t vdebayer_to_y(vrgb_pixels_t pixels) {
pixels.r = vrgb_pixels_to_grayscale(pixels);
#if (OMV_JPEG_CODEC_ENABLE == 0)
pixels.r = veor_u32(pixels.r, vdup_u32(0x800080));
#endif
return pixels.r;
}
// In the case of vectors larger than 32-bits the pattern is repeated for every 32-bits.
//
// pixels.r = MSB [0, R1, 0, R0] LSB pixels where each pixel is 8-bits.
// pixels.g = MSB [0, G1, 0, G0] LSB pixels where each pixel is 8-bits.
// pixels.b = MSB [0, B1, 0, B0] LSB pixels where each pixel is 8-bits.
//
// CB == ((B * 64) - ((R * 21) + (G * 43))) / 128
//
// Returns 2x int8_t CB (MSB [garbage, CB1, garbage, CB0] LSB) pixels for every 32-bits.
static inline v128_t vdebayer_to_cb(vrgb_pixels_t pixels) {
#if (__ARM_ARCH >= 8)
pixels.r = vmul_n_s16(pixels.r, -21);
pixels.r = vmla_n_s16(pixels.g, -43, pixels.r);
pixels.r = vmla_n_s16(pixels.b, 64, pixels.r);
#else
pixels.r = vmul_n_u32(pixels.r, 21);
pixels.r = vmla_n_u32(pixels.g, 43, pixels.r);
pixels.b = vmul_n_u32(pixels.b, 64);
pixels.r = vsub_s16(pixels.b, pixels.r);
#endif
pixels.r = vlsr_u32(pixels.r, 7);
#if (OMV_JPEG_CODEC_ENABLE == 1)
pixels.r = veor_u32(pixels.r, vdup_u32(0x800080));
#endif
return pixels.r;
}
// In the case of vectors larger than 32-bits the pattern is repeated for every 32-bits.
//
// pixels.r = MSB [0, R1, 0, R0] LSB pixels where each pixel is 8-bits.
// pixels.g = MSB [0, G1, 0, G0] LSB pixels where each pixel is 8-bits.
// pixels.b = MSB [0, B1, 0, B0] LSB pixels where each pixel is 8-bits.
//
// CR == ((R * 64) - ((G * 54) + (B * 10))) / 128
//
// Returns 2x int8_t CR (MSB [garbage, CR1, garbage, CR0] LSB) pixels for every 32-bits.
static inline v128_t vdebayer_to_cr(vrgb_pixels_t pixels) {
#if (__ARM_ARCH >= 8)
pixels.r = vmul_n_s16(pixels.r, 64);
pixels.r = vmla_n_s16(pixels.g, -54, pixels.r);
pixels.r = vmla_n_s16(pixels.b, -10, pixels.r);
#else
pixels.r = vmul_n_u32(pixels.r, 64);
pixels.g = vmul_n_u32(pixels.g, 54);
pixels.g = vmla_n_u32(pixels.b, 10, pixels.g);
pixels.r = vsub_s16(pixels.r, pixels.g);
#endif
pixels.r = vlsr_u32(pixels.r, 7);
#if (OMV_JPEG_CODEC_ENABLE == 1)
pixels.r = veor_u32(pixels.r, vdup_u32(0x800080));
#endif
return pixels.r;
}
// FIXME: This needs to be placed in the DTCM. .bss is in the DTCM on Alif. But, this is not portable.
// Move this to a proper DTCM section for other platforms.
static uint8_t gamma_lut[256];
// In the case of vectors larger than 32-bits the pattern is repeated for every 32-bits.
//
// pixels.r = MSB [0, R1, 0, R0] LSB pixels where each pixel is 8-bits.
// pixels.g = MSB [0, G1, 0, G0] LSB pixels where each pixel is 8-bits.
// pixels.b = MSB [0, B1, 0, B0] LSB pixels where each pixel is 8-bits.
//
// Returns the same.
static inline vrgb_pixels_t vdebayer_apply_rb_gain(vrgb_pixels_t pixels, uint32_t red_gain, uint32_t blue_gain) {
pixels.r = vusat_s16_narrow_u8_lo(pixels.r, vmul_n_u32(pixels.r, red_gain), 5);
pixels.b = vusat_s16_narrow_u8_lo(pixels.b, vmul_n_u32(pixels.b, blue_gain), 5);
pixels.r = vldr_u8_gather(gamma_lut, pixels.r);
pixels.g = vldr_u8_gather(gamma_lut, pixels.g);
pixels.b = vldr_u8_gather(gamma_lut, pixels.b);
return pixels;
}
// In the case of vectors larger than 32-bits the pattern is repeated for every 32-bits.
//
// pixels.r = MSB [R3, R2, R1, R0] LSB pixels where each pixel is 8-bits.
// pixels.g = MSB [G3, G2, G1, G0] LSB pixels where each pixel is 8-bits.
// pixels.b = MSB [B3, B2, B2, B0] LSB pixels where each pixel is 8-bits.
//
// Stores 4x Grayscale pixels for every 32-bits.
static inline void vdebayer_store_packed_grayscale(uint8_t *p, vrgb_pixels_t packed_pixels,
uint32_t red_gain, uint32_t blue_gain,
int32_t len) {
vrgb_pixels_t pixels0 = {
.r = vuxtb16(packed_pixels.r),
.g = vuxtb16(packed_pixels.g),
.b = vuxtb16(packed_pixels.b),
};
v128_t v0 = vrgb_pixels_to_grayscale(vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain));
vrgb_pixels_t pixels1 = {
.r = vuxtb16_ror8(packed_pixels.r),
.g = vuxtb16_ror8(packed_pixels.g),
.b = vuxtb16_ror8(packed_pixels.b),
};
v128_t v1 = vrgb_pixels_to_grayscale(vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain));
vstr_u8_pred(p, vmov_u16_narrow_u8_hi(v0, v1), vpredicate_8(len));
}
// In the case of vectors larger than 32-bits the pattern is repeated for every 32-bits.
//
// pixels.r = MSB [R3, R2, R1, R0] LSB pixels where each pixel is 8-bits.
// pixels.g = MSB [G3, G2, G1, G0] LSB pixels where each pixel is 8-bits.
// pixels.b = MSB [B3, B2, B2, B0] LSB pixels where each pixel is 8-bits.
//
// Stores 4x RGB565 pixels for every 32-bits.
static inline void vdebayer_store_packed_rgb565(uint16_t *p, vrgb_pixels_t packed_pixels,
uint32_t red_gain, uint32_t blue_gain,
int32_t len) {
v2x_rows_t out;
vrgb_pixels_t pixels0 = {
.r = vuxtb16(packed_pixels.r),
.g = vuxtb16(packed_pixels.g),
.b = vuxtb16(packed_pixels.b),
};
out.r0 = vrgb_pixels_to_rgb565(vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain));
vrgb_pixels_t pixels1 = {
.r = vuxtb16_ror8(packed_pixels.r),
.g = vuxtb16_ror8(packed_pixels.g),
.b = vuxtb16_ror8(packed_pixels.b),
};
out.r1 = vrgb_pixels_to_rgb565(vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain));
if (len >= (UINT16_VECTOR_SIZE * 2)) {
vst2_u16(p, out);
} else {
vst2_u16_len(p, out, len);
}
}
static inline void vdebayer_grayscale_buf_copy(int32_t y, image_t *buf, image_t *dst) {
if (y >= VBAYER_BUF_KSIZE) {
// Transfer buffer lines...
int32_t y_offset = y - VBAYER_BUF_KSIZE;
vmemcpy_8(IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(dst, y_offset),
IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(buf, (y_offset % VBAYER_BUF_BROWS)),
IMAGE_GRAYSCALE_LINE_LEN_BYTES(dst) * VBAYER_Y_STRIDE);
}
}
static inline void vdebayer_rgb565_buf_copy(int32_t y, image_t *buf, image_t *dst) {
if (y >= VBAYER_BUF_KSIZE) {
// Transfer buffer lines...
int32_t y_offset = y - VBAYER_BUF_KSIZE;
vmemcpy_16(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(dst, y_offset),
IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(buf, (y_offset % VBAYER_BUF_BROWS)),
IMAGE_RGB565_LINE_LEN_BYTES(dst) * VBAYER_Y_STRIDE);
}
}
#if defined(IMLIB_ENABLE_DEBAYER_OPTIMIZATION)
static void vdebayer_bggr_to_binary(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint32_t *p0 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(dst, y);
uint32_t *p1 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(dst, y + 1);
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_store_binary(p0, x + x_offset, vdebayer_bggr(rows.r0, rows.r1, rows.r2), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_BGGR shifted down by 1 becomes PIXFORMAT_BAYER_GRBG
vrgb_pixels_store_binary(p1, x + x_offset, vdebayer_grbg(rows.r1, rows.r2, rows.r3), pred);
}
}
}
static void vdebayer_gbrg_to_binary(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint32_t *p0 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(dst, y);
uint32_t *p1 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(dst, y + 1);
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_store_binary(p0, x + x_offset, vdebayer_gbrg(rows.r0, rows.r1, rows.r2), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_GBRG shifted down by 1 becomes PIXFORMAT_BAYER_RGGB
vrgb_pixels_store_binary(p1, x + x_offset, vdebayer_rggb(rows.r1, rows.r2, rows.r3), pred);
}
}
}
static void vdebayer_grbg_to_binary(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint32_t *p0 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(dst, y);
uint32_t *p1 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(dst, y + 1);
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_store_binary(p0, x + x_offset, vdebayer_grbg(rows.r0, rows.r1, rows.r2), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_GRBG shifted down by 1 becomes PIXFORMAT_BAYER_BGGR
vrgb_pixels_store_binary(p1, x + x_offset, vdebayer_bggr(rows.r1, rows.r2, rows.r3), pred);
}
}
}
static void vdebayer_rggb_to_binary(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint32_t *p0 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(dst, y);
uint32_t *p1 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(dst, y + 1);
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_store_binary(p0, x + x_offset, vdebayer_rggb(rows.r0, rows.r1, rows.r2), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_RGGB shifted down by 1 becomes PIXFORMAT_BAYER_GBRG
vrgb_pixels_store_binary(p1, x + x_offset, vdebayer_gbrg(rows.r1, rows.r2, rows.r3), pred);
}
}
}
static void vdebayer_bggr_to_grayscale(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint8_t *p0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(dst, y) + x_offset;
uint8_t *p1 = p0 + dst->w;
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_store_grayscale(p0, x, vdebayer_bggr(rows.r0, rows.r1, rows.r2), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_BGGR shifted down by 1 becomes PIXFORMAT_BAYER_GRBG
vrgb_pixels_store_grayscale(p1, x, vdebayer_grbg(rows.r1, rows.r2, rows.r3), pred);
}
}
}
static void vdebayer_gbrg_to_grayscale(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint8_t *p0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(dst, y) + x_offset;
uint8_t *p1 = p0 + dst->w;
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_store_grayscale(p0, x, vdebayer_gbrg(rows.r0, rows.r1, rows.r2), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_GBRG shifted down by 1 becomes PIXFORMAT_BAYER_RGGB
vrgb_pixels_store_grayscale(p1, x, vdebayer_rggb(rows.r1, rows.r2, rows.r3), pred);
}
}
}
static void vdebayer_grbg_to_grayscale(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint8_t *p0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(dst, y) + x_offset;
uint8_t *p1 = p0 + dst->w;
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_store_grayscale(p0, x, vdebayer_grbg(rows.r0, rows.r1, rows.r2), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_GRBG shifted down by 1 becomes PIXFORMAT_BAYER_BGGR
vrgb_pixels_store_grayscale(p1, x, vdebayer_bggr(rows.r1, rows.r2, rows.r3), pred);
}
}
}
static void vdebayer_rggb_to_grayscale(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint8_t *p0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(dst, y) + x_offset;
uint8_t *p1 = p0 + dst->w;
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_store_grayscale(p0, x, vdebayer_rggb(rows.r0, rows.r1, rows.r2), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_RGGB shifted down by 1 becomes PIXFORMAT_BAYER_GBRG
vrgb_pixels_store_grayscale(p1, x, vdebayer_gbrg(rows.r1, rows.r2, rows.r3), pred);
}
}
}
static void vdebayer_bggr_to_rgb565(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint16_t *p0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(dst, y) + x_offset;
uint16_t *p1 = p0 + dst->w;
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_store_rgb565(p0, x, vdebayer_bggr(rows.r0, rows.r1, rows.r2), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_BGGR shifted down by 1 becomes PIXFORMAT_BAYER_GRBG
vrgb_pixels_store_rgb565(p1, x, vdebayer_grbg(rows.r1, rows.r2, rows.r3), pred);
}
}
}
static void vdebayer_gbrg_to_rgb565(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint16_t *p0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(dst, y) + x_offset;
uint16_t *p1 = p0 + dst->w;
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_store_rgb565(p0, x, vdebayer_gbrg(rows.r0, rows.r1, rows.r2), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_GBRG shifted down by 1 becomes PIXFORMAT_BAYER_RGGB
vrgb_pixels_store_rgb565(p1, x, vdebayer_rggb(rows.r1, rows.r2, rows.r3), pred);
}
}
}
static void vdebayer_grbg_to_rgb565(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint16_t *p0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(dst, y) + x_offset;
uint16_t *p1 = p0 + dst->w;
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_store_rgb565(p0, x, vdebayer_grbg(rows.r0, rows.r1, rows.r2), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_GRBG shifted down by 1 becomes PIXFORMAT_BAYER_BGGR
vrgb_pixels_store_rgb565(p1, x, vdebayer_bggr(rows.r1, rows.r2, rows.r3), pred);
}
}
}
static void vdebayer_rggb_to_rgb565(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint16_t *p0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(dst, y) + x_offset;
uint16_t *p1 = p0 + dst->w;
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_store_rgb565(p0, x, vdebayer_rggb(rows.r0, rows.r1, rows.r2), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_RGGB shifted down by 1 becomes PIXFORMAT_BAYER_GBRG
vrgb_pixels_store_rgb565(p1, x, vdebayer_gbrg(rows.r1, rows.r2, rows.r3), pred);
}
}
}
#else
static void vdebayer_to_binary(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint32_t *p0 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(dst, y);
uint32_t *p1 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(dst, y + 1);
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_store_binary(p0, x + x_offset, vdebayer_all_0(src, rows.r0, rows.r1, rows.r2), pred);
if (y == y_end) {
continue;
}
vrgb_pixels_store_binary(p1, x + x_offset, vdebayer_all_1(src, rows.r1, rows.r2, rows.r3), pred);
}
}
}
static void vdebayer_to_grayscale(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint8_t *p0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(dst, y) + x_offset;
uint8_t *p1 = p0 + dst->w;
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_store_grayscale(p0, x, vdebayer_all_0(src, rows.r0, rows.r1, rows.r2), pred);
if (y == y_end) {
continue;
}
vrgb_pixels_store_grayscale(p1, x, vdebayer_all_1(src, rows.r1, rows.r2, rows.r3), pred);
}
}
}
static void vdebayer_to_rgb565(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint16_t *p0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(dst, y) + x_offset;
uint16_t *p1 = p0 + dst->w;
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_store_rgb565(p0, x, vdebayer_all_0(src, rows.r0, rows.r1, rows.r2), pred);
if (y == y_end) {
continue;
}
vrgb_pixels_store_rgb565(p1, x, vdebayer_all_1(src, rows.r1, rows.r2, rows.r3), pred);
}
}
}
#endif // IMLIB_ENABLE_DEBAYER_OPTIMIZATION
static void vdebayer(image_t *src, rectangle_t *roi, int32_t x_offset, image_t *dst) {
#if defined(IMLIB_ENABLE_DEBAYER_OPTIMIZATION)
switch (src->pixfmt) {
case PIXFORMAT_BAYER_BGGR: {
switch (dst->pixfmt) {
case PIXFORMAT_BINARY: {
vdebayer_bggr_to_binary(src, roi, x_offset, dst);
break;
}
case PIXFORMAT_GRAYSCALE: {
vdebayer_bggr_to_grayscale(src, roi, x_offset, dst);
break;
}
case PIXFORMAT_RGB565: {
vdebayer_bggr_to_rgb565(src, roi, x_offset, dst);
break;
}
default: {
__builtin_unreachable();
}
}
break;
}
case PIXFORMAT_BAYER_GBRG: {
switch (dst->pixfmt) {
case PIXFORMAT_BINARY: {
vdebayer_gbrg_to_binary(src, roi, x_offset, dst);
break;
}
case PIXFORMAT_GRAYSCALE: {
vdebayer_gbrg_to_grayscale(src, roi, x_offset, dst);
break;
}
case PIXFORMAT_RGB565: {
vdebayer_gbrg_to_rgb565(src, roi, x_offset, dst);
break;
}
default: {
__builtin_unreachable();
}
}
break;
}
case PIXFORMAT_BAYER_GRBG: {
switch (dst->pixfmt) {
case PIXFORMAT_BINARY: {
vdebayer_grbg_to_binary(src, roi, x_offset, dst);
break;
}
case PIXFORMAT_GRAYSCALE: {
vdebayer_grbg_to_grayscale(src, roi, x_offset, dst);
break;
}
case PIXFORMAT_RGB565: {
vdebayer_grbg_to_rgb565(src, roi, x_offset, dst);
break;
}
default: {
__builtin_unreachable();
}
}
break;
}
case PIXFORMAT_BAYER_RGGB: {
switch (dst->pixfmt) {
case PIXFORMAT_BINARY: {
vdebayer_rggb_to_binary(src, roi, x_offset, dst);
break;
}
case PIXFORMAT_GRAYSCALE: {
vdebayer_rggb_to_grayscale(src, roi, x_offset, dst);
break;
}
case PIXFORMAT_RGB565: {
vdebayer_rggb_to_rgb565(src, roi, x_offset, dst);
break;
}
default: {
__builtin_unreachable();
}
}
break;
}
default: {
__builtin_unreachable();
}
}
#else
switch (dst->pixfmt) {
case PIXFORMAT_BINARY: {
vdebayer_to_binary(src, roi, x_offset, dst);
break;
}
case PIXFORMAT_GRAYSCALE: {
vdebayer_to_grayscale(src, roi, x_offset, dst);
break;
}
case PIXFORMAT_RGB565: {
vdebayer_to_rgb565(src, roi, x_offset, dst);
break;
}
default: {
__builtin_unreachable();
}
}
#endif // IMLIB_ENABLE_DEBAYER_OPTIMIZATION
}
// assumes dst->w == src->w
// assumes dst->h == 1
void imlib_debayer_line(int x_start, int x_end, int y_row, void *dst_row_ptr, pixformat_t pixfmt, image_t *src) {
rectangle_t roi = {
.x = x_start,
.y = y_row,
.w = x_end - x_start,
.h = 1,
};
image_t dst = {
.w = src->w,
.h = 1,
.pixfmt = pixfmt,
.data = dst_row_ptr,
};
vdebayer(src, &roi, x_start, &dst);
}
// assumes dst->w == src->w
// assumes dst->h == src->h
// src and dst may not overlap, but, faster than imlib_debayer_image_awb
void imlib_debayer_image(image_t *dst, image_t *src) {
rectangle_t roi = {
.x = 0,
.y = 0,
.w = src->w,
.h = src->h,
};
vdebayer(src, &roi, 0, dst);
}
#if defined(IMLIB_ENABLE_DEBAYER_OPTIMIZATION)
static void vdebayer_bggr_to_ycbcr(image_t *src, rectangle_t *roi, int8_t *Y0, int8_t *CB, int8_t *CR) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint8_t *Y0p0 = ((uint8_t *) Y0) + (y * JPEG_MCU_W);
uint8_t *CBp0 = ((uint8_t *) CB) + (y * JPEG_MCU_W);
uint8_t *CRp0 = ((uint8_t *) CR) + (y * JPEG_MCU_W);
uint8_t *Y0p1 = Y0p0 + JPEG_MCU_W;
uint8_t *CBp1 = CBp0 + JPEG_MCU_W;
uint8_t *CRp1 = CRp0 + JPEG_MCU_W;
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_t pixels0 = vdebayer_bggr(rows.r0, rows.r1, rows.r2);
vstr_u16_narrow_u8_pred(Y0p0 + x, vdebayer_to_y(pixels0), pred);
vstr_u16_narrow_u8_pred(CBp0 + x, vdebayer_to_cb(pixels0), pred);
vstr_u16_narrow_u8_pred(CRp0 + x, vdebayer_to_cr(pixels0), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_BGGR shifted down by 1 becomes PIXFORMAT_BAYER_GRBG
vrgb_pixels_t pixels1 = vdebayer_grbg(rows.r1, rows.r2, rows.r3);
vstr_u16_narrow_u8_pred(Y0p1 + x, vdebayer_to_y(pixels1), pred);
vstr_u16_narrow_u8_pred(CBp1 + x, vdebayer_to_cb(pixels1), pred);
vstr_u16_narrow_u8_pred(CRp1 + x, vdebayer_to_cr(pixels1), pred);
}
}
}
static void vdebayer_gbrg_to_ycbcr(image_t *src, rectangle_t *roi, int8_t *Y0, int8_t *CB, int8_t *CR) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint8_t *Y0p0 = ((uint8_t *) Y0) + (y * JPEG_MCU_W);
uint8_t *CBp0 = ((uint8_t *) CB) + (y * JPEG_MCU_W);
uint8_t *CRp0 = ((uint8_t *) CR) + (y * JPEG_MCU_W);
uint8_t *Y0p1 = Y0p0 + JPEG_MCU_W;
uint8_t *CBp1 = CBp0 + JPEG_MCU_W;
uint8_t *CRp1 = CRp0 + JPEG_MCU_W;
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_t pixels0 = vdebayer_gbrg(rows.r0, rows.r1, rows.r2);
vstr_u16_narrow_u8_pred(Y0p0 + x, vdebayer_to_y(pixels0), pred);
vstr_u16_narrow_u8_pred(CBp0 + x, vdebayer_to_cb(pixels0), pred);
vstr_u16_narrow_u8_pred(CRp0 + x, vdebayer_to_cr(pixels0), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_GBRG shifted down by 1 becomes PIXFORMAT_BAYER_RGGB
vrgb_pixels_t pixels1 = vdebayer_rggb(rows.r1, rows.r2, rows.r3);
vstr_u16_narrow_u8_pred(Y0p1 + x, vdebayer_to_y(pixels1), pred);
vstr_u16_narrow_u8_pred(CBp1 + x, vdebayer_to_cb(pixels1), pred);
vstr_u16_narrow_u8_pred(CRp1 + x, vdebayer_to_cr(pixels1), pred);
}
}
}
static void vdebayer_grbg_to_ycbcr(image_t *src, rectangle_t *roi, int8_t *Y0, int8_t *CB, int8_t *CR) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint8_t *Y0p0 = ((uint8_t *) Y0) + (y * JPEG_MCU_W);
uint8_t *CBp0 = ((uint8_t *) CB) + (y * JPEG_MCU_W);
uint8_t *CRp0 = ((uint8_t *) CR) + (y * JPEG_MCU_W);
uint8_t *Y0p1 = Y0p0 + JPEG_MCU_W;
uint8_t *CBp1 = CBp0 + JPEG_MCU_W;
uint8_t *CRp1 = CRp0 + JPEG_MCU_W;
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_t pixels0 = vdebayer_grbg(rows.r0, rows.r1, rows.r2);
vstr_u16_narrow_u8_pred(Y0p0 + x, vdebayer_to_y(pixels0), pred);
vstr_u16_narrow_u8_pred(CBp0 + x, vdebayer_to_cb(pixels0), pred);
vstr_u16_narrow_u8_pred(CRp0 + x, vdebayer_to_cr(pixels0), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_GRBG shifted down by 1 becomes PIXFORMAT_BAYER_BGGR
vrgb_pixels_t pixels1 = vdebayer_bggr(rows.r1, rows.r2, rows.r3);
vstr_u16_narrow_u8_pred(Y0p1 + x, vdebayer_to_y(pixels1), pred);
vstr_u16_narrow_u8_pred(CBp1 + x, vdebayer_to_cb(pixels1), pred);
vstr_u16_narrow_u8_pred(CRp1 + x, vdebayer_to_cr(pixels1), pred);
}
}
}
static void vdebayer_rggb_to_ycbcr(image_t *src, rectangle_t *roi, int8_t *Y0, int8_t *CB, int8_t *CR) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint8_t *Y0p0 = ((uint8_t *) Y0) + (y * JPEG_MCU_W);
uint8_t *CBp0 = ((uint8_t *) CB) + (y * JPEG_MCU_W);
uint8_t *CRp0 = ((uint8_t *) CR) + (y * JPEG_MCU_W);
uint8_t *Y0p1 = Y0p0 + JPEG_MCU_W;
uint8_t *CBp1 = CBp0 + JPEG_MCU_W;
uint8_t *CRp1 = CRp0 + JPEG_MCU_W;
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_t pixels0 = vdebayer_rggb(rows.r0, rows.r1, rows.r2);
vstr_u16_narrow_u8_pred(Y0p0 + x, vdebayer_to_y(pixels0), pred);
vstr_u16_narrow_u8_pred(CBp0 + x, vdebayer_to_cb(pixels0), pred);
vstr_u16_narrow_u8_pred(CRp0 + x, vdebayer_to_cr(pixels0), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_RGGB shifted down by 1 becomes PIXFORMAT_BAYER_GBRG
vrgb_pixels_t pixels1 = vdebayer_gbrg(rows.r1, rows.r2, rows.r3);
vstr_u16_narrow_u8_pred(Y0p1 + x, vdebayer_to_y(pixels1), pred);
vstr_u16_narrow_u8_pred(CBp1 + x, vdebayer_to_cb(pixels1), pred);
vstr_u16_narrow_u8_pred(CRp1 + x, vdebayer_to_cr(pixels1), pred);
}
}
}
void imlib_debayer_ycbcr(image_t *src, rectangle_t *roi, int8_t *Y0, int8_t *CB, int8_t *CR) {
switch (src->pixfmt) {
case PIXFORMAT_BAYER_BGGR: {
vdebayer_bggr_to_ycbcr(src, roi, Y0, CB, CR);
break;
}
case PIXFORMAT_BAYER_GBRG: {
vdebayer_gbrg_to_ycbcr(src, roi, Y0, CB, CR);
break;
}
case PIXFORMAT_BAYER_GRBG: {
vdebayer_grbg_to_ycbcr(src, roi, Y0, CB, CR);
break;
}
case PIXFORMAT_BAYER_RGGB: {
vdebayer_rggb_to_ycbcr(src, roi, Y0, CB, CR);
break;
}
default: {
__builtin_unreachable();
}
}
}
#else
void imlib_debayer_ycbcr(image_t *src, rectangle_t *roi, int8_t *Y0, int8_t *CB, int8_t *CR) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = roi->h - 1; y < roi->h; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y + roi->y);
uint8_t *Y0p0 = ((uint8_t *) Y0) + (y * JPEG_MCU_W);
uint8_t *CBp0 = ((uint8_t *) CB) + (y * JPEG_MCU_W);
uint8_t *CRp0 = ((uint8_t *) CR) + (y * JPEG_MCU_W);
uint8_t *Y0p1 = Y0p0 + JPEG_MCU_W;
uint8_t *CBp1 = CBp0 + JPEG_MCU_W;
uint8_t *CRp1 = CRp0 + JPEG_MCU_W;
for (int32_t x = 0; x < roi->w; x += VBAYER_X_STRIDE) {
v4x_rows_t rows = vdebayer_load_rows(src, rowptrs, x + roi->x, offsets);
v128_predicate_t pred = vdebayer_store_pred(roi->w, x);
vrgb_pixels_t pixels0 = vdebayer_all_0(src, rows.r0, rows.r1, rows.r2);
vstr_u16_narrow_u8_pred(Y0p0 + x, vdebayer_to_y(pixels0), pred);
vstr_u16_narrow_u8_pred(CBp0 + x, vdebayer_to_cb(pixels0), pred);
vstr_u16_narrow_u8_pred(CRp0 + x, vdebayer_to_cr(pixels0), pred);
if (y == y_end) {
continue;
}
// PIXFORMAT_BAYER_RGGB shifted down by 1 becomes PIXFORMAT_BAYER_GBRG
vrgb_pixels_t pixels1 = vdebayer_all_1(src, rows.r1, rows.r2, rows.r3);
vstr_u16_narrow_u8_pred(Y0p1 + x, vdebayer_to_y(pixels1), pred);
vstr_u16_narrow_u8_pred(CBp1 + x, vdebayer_to_cb(pixels1), pred);
vstr_u16_narrow_u8_pred(CRp1 + x, vdebayer_to_cr(pixels1), pred);
}
}
}
#endif // IMLIB_ENABLE_DEBAYER_OPTIMIZATION
static void vdebayer_bggr_to_grayscale_awb(image_t *src, image_t *dst, image_t *buf,
uint32_t red_gain, uint32_t blue_gain) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = (src->h / VBAYER_Y_STRIDE) * VBAYER_Y_STRIDE; y < y_end; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint8_t *p0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
uint8_t *p1 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(buf, ((y + 1) % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_bggr(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_BGGR shifted down by 1 becomes PIXFORMAT_BAYER_GRBG
vrgb_pixels_t pixels1 = vdebayer_grbg(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_grayscale(p1, 0, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
rows.r3 = vldr_u32_gather_unaligned(rowptrs.p3.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_bggr(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_BGGR shifted down by 1 becomes PIXFORMAT_BAYER_GRBG
vrgb_pixels_t pixels1 = vdebayer_grbg(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_grayscale(p1, x, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_bggr(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_BGGR shifted down by 1 becomes PIXFORMAT_BAYER_GRBG
vrgb_pixels_t pixels1 = vdebayer_grbg(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_grayscale(p1, x, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
}
vdebayer_grayscale_buf_copy(y, buf, dst);
}
// Last odd row.
if (src->h % VBAYER_Y_STRIDE) {
int32_t y = src->h - 1;
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint8_t *p0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_bggr(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_bggr(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_bggr(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
}
vdebayer_grayscale_buf_copy(y, buf, dst);
}
}
static void vdebayer_gbrg_to_grayscale_awb(image_t *src, image_t *dst, image_t *buf,
uint32_t red_gain, uint32_t blue_gain) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = (src->h / VBAYER_Y_STRIDE) * VBAYER_Y_STRIDE; y < y_end; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint8_t *p0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
uint8_t *p1 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(buf, ((y + 1) % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_gbrg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_GBRG shifted down by 1 becomes PIXFORMAT_BAYER_RGGB
vrgb_pixels_t pixels1 = vdebayer_rggb(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_grayscale(p1, 0, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
rows.r3 = vldr_u32_gather_unaligned(rowptrs.p3.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_gbrg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_GBRG shifted down by 1 becomes PIXFORMAT_BAYER_RGGB
vrgb_pixels_t pixels1 = vdebayer_rggb(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_grayscale(p1, x, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_gbrg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_GBRG shifted down by 1 becomes PIXFORMAT_BAYER_RGGB
vrgb_pixels_t pixels1 = vdebayer_rggb(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_grayscale(p1, x, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
}
vdebayer_grayscale_buf_copy(y, buf, dst);
}
// Last odd row.
if (src->h % VBAYER_Y_STRIDE) {
int32_t y = src->h - 1;
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint8_t *p0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_gbrg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_gbrg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_gbrg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
}
vdebayer_grayscale_buf_copy(y, buf, dst);
}
}
static void vdebayer_grbg_to_grayscale_awb(image_t *src, image_t *dst, image_t *buf,
uint32_t red_gain, uint32_t blue_gain) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = (src->h / VBAYER_Y_STRIDE) * VBAYER_Y_STRIDE; y < y_end; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint8_t *p0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
uint8_t *p1 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(buf, ((y + 1) % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_grbg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_GRBG shifted down by 1 becomes PIXFORMAT_BAYER_BGGR
vrgb_pixels_t pixels1 = vdebayer_bggr(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_grayscale(p1, 0, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
rows.r3 = vldr_u32_gather_unaligned(rowptrs.p3.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_grbg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_GRBG shifted down by 1 becomes PIXFORMAT_BAYER_BGGR
vrgb_pixels_t pixels1 = vdebayer_bggr(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_grayscale(p1, x, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_grbg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_GRBG shifted down by 1 becomes PIXFORMAT_BAYER_BGGR
vrgb_pixels_t pixels1 = vdebayer_bggr(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_grayscale(p1, x, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
}
vdebayer_grayscale_buf_copy(y, buf, dst);
}
// Last odd row.
if (src->h % VBAYER_Y_STRIDE) {
int32_t y = src->h - 1;
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint8_t *p0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_grbg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_grbg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_grbg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
}
vdebayer_grayscale_buf_copy(y, buf, dst);
}
}
static void vdebayer_rggb_to_grayscale_awb(image_t *src, image_t *dst, image_t *buf,
uint32_t red_gain, uint32_t blue_gain) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = (src->h / VBAYER_Y_STRIDE) * VBAYER_Y_STRIDE; y < y_end; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint8_t *p0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
uint8_t *p1 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(buf, ((y + 1) % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_rggb(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_RGGB shifted down by 1 becomes PIXFORMAT_BAYER_GBRG
vrgb_pixels_t pixels1 = vdebayer_gbrg(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_grayscale(p1, 0, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
rows.r3 = vldr_u32_gather_unaligned(rowptrs.p3.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_rggb(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_RGGB shifted down by 1 becomes PIXFORMAT_BAYER_GBRG
vrgb_pixels_t pixels1 = vdebayer_gbrg(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_grayscale(p1, x, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_rggb(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_RGGB shifted down by 1 becomes PIXFORMAT_BAYER_GBRG
vrgb_pixels_t pixels1 = vdebayer_gbrg(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_grayscale(p1, x, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
}
vdebayer_grayscale_buf_copy(y, buf, dst);
}
// Last odd row.
if (src->h % VBAYER_Y_STRIDE) {
int32_t y = src->h - 1;
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint8_t *p0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_rggb(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_rggb(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_rggb(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_grayscale(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
}
vdebayer_grayscale_buf_copy(y, buf, dst);
}
}
static void vdebayer_bggr_to_rgb565_awb(image_t *src, image_t *dst, image_t *buf,
uint32_t red_gain, uint32_t blue_gain) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = (src->h / VBAYER_Y_STRIDE) * VBAYER_Y_STRIDE; y < y_end; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint16_t *p0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
uint16_t *p1 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(buf, ((y + 1) % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_bggr(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_BGGR shifted down by 1 becomes PIXFORMAT_BAYER_GRBG
vrgb_pixels_t pixels1 = vdebayer_grbg(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_rgb565(p1, 0, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
rows.r3 = vldr_u32_gather_unaligned(rowptrs.p3.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_bggr(rows.r0, rows.r1, rows.r2);
vstr_u16(p0 + x, vrgb_pixels_to_rgb565(vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain)));
// PIXFORMAT_BAYER_BGGR shifted down by 1 becomes PIXFORMAT_BAYER_GRBG
vrgb_pixels_t pixels1 = vdebayer_grbg(rows.r1, rows.r2, rows.r3);
vstr_u16(p1 + x, vrgb_pixels_to_rgb565(vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain)));
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_bggr(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_BGGR shifted down by 1 becomes PIXFORMAT_BAYER_GRBG
vrgb_pixels_t pixels1 = vdebayer_grbg(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_rgb565(p1, x, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
}
vdebayer_rgb565_buf_copy(y, buf, dst);
}
// Last odd row.
if (src->h % VBAYER_Y_STRIDE) {
int32_t y = src->h - 1;
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint16_t *p0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_bggr(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_bggr(rows.r0, rows.r1, rows.r2);
vstr_u16(p0 + x, vrgb_pixels_to_rgb565(vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain)));
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_bggr(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
}
vdebayer_rgb565_buf_copy(y, buf, dst);
}
}
static void vdebayer_gbrg_to_rgb565_awb(image_t *src, image_t *dst, image_t *buf,
uint32_t red_gain, uint32_t blue_gain) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = (src->h / VBAYER_Y_STRIDE) * VBAYER_Y_STRIDE; y < y_end; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint16_t *p0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
uint16_t *p1 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(buf, ((y + 1) % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_gbrg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_GBRG shifted down by 1 becomes PIXFORMAT_BAYER_RGGB
vrgb_pixels_t pixels1 = vdebayer_rggb(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_rgb565(p1, 0, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
rows.r3 = vldr_u32_gather_unaligned(rowptrs.p3.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_gbrg(rows.r0, rows.r1, rows.r2);
vstr_u16(p0 + x, vrgb_pixels_to_rgb565(vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain)));
// PIXFORMAT_BAYER_GBRG shifted down by 1 becomes PIXFORMAT_BAYER_RGGB
vrgb_pixels_t pixels1 = vdebayer_rggb(rows.r1, rows.r2, rows.r3);
vstr_u16(p1 + x, vrgb_pixels_to_rgb565(vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain)));
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_gbrg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_GBRG shifted down by 1 becomes PIXFORMAT_BAYER_RGGB
vrgb_pixels_t pixels1 = vdebayer_rggb(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_rgb565(p1, x, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
}
vdebayer_rgb565_buf_copy(y, buf, dst);
}
// Last odd row.
if (src->h % VBAYER_Y_STRIDE) {
int32_t y = src->h - 1;
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint16_t *p0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_gbrg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_gbrg(rows.r0, rows.r1, rows.r2);
vstr_u16(p0 + x, vrgb_pixels_to_rgb565(vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain)));
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_gbrg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
}
vdebayer_rgb565_buf_copy(y, buf, dst);
}
}
static void vdebayer_grbg_to_rgb565_awb(image_t *src, image_t *dst, image_t *buf,
uint32_t red_gain, uint32_t blue_gain) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = (src->h / VBAYER_Y_STRIDE) * VBAYER_Y_STRIDE; y < y_end; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint16_t *p0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
uint16_t *p1 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(buf, ((y + 1) % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_grbg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_GRBG shifted down by 1 becomes PIXFORMAT_BAYER_BGGR
vrgb_pixels_t pixels1 = vdebayer_bggr(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_rgb565(p1, 0, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
rows.r3 = vldr_u32_gather_unaligned(rowptrs.p3.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_grbg(rows.r0, rows.r1, rows.r2);
vstr_u16(p0 + x, vrgb_pixels_to_rgb565(vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain)));
// PIXFORMAT_BAYER_GRBG shifted down by 1 becomes PIXFORMAT_BAYER_BGGR
vrgb_pixels_t pixels1 = vdebayer_bggr(rows.r1, rows.r2, rows.r3);
vstr_u16(p1 + x, vrgb_pixels_to_rgb565(vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain)));
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_grbg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_GRBG shifted down by 1 becomes PIXFORMAT_BAYER_BGGR
vrgb_pixels_t pixels1 = vdebayer_bggr(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_rgb565(p1, x, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
}
vdebayer_rgb565_buf_copy(y, buf, dst);
}
// Last odd row.
if (src->h % VBAYER_Y_STRIDE) {
int32_t y = src->h - 1;
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint16_t *p0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_grbg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_grbg(rows.r0, rows.r1, rows.r2);
vstr_u16(p0 + x, vrgb_pixels_to_rgb565(vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain)));
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_grbg(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
}
vdebayer_rgb565_buf_copy(y, buf, dst);
}
}
static void vdebayer_rggb_to_rgb565_awb(image_t *src, image_t *dst, image_t *buf,
uint32_t red_gain, uint32_t blue_gain) {
// Load pixels, but, each set of 4 pixels overlaps the previous by 2 pixels.
v128_t offsets = vidup_u32_unaligned(0, 2);
for (int32_t y = 0, y_end = (src->h / VBAYER_Y_STRIDE) * VBAYER_Y_STRIDE; y < y_end; y += VBAYER_Y_STRIDE) {
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint16_t *p0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
uint16_t *p1 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(buf, ((y + 1) % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_rggb(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_RGGB shifted down by 1 becomes PIXFORMAT_BAYER_GBRG
vrgb_pixels_t pixels1 = vdebayer_gbrg(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_rgb565(p1, 0, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
rows.r3 = vldr_u32_gather_unaligned(rowptrs.p3.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_rggb(rows.r0, rows.r1, rows.r2);
vstr_u16(p0 + x, vrgb_pixels_to_rgb565(vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain)));
// PIXFORMAT_BAYER_RGGB shifted down by 1 becomes PIXFORMAT_BAYER_GBRG
vrgb_pixels_t pixels1 = vdebayer_gbrg(rows.r1, rows.r2, rows.r3);
vstr_u16(p1 + x, vrgb_pixels_to_rgb565(vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain)));
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_rggb(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
// PIXFORMAT_BAYER_RGGB shifted down by 1 becomes PIXFORMAT_BAYER_GBRG
vrgb_pixels_t pixels1 = vdebayer_gbrg(rows.r1, rows.r2, rows.r3);
vrgb_pixels_store_rgb565(p1, x, vdebayer_apply_rb_gain(pixels1, red_gain, blue_gain), pred);
}
vdebayer_rgb565_buf_copy(y, buf, dst);
}
// Last odd row.
if (src->h % VBAYER_Y_STRIDE) {
int32_t y = src->h - 1;
v4x_row_ptrs_t rowptrs = vdebayer_rowptrs_init(src, y);
uint16_t *p0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(buf, (y % VBAYER_BUF_BROWS));
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, 0, offsets, vdebayer_load_pred(src, 0));
v128_predicate_t pred = vdebayer_store_pred(src->w, 0);
vrgb_pixels_t pixels0 = vdebayer_rggb(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, 0, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
int32_t x = VBAYER_X_STRIDE;
for (int32_t x_end = src->w - VBAYER_X_STRIDE; x < x_end; x += VBAYER_X_STRIDE) {
v4x_rows_t rows;
rows.r0 = vldr_u32_gather_unaligned(rowptrs.p0.u8 + x, offsets);
rows.r1 = vldr_u32_gather_unaligned(rowptrs.p1.u8 + x, offsets);
rows.r2 = vldr_u32_gather_unaligned(rowptrs.p2.u8 + x, offsets);
vrgb_pixels_t pixels0 = vdebayer_rggb(rows.r0, rows.r1, rows.r2);
vstr_u16(p0 + x, vrgb_pixels_to_rgb565(vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain)));
}
if (x < src->w) {
v4x_rows_t rows = vdebayer_load_rows_inner(rowptrs, x, offsets, vdebayer_load_pred(src, x));
v128_predicate_t pred = vdebayer_store_pred(src->w, x);
vrgb_pixels_t pixels0 = vdebayer_rggb(rows.r0, rows.r1, rows.r2);
vrgb_pixels_store_rgb565(p0, x, vdebayer_apply_rb_gain(pixels0, red_gain, blue_gain), pred);
}
vdebayer_rgb565_buf_copy(y, buf, dst);
}
}
static void vdebayer_bggr_to_grayscale_awb_quarter(image_t *src, image_t *dst, uint32_t red_gain, uint32_t blue_gain) {
for (int32_t y = 0; y < src->h; y += VBAYER_Y_STRIDE) {
v2x_row_ptrs_t rowptrs = vdebayer_quarter_rowptrs_init(src, y);
uint8_t *p = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(dst, (y / 2));
for (int32_t x = 0; x < src->w; x += VBAYER_X_STRIDE_2X2) {
int32_t len = src->w - x;
v2x_rows_t bg, gr;
if (len >= VBAYER_X_STRIDE_2X2) {
bg = vld2_u8(rowptrs.p0.u8 + x);
gr = vld2_u8(rowptrs.p1.u8 + x);
} else {
bg = vld2_u8_len(rowptrs.p0.u8 + x, len);
gr = vld2_u8_len(rowptrs.p1.u8 + x, len);
}
vrgb_pixels_t packed = {
.r = gr.r1,
.g = vhadd_u8(gr.r0, bg.r1),
.b = bg.r0
};
vdebayer_store_packed_grayscale(p + (x / 2), packed, red_gain, blue_gain, dst->w - (x / 2));
}
}
}
static void vdebayer_gbrg_to_grayscale_awb_quarter(image_t *src, image_t *dst, uint32_t red_gain, uint32_t blue_gain) {
for (int32_t y = 0; y < src->h; y += VBAYER_Y_STRIDE) {
v2x_row_ptrs_t rowptrs = vdebayer_quarter_rowptrs_init(src, y);
uint8_t *p = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(dst, (y / 2));
for (int32_t x = 0; x < src->w; x += VBAYER_X_STRIDE_2X2) {
int32_t len = src->w - x;
v2x_rows_t gb, rg;
if (len >= VBAYER_X_STRIDE_2X2) {
gb = vld2_u8(rowptrs.p0.u8 + x);
rg = vld2_u8(rowptrs.p1.u8 + x);
} else {
gb = vld2_u8_len(rowptrs.p0.u8 + x, len);
rg = vld2_u8_len(rowptrs.p1.u8 + x, len);
}
vrgb_pixels_t packed = {
.r = rg.r0,
.g = vhadd_u8(gb.r0, rg.r1),
.b = gb.r1
};
vdebayer_store_packed_grayscale(p + (x / 2), packed, red_gain, blue_gain, dst->w - (x / 2));
}
}
}
static void vdebayer_grbg_to_grayscale_awb_quarter(image_t *src, image_t *dst, uint32_t red_gain, uint32_t blue_gain) {
for (int32_t y = 0; y < src->h; y += VBAYER_Y_STRIDE) {
v2x_row_ptrs_t rowptrs = vdebayer_quarter_rowptrs_init(src, y);
uint8_t *p = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(dst, (y / 2));
for (int32_t x = 0; x < src->w; x += VBAYER_X_STRIDE_2X2) {
int32_t len = src->w - x;
v2x_rows_t gr, bg;
if (len >= VBAYER_X_STRIDE_2X2) {
gr = vld2_u8(rowptrs.p0.u8 + x);
bg = vld2_u8(rowptrs.p1.u8 + x);
} else {
gr = vld2_u8_len(rowptrs.p0.u8 + x, len);
bg = vld2_u8_len(rowptrs.p1.u8 + x, len);
}
vrgb_pixels_t packed = {
.r = gr.r1,
.g = vhadd_u8(gr.r0, bg.r1),
.b = bg.r0
};
vdebayer_store_packed_grayscale(p + (x / 2), packed, red_gain, blue_gain, dst->w - (x / 2));
}
}
}
static void vdebayer_rggb_to_grayscale_awb_quarter(image_t *src, image_t *dst, uint32_t red_gain, uint32_t blue_gain) {
for (int32_t y = 0; y < src->h; y += VBAYER_Y_STRIDE) {
v2x_row_ptrs_t rowptrs = vdebayer_quarter_rowptrs_init(src, y);
uint8_t *p = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(dst, (y / 2));
for (int32_t x = 0; x < src->w; x += VBAYER_X_STRIDE_2X2) {
int32_t len = src->w - x;
v2x_rows_t rg, gb;
if (len >= VBAYER_X_STRIDE_2X2) {
rg = vld2_u8(rowptrs.p0.u8 + x);
gb = vld2_u8(rowptrs.p1.u8 + x);
} else {
rg = vld2_u8_len(rowptrs.p0.u8 + x, len);
gb = vld2_u8_len(rowptrs.p1.u8 + x, len);
}
vrgb_pixels_t packed = {
.r = rg.r0,
.g = vhadd_u8(gb.r0, rg.r1),
.b = gb.r1
};
vdebayer_store_packed_grayscale(p + (x / 2), packed, red_gain, blue_gain, dst->w - (x / 2));
}
}
}
static void vdebayer_bggr_to_rgb565_awb_quarter(image_t *src, image_t *dst, uint32_t red_gain, uint32_t blue_gain) {
for (int32_t y = 0; y < src->h; y += VBAYER_Y_STRIDE) {
v2x_row_ptrs_t rowptrs = vdebayer_quarter_rowptrs_init(src, y);
uint16_t *p = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(dst, (y / 2));
for (int32_t x = 0; x < src->w; x += VBAYER_X_STRIDE_2X2) {
int32_t len = src->w - x;
v2x_rows_t bg, gr;
if (len >= VBAYER_X_STRIDE_2X2) {
bg = vld2_u8(rowptrs.p0.u8 + x);
gr = vld2_u8(rowptrs.p1.u8 + x);
} else {
bg = vld2_u8_len(rowptrs.p0.u8 + x, len);
gr = vld2_u8_len(rowptrs.p1.u8 + x, len);
}
vrgb_pixels_t packed = {
.r = gr.r1,
.g = vhadd_u8(gr.r0, bg.r1),
.b = bg.r0
};
vdebayer_store_packed_rgb565(p + (x / 2), packed, red_gain, blue_gain, dst->w - (x / 2));
}
}
}
static void vdebayer_gbrg_to_rgb565_awb_quarter(image_t *src, image_t *dst, uint32_t red_gain, uint32_t blue_gain) {
for (int32_t y = 0; y < src->h; y += VBAYER_Y_STRIDE) {
v2x_row_ptrs_t rowptrs = vdebayer_quarter_rowptrs_init(src, y);
uint16_t *p = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(dst, (y / 2));
for (int32_t x = 0; x < src->w; x += VBAYER_X_STRIDE_2X2) {
int32_t len = src->w - x;
v2x_rows_t gb, rg;
if (len >= VBAYER_X_STRIDE_2X2) {
gb = vld2_u8(rowptrs.p0.u8 + x);
rg = vld2_u8(rowptrs.p1.u8 + x);
} else {
gb = vld2_u8_len(rowptrs.p0.u8 + x, len);
rg = vld2_u8_len(rowptrs.p1.u8 + x, len);
}
vrgb_pixels_t packed = {
.r = rg.r0,
.g = vhadd_u8(gb.r0, rg.r1),
.b = gb.r1
};
vdebayer_store_packed_rgb565(p + (x / 2), packed, red_gain, blue_gain, dst->w - (x / 2));
}
}
}
static void vdebayer_grbg_to_rgb565_awb_quarter(image_t *src, image_t *dst, uint32_t red_gain, uint32_t blue_gain) {
for (int32_t y = 0; y < src->h; y += VBAYER_Y_STRIDE) {
v2x_row_ptrs_t rowptrs = vdebayer_quarter_rowptrs_init(src, y);
uint16_t *p = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(dst, (y / 2));
for (int32_t x = 0; x < src->w; x += VBAYER_X_STRIDE_2X2) {
int32_t len = src->w - x;
v2x_rows_t gr, bg;
if (len >= VBAYER_X_STRIDE_2X2) {
gr = vld2_u8(rowptrs.p0.u8 + x);
bg = vld2_u8(rowptrs.p1.u8 + x);
} else {
gr = vld2_u8_len(rowptrs.p0.u8 + x, len);
bg = vld2_u8_len(rowptrs.p1.u8 + x, len);
}
vrgb_pixels_t packed = {
.r = gr.r1,
.g = vhadd_u8(gr.r0, bg.r1),
.b = bg.r0
};
vdebayer_store_packed_rgb565(p + (x / 2), packed, red_gain, blue_gain, dst->w - (x / 2));
}
}
}
static void vdebayer_rggb_to_rgb565_awb_quarter(image_t *src, image_t *dst, uint32_t red_gain, uint32_t blue_gain) {
for (int32_t y = 0; y < src->h; y += VBAYER_Y_STRIDE) {
v2x_row_ptrs_t rowptrs = vdebayer_quarter_rowptrs_init(src, y);
uint16_t *p = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(dst, (y / 2));
for (int32_t x = 0; x < src->w; x += VBAYER_X_STRIDE_2X2) {
int32_t len = src->w - x;
v2x_rows_t rg, gb;
if (len >= VBAYER_X_STRIDE_2X2) {
rg = vld2_u8(rowptrs.p0.u8 + x);
gb = vld2_u8(rowptrs.p1.u8 + x);
} else {
rg = vld2_u8_len(rowptrs.p0.u8 + x, len);
gb = vld2_u8_len(rowptrs.p1.u8 + x, len);
}
vrgb_pixels_t packed = {
.r = rg.r0,
.g = vhadd_u8(gb.r0, rg.r1),
.b = gb.r1
};
vdebayer_store_packed_rgb565(p + (x / 2), packed, red_gain, blue_gain, dst->w - (x / 2));
}
}
}
// assumes dst->w == src->w (or dst->w == src->w / 2)
// assumes dst->h == src->h (or dst->h == src->h / 2)
// src and dst may overlap, but, slower than imlib_debayer_image
// BINARY: Not supported
// GRAYSCALE: src->data == dst->data
// RGB565: src->data == dst->data + image_size(src)
// YUV422: Not supported
void imlib_debayer_image_awb(image_t *dst, image_t *src, bool fast, uint32_t r_out, uint32_t g_out, uint32_t b_out,
float brightness, float contrast, float gamma) {
uint32_t red_gain = IM_DIV(g_out * 32, r_out);
red_gain = IM_MIN(red_gain, 128U);
uint32_t blue_gain = IM_DIV(g_out * 32, b_out);
blue_gain = IM_MIN(blue_gain, 128U);
for (int i = 0; i < 256; i++) {
int v = fast_roundf(((fast_powf(i * (1.0f / 255.0f), gamma) * contrast) + brightness) * 255.0f);
gamma_lut[i] = IM_CLAMP(v, 0, 255);
}
if (fast) {
switch (src->pixfmt) {
case PIXFORMAT_BAYER_BGGR: {
switch (dst->pixfmt) {
case PIXFORMAT_GRAYSCALE: {
vdebayer_bggr_to_grayscale_awb_quarter(src, dst, red_gain, blue_gain);
break;
}
case PIXFORMAT_RGB565: {
vdebayer_bggr_to_rgb565_awb_quarter(src, dst, red_gain, blue_gain);
break;
}
default: {
__builtin_unreachable();
}
}
break;
}
case PIXFORMAT_BAYER_GBRG: {
switch (dst->pixfmt) {
case PIXFORMAT_GRAYSCALE: {
vdebayer_gbrg_to_grayscale_awb_quarter(src, dst, red_gain, blue_gain);
break;
}
case PIXFORMAT_RGB565: {
vdebayer_gbrg_to_rgb565_awb_quarter(src, dst, red_gain, blue_gain);
break;
}
default: {
__builtin_unreachable();
}
}
break;
}
case PIXFORMAT_BAYER_GRBG: {
switch (dst->pixfmt) {
case PIXFORMAT_GRAYSCALE: {
vdebayer_grbg_to_grayscale_awb_quarter(src, dst, red_gain, blue_gain);
break;
}
case PIXFORMAT_RGB565: {
vdebayer_grbg_to_rgb565_awb_quarter(src, dst, red_gain, blue_gain);
break;
}
default: {
__builtin_unreachable();
}
}
break;
}
case PIXFORMAT_BAYER_RGGB: {
switch (dst->pixfmt) {
case PIXFORMAT_GRAYSCALE: {
vdebayer_rggb_to_grayscale_awb_quarter(src, dst, red_gain, blue_gain);
break;
}
case PIXFORMAT_RGB565: {
vdebayer_rggb_to_rgb565_awb_quarter(src, dst, red_gain, blue_gain);
break;
}
default: {
__builtin_unreachable();
}
}
break;
}
default: {
__builtin_unreachable();
}
}
} else {
switch (dst->pixfmt) {
case PIXFORMAT_GRAYSCALE: {
image_t buf = {
.w = dst->w,
.h = VBAYER_BUF_BROWS,
.pixfmt = PIXFORMAT_GRAYSCALE,
.data = fb_alloc(dst->w * VBAYER_BUF_BROWS * sizeof(uint8_t), FB_ALLOC_PREFER_SPEED),
};
switch (src->pixfmt) {
case PIXFORMAT_BAYER_BGGR: {
vdebayer_bggr_to_grayscale_awb(src, dst, &buf, red_gain, blue_gain);
break;
}
case PIXFORMAT_BAYER_GBRG: {
vdebayer_gbrg_to_grayscale_awb(src, dst, &buf, red_gain, blue_gain);
break;
}
case PIXFORMAT_BAYER_GRBG: {
vdebayer_grbg_to_grayscale_awb(src, dst, &buf, red_gain, blue_gain);
break;
}
case PIXFORMAT_BAYER_RGGB: {
vdebayer_rggb_to_grayscale_awb(src, dst, &buf, red_gain, blue_gain);
break;
}
default: {
__builtin_unreachable();
}
}
// Copy any remaining lines from the buffer image...
for (int32_t y = IM_MAX(dst->h - VBAYER_BUF_KSIZE, 0); y < dst->h; y++) {
vmemcpy_8(IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(dst, y),
IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, (y % VBAYER_BUF_BROWS)),
IMAGE_GRAYSCALE_LINE_LEN_BYTES(dst));
}
fb_free(); // buf.data
break;
}
case PIXFORMAT_RGB565: {
image_t buf = {
.w = dst->w,
.h = VBAYER_BUF_BROWS,
.pixfmt = PIXFORMAT_RGB565,
.data = fb_alloc(dst->w * VBAYER_BUF_BROWS * sizeof(uint16_t), FB_ALLOC_PREFER_SPEED),
};
switch (src->pixfmt) {
case PIXFORMAT_BAYER_BGGR: {
vdebayer_bggr_to_rgb565_awb(src, dst, &buf, red_gain, blue_gain);
break;
}
case PIXFORMAT_BAYER_GBRG: {
vdebayer_gbrg_to_rgb565_awb(src, dst, &buf, red_gain, blue_gain);
break;
}
case PIXFORMAT_BAYER_GRBG: {
vdebayer_grbg_to_rgb565_awb(src, dst, &buf, red_gain, blue_gain);
break;
}
case PIXFORMAT_BAYER_RGGB: {
vdebayer_rggb_to_rgb565_awb(src, dst, &buf, red_gain, blue_gain);
break;
}
default: {
__builtin_unreachable();
}
}
// Copy any remaining lines from the buffer image...
for (int32_t y = IM_MAX(dst->h - VBAYER_BUF_KSIZE, 0); y < dst->h; y++) {
vmemcpy_16(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(dst, y),
IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, (y % VBAYER_BUF_BROWS)),
IMAGE_RGB565_LINE_LEN_BYTES(dst));
}
fb_free(); // buf.data
break;
}
default: {
__builtin_unreachable();
}
}
}
}