1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * Contains implemenation of framebuffer conversion routines.
19 */
20
21#define LOG_NDEBUG 0
22#define LOG_TAG "EmulatedCamera_Converter"
23#include <cutils/log.h>
24#include "Converters.h"
25
26#include "Alignment.h"
27
28namespace android {
29
30static void _YUV420SToRGB565(const uint8_t* Y,
31                             const uint8_t* U,
32                             const uint8_t* V,
33                             int dUV,
34                             uint16_t* rgb,
35                             int width,
36                             int height,
37                             int y_stride,
38                             int uv_stride)
39{
40    const uint8_t* Y_pos = Y;
41    const uint8_t* U_pos = U;
42    const uint8_t* V_pos = V;
43
44    for (int y = 0; y < height; y++) {
45        Y = Y_pos + y_stride * y;
46        U = U_pos + uv_stride * (y / 2);
47        V = V_pos + uv_stride * (y / 2);
48        for (int x = 0; x < width; x += 2, U += dUV, V += dUV) {
49            const uint8_t nU = *U;
50            const uint8_t nV = *V;
51            *rgb = YUVToRGB565(*Y, nU, nV);
52            Y++; rgb++;
53            *rgb = YUVToRGB565(*Y, nU, nV);
54            Y++; rgb++;
55        }
56    }
57}
58
59static void _YUV420SToRGB32(const uint8_t* Y,
60                            const uint8_t* U,
61                            const uint8_t* V,
62                            int dUV,
63                            uint32_t* rgb,
64                            int width,
65                            int height,
66                            int y_stride,
67                            int uv_stride)
68{
69    const uint8_t* Y_pos = Y;
70    const uint8_t* U_pos = U;
71    const uint8_t* V_pos = V;
72
73    for (int y = 0; y < height; y++) {
74        Y = Y_pos + y_stride * y;
75        U = U_pos + uv_stride * (y / 2);
76        V = V_pos + uv_stride * (y / 2);
77        for (int x = 0; x < width; x += 2, U += dUV, V += dUV) {
78            const uint8_t nU = *U;
79            const uint8_t nV = *V;
80            *rgb = YUVToRGB32(*Y, nU, nV);
81            Y++; rgb++;
82            *rgb = YUVToRGB32(*Y, nU, nV);
83            Y++; rgb++;
84        }
85    }
86}
87
88/* The YV12 and YU12 formats require that the row strides are aligned to 16 byte
89 * boundaries as per the format specification at:
90 * https://developer.android.com/reference/android/graphics/ImageFormat.html#YV12
91 *
92 * This means that we can't just use the width or assume that pixels are
93 * tightly packed, we have to calculate aligned strides and use them to find the
94 * next row.
95 */
96void YV12ToRGB565(const void* yv12, void* rgb, int width, int height)
97{
98    // See note above about alignment
99    const int y_stride = align(width, 16);
100    const int uv_stride = align(y_stride / 2, 16);
101    const uint8_t* Y = reinterpret_cast<const uint8_t*>(yv12);
102    const uint8_t* U = Y + y_stride * height;
103    const uint8_t* V = U + uv_stride * (height / 2);
104    _YUV420SToRGB565(Y, U, V, 1, reinterpret_cast<uint16_t*>(rgb),
105                     width, height, y_stride, uv_stride);
106}
107
108void YV12ToRGB32(const void* yv12, void* rgb, int width, int height)
109{
110    // See note above about alignment
111    const int y_stride = align(width, 16);
112    const int uv_stride = align(y_stride / 2, 16);
113    const uint8_t* Y = reinterpret_cast<const uint8_t*>(yv12);
114    const uint8_t* V = Y + y_stride * height;
115    const uint8_t* U = V + uv_stride * (height / 2);
116    _YUV420SToRGB32(Y, U, V, 1, reinterpret_cast<uint32_t*>(rgb), width, height,
117                    y_stride, uv_stride);
118}
119
120void YU12ToRGB32(const void* yu12, void* rgb, int width, int height)
121{
122    // See note above about alignment
123    const int y_stride = align(width, 16);
124    const int uv_stride = align(y_stride / 2, 16);
125    const uint8_t* Y = reinterpret_cast<const uint8_t*>(yu12);
126    const uint8_t* U = Y + y_stride * height;
127    const uint8_t* V = U + uv_stride * (height / 2);
128    _YUV420SToRGB32(Y, U, V, 1, reinterpret_cast<uint32_t*>(rgb), width, height,
129                    y_stride, uv_stride);
130}
131
132/* Common converter for YUV 4:2:0 interleaved to RGB565.
133 * y, u, and v point to Y,U, and V panes, where U and V values are interleaved.
134 */
135static void _NVXXToRGB565(const uint8_t* Y,
136                          const uint8_t* U,
137                          const uint8_t* V,
138                          uint16_t* rgb,
139                          int width,
140                          int height)
141{
142    // The UV stride for NV21 and NV12 is the same as the width because the
143    // U and V values are interleaved, making each row twice as wide even though
144    // each value covers a two pixel wide area. These formats do not require any
145    // kind of alignment.
146    int y_stride = width;
147    int uv_stride = width;
148    _YUV420SToRGB565(Y, U, V, 2, rgb, width, height, y_stride, uv_stride);
149}
150
151/* Common converter for YUV 4:2:0 interleaved to RGB32.
152 * y, u, and v point to Y,U, and V panes, where U and V values are interleaved.
153 */
154static void _NVXXToRGB32(const uint8_t* Y,
155                         const uint8_t* U,
156                         const uint8_t* V,
157                         uint32_t* rgb,
158                         int width,
159                         int height)
160{
161    // The UV stride for NV21 and NV12 is the same as the width because the
162    // U and V values are interleaved, making each row twice as wide even though
163    // each value covers a two pixel wide area. These formats do not require any
164    // kind of alignment.
165    int y_stride = width;
166    int uv_stride = width;
167    _YUV420SToRGB32(Y, U, V, 2, rgb, width, height, y_stride, uv_stride);
168}
169
170void NV12ToRGB565(const void* nv12, void* rgb, int width, int height)
171{
172    const int pix_total = width * height;
173    const uint8_t* y = reinterpret_cast<const uint8_t*>(nv12);
174    _NVXXToRGB565(y, y + pix_total, y + pix_total + 1,
175                  reinterpret_cast<uint16_t*>(rgb), width, height);
176}
177
178void NV12ToRGB32(const void* nv12, void* rgb, int width, int height)
179{
180    const int pix_total = width * height;
181    const uint8_t* y = reinterpret_cast<const uint8_t*>(nv12);
182    _NVXXToRGB32(y, y + pix_total, y + pix_total + 1,
183                 reinterpret_cast<uint32_t*>(rgb), width, height);
184}
185
186void NV21ToRGB565(const void* nv21, void* rgb, int width, int height)
187{
188    const int pix_total = width * height;
189    const uint8_t* y = reinterpret_cast<const uint8_t*>(nv21);
190    _NVXXToRGB565(y, y + pix_total + 1, y + pix_total,
191                  reinterpret_cast<uint16_t*>(rgb), width, height);
192}
193
194void NV21ToRGB32(const void* nv21, void* rgb, int width, int height)
195{
196    const int pix_total = width * height;
197    const uint8_t* y = reinterpret_cast<const uint8_t*>(nv21);
198    _NVXXToRGB32(y, y + pix_total + 1, y + pix_total,
199                 reinterpret_cast<uint32_t*>(rgb), width, height);
200}
201
202}; /* namespace android */
203