1#pragma version(1)
2#pragma rs java_package_name(com.android.testingcamera)
3#pragma rs_fp_relaxed
4
5uchar *yuv_in;
6
7// Input globals
8uint32_t yuv_height;
9uint32_t yuv_width;
10uint32_t out_width;
11uint32_t out_height;
12// Derived globals
13uint32_t y_stride;
14uint32_t uv_stride;
15uint32_t u_start;
16uint32_t v_start;
17float x_scale;
18float y_scale;
19
20static const float CLAMP_MIN = 0;
21static const float CLAMP_MAX = 255;
22
23/**
24 * JFIF standard YCbCr <-> RGB conversion matrix,
25 * column-major order.
26 */
27static const float YUV2RGB[] = {
28    1.0f, 1.0f, 1.0f, 0.0f,
29    0.0f, -0.34414f, 1.772f, 0.0f,
30    1.402f, -0.71414f, 0.0f, 0.0f,
31    -0.701f, 0.529f, -0.886f, 1.0f
32};
33rs_matrix4x4 yuv2rgb_matrix;
34
35enum ImageFormat {
36    NV16 = 16,
37    NV21 = 17,
38    RGB_565 = 4,
39    UNKNOWN = 0,
40    YUY2 = 20,
41    YV12 = 0x32315659
42};
43
44// Must be called before using any conversion methods
45void init_convert(uint32_t yw, uint32_t yh, uint32_t format,
46        uint32_t ow, uint32_t oh) {
47    yuv_height = yh;
48    yuv_width = yw;
49    out_width = ow;
50    out_height = oh;
51    rsMatrixLoad(&yuv2rgb_matrix, YUV2RGB);
52
53    x_scale = (float)yuv_width / out_width;
54    y_scale = (float)yuv_height / out_height;
55
56    switch (format) {
57    case NV16:
58    case NV21:
59        y_stride = yuv_width;
60        uv_stride = yuv_width;
61        v_start = y_stride * yuv_height;
62        u_start = v_start + 1;
63        break;
64    case YV12:
65        // Minimum align-16 stride
66        y_stride = (yuv_width + 0xF) & ~0xF;
67        uv_stride = (y_stride / 2 + 0xF) & ~0xF;
68        v_start = y_stride * yuv_height;
69        u_start = v_start + uv_stride * (yuv_height / 2);
70        break;
71    case YUY2:
72        y_stride = yuv_width * 2;
73        uv_stride = y_stride;
74        u_start = 1;
75        v_start = 3;
76        break;
77    case RGB_565:
78    case UNKNOWN:
79    default:
80        y_stride = yuv_width;
81        uv_stride = yuv_width;
82        v_start = 0;
83        u_start = 0;
84    }
85}
86
87// Multiply by color matrix and clamp to range [0, 255]
88static inline uchar4 multiply_and_clamp(const rs_matrix4x4* mat, uchar4 input) {
89    float4 intermediate = convert_float4(input);
90    intermediate = rsMatrixMultiply(mat, intermediate);
91    intermediate = clamp(intermediate, CLAMP_MIN, CLAMP_MAX);
92    return convert_uchar4(intermediate);
93}
94
95// Makes up a conversion for unknown YUV types to try to display something
96// Asssumes that there's at least 1bpp in input YUV data
97uchar4 __attribute__((kernel)) convert_unknown(uint32_t x, uint32_t y) {
98    uint32_t x_scaled = x * x_scale;
99    uint32_t y_scaled = y * y_scale;
100
101    uchar4 out;
102    out.r = yuv_in[y_stride * y_scaled + x_scaled];
103    out.g = 128;
104    out.b = 128;
105    out.a = 255; // For affine transform later
106
107    // Apply yuv->rgb color transform
108    return multiply_and_clamp(&yuv2rgb_matrix, out);
109}
110
111// Converts semiplanar YVU to interleaved YUV, nearest neighbor
112uchar4 __attribute__((kernel)) convert_semiplanar(uint32_t x, uint32_t y) {
113    uint32_t x_scaled = x * x_scale;
114    uint32_t y_scaled = y * y_scale;
115
116    uint32_t uv_row = y_scaled / 2; // truncation is important here
117    uint32_t uv_col = x_scaled & ~0x1;
118    uint32_t vu_pixel = uv_row * uv_stride + uv_col;
119
120    uchar4 out;
121    out.r = yuv_in[y_stride * y_scaled + x_scaled];
122    out.g = yuv_in[u_start + vu_pixel];
123    out.b = yuv_in[v_start + vu_pixel];
124    out.a = 255; // For affine transform later
125
126    // Apply yuv->rgb color transform
127    return multiply_and_clamp(&yuv2rgb_matrix, out);
128}
129
130// Converts planar YVU to interleaved YUV, nearest neighbor
131uchar4 __attribute__((kernel)) convert_planar(uint32_t x, uint32_t y) {
132    uint32_t x_scaled = x * x_scale;
133    uint32_t y_scaled = y * y_scale;
134
135    uint32_t uv_row = y_scaled / 2; // truncation is important here
136    uint32_t vu_pixel = uv_stride * uv_row + x_scaled / 2;
137
138    uchar4 out;
139    out.r = yuv_in[y_stride * y_scaled + x_scaled];
140    out.g = yuv_in[u_start + vu_pixel];
141    out.b = yuv_in[v_start + vu_pixel];
142    out.a = 255; // For affine transform later
143
144    // Apply yuv->rgb color transform
145    return multiply_and_clamp(&yuv2rgb_matrix, out);
146}
147
148// Converts interleaved 4:2:2 YUV to interleaved YUV, nearest neighbor
149uchar4 __attribute__((kernel)) convert_interleaved(uint32_t x, uint32_t y) {
150    uint32_t x_scaled = x * x_scale;
151    uint32_t y_scaled = y * y_scale;
152
153    uint32_t uv_col = 2 * (x_scaled & ~0x1);
154    uint32_t vu_pixel = y_stride * y_scaled + uv_col;
155
156    uchar4 out;
157    out.r = yuv_in[y_stride * y_scaled + x_scaled * 2];
158    out.g = yuv_in[u_start + vu_pixel];
159    out.b = yuv_in[v_start + vu_pixel];
160    out.a = 255; // For affine transform later
161
162    // Apply yuv->rgb color transform
163    return multiply_and_clamp(&yuv2rgb_matrix, out);
164}
165