| // Copyright 2016 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <hardware/gralloc.h> |
| #include "FormatConversions.h" |
| |
| #if PLATFORM_SDK_VERSION < 26 |
| #include <cutils/log.h> |
| #else |
| #include <log/log.h> |
| #endif |
| #include <string.h> |
| #include <stdio.h> |
| |
| #define DEBUG 0 |
| |
| #if DEBUG |
| #define DD(...) ALOGD(__VA_ARGS__) |
| #else |
| #define DD(...) |
| #endif |
| |
| static int get_rgb_offset(int row, int width, int rgbStride) { |
| return row * width * rgbStride; |
| } |
| |
| #define OMX_COLOR_FormatYUV420Planar 0x13 |
| |
| bool gralloc_is_yuv_format(const int format) { |
| switch (format) { |
| case HAL_PIXEL_FORMAT_YV12: |
| case HAL_PIXEL_FORMAT_YCbCr_420_888: |
| case HAL_PIXEL_FORMAT_YCrCb_420_SP: |
| case OMX_COLOR_FormatYUV420Planar: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| void get_yv12_offsets(int width, int height, |
| uint32_t* yStride_out, |
| uint32_t* cStride_out, |
| uint32_t* totalSz_out) { |
| uint32_t align = 16; |
| uint32_t yStride = (width + (align - 1)) & ~(align-1); |
| uint32_t uvStride = (yStride / 2 + (align - 1)) & ~(align-1); |
| uint32_t uvHeight = height / 2; |
| uint32_t sz = yStride * height + 2 * (uvHeight * uvStride); |
| |
| if (yStride_out) *yStride_out = yStride; |
| if (cStride_out) *cStride_out = uvStride; |
| if (totalSz_out) *totalSz_out = sz; |
| } |
| |
| void get_yuv420p_offsets(int width, int height, |
| uint32_t* yStride_out, |
| uint32_t* cStride_out, |
| uint32_t* totalSz_out) { |
| uint32_t align = 1; |
| uint32_t yStride = (width + (align - 1)) & ~(align-1); |
| uint32_t uvStride = (yStride / 2 + (align - 1)) & ~(align-1); |
| uint32_t uvHeight = height / 2; |
| uint32_t sz = yStride * height + 2 * (uvHeight * uvStride); |
| |
| if (yStride_out) *yStride_out = yStride; |
| if (cStride_out) *cStride_out = uvStride; |
| if (totalSz_out) *totalSz_out = sz; |
| } |
| |
| signed clamp_rgb(signed value) { |
| if (value > 255) { |
| value = 255; |
| } else if (value < 0) { |
| value = 0; |
| } |
| return value; |
| } |
| |
| void rgb565_to_yv12(char* dest, char* src, int width, int height, |
| int left, int top, int right, int bottom) { |
| const int rgb_stride = 2; |
| |
| int align = 16; |
| int yStride = (width + (align -1)) & ~(align-1); |
| int cStride = (yStride / 2 + (align - 1)) & ~(align-1); |
| int cSize = cStride * height/2; |
| |
| uint16_t *rgb_ptr0 = (uint16_t *)src; |
| uint8_t *yv12_y0 = (uint8_t *)dest; |
| uint8_t *yv12_v0 = yv12_y0 + yStride * height; |
| |
| for (int j = top; j <= bottom; ++j) { |
| uint8_t *yv12_y = yv12_y0 + j * yStride; |
| uint8_t *yv12_v = yv12_v0 + (j/2) * cStride; |
| uint8_t *yv12_u = yv12_v + cSize; |
| uint16_t *rgb_ptr = rgb_ptr0 + get_rgb_offset(j, width, rgb_stride) / 2; |
| bool jeven = (j & 1) == 0; |
| for (int i = left; i <= right; ++i) { |
| uint8_t r = ((rgb_ptr[i]) >> 11) & 0x01f; |
| uint8_t g = ((rgb_ptr[i]) >> 5) & 0x03f; |
| uint8_t b = (rgb_ptr[i]) & 0x01f; |
| // convert to 8bits |
| // http://stackoverflow.com/questions/2442576/how-does-one-convert-16-bit-rgb565-to-24-bit-rgb888 |
| uint8_t R = (r * 527 + 23) >> 6; |
| uint8_t G = (g * 259 + 33) >> 6; |
| uint8_t B = (b * 527 + 23) >> 6; |
| // convert to YV12 |
| // frameworks/base/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp |
| yv12_y[i] = clamp_rgb((77 * R + 150 * G + 29 * B) >> 8); |
| bool ieven = (i & 1) == 0; |
| if (jeven && ieven) { |
| yv12_u[i] = clamp_rgb((( -43 * R - 85 * G + 128 * B) >> 8) + 128); |
| yv12_v[i] = clamp_rgb((( 128 * R - 107 * G - 21 * B) >> 8) + 128); |
| } |
| } |
| } |
| } |
| |
| void rgb888_to_yv12(char* dest, char* src, int width, int height, |
| int left, int top, int right, int bottom) { |
| const int rgb_stride = 3; |
| |
| DD("%s convert %d by %d", __func__, width, height); |
| int align = 16; |
| int yStride = (width + (align -1)) & ~(align-1); |
| int cStride = (yStride / 2 + (align - 1)) & ~(align-1); |
| int cSize = cStride * height/2; |
| |
| |
| uint8_t *rgb_ptr0 = (uint8_t *)src; |
| uint8_t *yv12_y0 = (uint8_t *)dest; |
| uint8_t *yv12_u0 = yv12_y0 + yStride * height + cSize; |
| uint8_t *yv12_v0 = yv12_y0 + yStride * height; |
| |
| #if DEBUG |
| char mybuf[1024]; |
| snprintf(mybuf, sizeof(mybuf), "/sdcard/raw_%d_%d_rgb.ppm", width, height); |
| FILE *myfp = fopen(mybuf, "wb"); /* b - binary mode */ |
| (void) fprintf(myfp, "P6\n%d %d\n255\n", width, height); |
| |
| if (myfp == NULL) { |
| DD("failed to open /sdcard/raw_rgb888.ppm"); |
| } else { |
| fwrite(rgb_ptr0, width * height * rgb_stride, 1, myfp); |
| fclose(myfp); |
| } |
| #endif |
| |
| int uvcount = 0; |
| for (int j = top; j <= bottom; ++j) { |
| uint8_t *yv12_y = yv12_y0 + j * yStride; |
| uint8_t *rgb_ptr = rgb_ptr0 + get_rgb_offset(j, width, rgb_stride); |
| bool jeven = (j & 1) == 0; |
| for (int i = left; i <= right; ++i) { |
| uint8_t R = rgb_ptr[i*rgb_stride]; |
| uint8_t G = rgb_ptr[i*rgb_stride+1]; |
| uint8_t B = rgb_ptr[i*rgb_stride+2]; |
| // convert to YV12 |
| // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion |
| // but scale up U by 1/0.96 |
| yv12_y[i] = clamp_rgb(1.0 * ((0.25678823529411765 * R) + (0.5041294117647058 * G) + (0.09790588235294118 * B)) + 16); |
| bool ieven = (i & 1) == 0; |
| if (jeven && ieven) { |
| yv12_u0[uvcount] = clamp_rgb((1/0.96) * (-(0.1482235294117647 * R) - (0.2909921568627451 * G) + (0.4392156862745098 * B)) + 128); |
| yv12_v0[uvcount] = clamp_rgb((1.0)* ((0.4392156862745098 * R) - (0.36778823529411764 * G) - (0.07142745098039215 * B)) + 128); |
| uvcount ++; |
| } |
| } |
| if (jeven) { |
| yv12_u0 += cStride; |
| yv12_v0 += cStride; |
| uvcount = 0; |
| } |
| } |
| |
| #if DEBUG |
| snprintf(mybuf, sizeof(mybuf), "/sdcard/raw_%d_%d_yv12.yuv", width, height); |
| FILE *yuvfp = fopen(mybuf, "wb"); /* b - binary mode */ |
| if (yuvfp != NULL) { |
| fwrite(yv12_y0, yStride * height + 2 * cSize, 1, yuvfp); |
| fclose(yuvfp); |
| } |
| #endif |
| |
| } |
| |
| void rgb888_to_yuv420p(char* dest, char* src, int width, int height, |
| int left, int top, int right, int bottom) { |
| const int rgb_stride = 3; |
| |
| DD("%s convert %d by %d", __func__, width, height); |
| int yStride = width; |
| int cStride = yStride / 2; |
| int cSize = cStride * height/2; |
| |
| uint8_t *rgb_ptr0 = (uint8_t *)src; |
| uint8_t *yv12_y0 = (uint8_t *)dest; |
| uint8_t *yv12_u0 = yv12_y0 + yStride * height; |
| |
| for (int j = top; j <= bottom; ++j) { |
| uint8_t *yv12_y = yv12_y0 + j * yStride; |
| uint8_t *yv12_u = yv12_u0 + (j/2) * cStride; |
| uint8_t *yv12_v = yv12_u + cSize; |
| uint8_t *rgb_ptr = rgb_ptr0 + get_rgb_offset(j, width, rgb_stride); |
| bool jeven = (j & 1) == 0; |
| for (int i = left; i <= right; ++i) { |
| uint8_t R = rgb_ptr[i*rgb_stride]; |
| uint8_t G = rgb_ptr[i*rgb_stride+1]; |
| uint8_t B = rgb_ptr[i*rgb_stride+2]; |
| // convert to YV12 |
| // frameworks/base/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp |
| yv12_y[i] = clamp_rgb((77 * R + 150 * G + 29 * B) >> 8); |
| bool ieven = (i & 1) == 0; |
| if (jeven && ieven) { |
| yv12_u[i] = clamp_rgb((( -43 * R - 85 * G + 128 * B) >> 8) + 128); |
| yv12_v[i] = clamp_rgb((( 128 * R - 107 * G - 21 * B) >> 8) + 128); |
| } |
| } |
| } |
| } |
| |
| // YV12 is aka YUV420Planar, or YUV420p; the only difference is that YV12 has |
| // certain stride requirements for Y and UV respectively. |
| void yv12_to_rgb565(char* dest, char* src, int width, int height, |
| int left, int top, int right, int bottom) { |
| const int rgb_stride = 2; |
| |
| DD("%s convert %d by %d", __func__, width, height); |
| int align = 16; |
| int yStride = (width + (align -1)) & ~(align-1); |
| int cStride = (yStride / 2 + (align - 1)) & ~(align-1); |
| int cSize = cStride * height/2; |
| |
| uint16_t *rgb_ptr0 = (uint16_t *)dest; |
| uint8_t *yv12_y0 = (uint8_t *)src; |
| uint8_t *yv12_v0 = yv12_y0 + yStride * height; |
| |
| for (int j = top; j <= bottom; ++j) { |
| uint8_t *yv12_y = yv12_y0 + j * yStride; |
| uint8_t *yv12_v = yv12_v0 + (j/2) * cStride; |
| uint8_t *yv12_u = yv12_v + cSize; |
| uint16_t *rgb_ptr = rgb_ptr0 + get_rgb_offset(j, width, rgb_stride); |
| for (int i = left; i <= right; ++i) { |
| // convert to rgb |
| // frameworks/av/media/libstagefright/colorconversion/ColorConverter.cpp |
| signed y1 = (signed)yv12_y[i] - 16; |
| signed u = (signed)yv12_u[i / 2] - 128; |
| signed v = (signed)yv12_v[i / 2] - 128; |
| |
| signed u_b = u * 517; |
| signed u_g = -u * 100; |
| signed v_g = -v * 208; |
| signed v_r = v * 409; |
| |
| signed tmp1 = y1 * 298; |
| signed b1 = clamp_rgb((tmp1 + u_b) / 256); |
| signed g1 = clamp_rgb((tmp1 + v_g + u_g) / 256); |
| signed r1 = clamp_rgb((tmp1 + v_r) / 256); |
| |
| uint16_t rgb1 = ((r1 >> 3) << 11) | ((g1 >> 2) << 5) | (b1 >> 3); |
| |
| rgb_ptr[i-left] = rgb1; |
| } |
| } |
| } |
| |
| // YV12 is aka YUV420Planar, or YUV420p; the only difference is that YV12 has |
| // certain stride requirements for Y and UV respectively. |
| void yv12_to_rgb888(char* dest, char* src, int width, int height, |
| int left, int top, int right, int bottom) { |
| const int rgb_stride = 3; |
| |
| DD("%s convert %d by %d", __func__, width, height); |
| int align = 16; |
| int yStride = (width + (align -1)) & ~(align-1); |
| int cStride = (yStride / 2 + (align - 1)) & ~(align-1); |
| int cSize = cStride * height/2; |
| |
| uint8_t *rgb_ptr0 = (uint8_t *)dest; |
| uint8_t *yv12_y0 = (uint8_t *)src; |
| uint8_t *yv12_v0 = yv12_y0 + yStride * height; |
| |
| for (int j = top; j <= bottom; ++j) { |
| uint8_t *yv12_y = yv12_y0 + j * yStride; |
| uint8_t *yv12_v = yv12_v0 + (j/2) * cStride; |
| uint8_t *yv12_u = yv12_v + cSize; |
| uint8_t *rgb_ptr = rgb_ptr0 + get_rgb_offset(j - top, right - left + 1, rgb_stride); |
| for (int i = left; i <= right; ++i) { |
| // convert to rgb |
| // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion |
| // but scale down U by 0.96 to mitigate rgb over/under flow |
| signed y1 = (signed)yv12_y[i] - 16; |
| signed u = (signed)yv12_u[i / 2] - 128; |
| signed v = (signed)yv12_v[i / 2] - 128; |
| |
| signed r1 = clamp_rgb(1 * (1.1643835616438356 * y1 + 1.5960267857142856 * v)); |
| signed g1 = clamp_rgb(1 * (1.1643835616438356 * y1 - 0.39176229009491365 * u * 0.97 - 0.8129676472377708 * v)); |
| signed b1 = clamp_rgb(1 * (1.1643835616438356 * y1 + 2.017232142857143 * u * 0.97)); |
| |
| rgb_ptr[(i-left)*rgb_stride] = r1; |
| rgb_ptr[(i-left)*rgb_stride+1] = g1; |
| rgb_ptr[(i-left)*rgb_stride+2] = b1; |
| } |
| } |
| } |
| |
| // YV12 is aka YUV420Planar, or YUV420p; the only difference is that YV12 has |
| // certain stride requirements for Y and UV respectively. |
| void yuv420p_to_rgb888(char* dest, char* src, int width, int height, |
| int left, int top, int right, int bottom) { |
| const int rgb_stride = 3; |
| |
| DD("%s convert %d by %d", __func__, width, height); |
| int yStride = width; |
| int cStride = yStride / 2; |
| int cSize = cStride * height/2; |
| |
| uint8_t *rgb_ptr0 = (uint8_t *)dest; |
| uint8_t *yv12_y0 = (uint8_t *)src; |
| uint8_t *yv12_u0 = yv12_y0 + yStride * height; |
| |
| for (int j = top; j <= bottom; ++j) { |
| uint8_t *yv12_y = yv12_y0 + j * yStride; |
| uint8_t *yv12_u = yv12_u0 + (j/2) * cStride; |
| uint8_t *yv12_v = yv12_u + cSize; |
| uint8_t *rgb_ptr = rgb_ptr0 + get_rgb_offset(j - top, right - left + 1, rgb_stride); |
| for (int i = left; i <= right; ++i) { |
| // convert to rgb |
| // frameworks/av/media/libstagefright/colorconversion/ColorConverter.cpp |
| signed y1 = (signed)yv12_y[i] - 16; |
| signed u = (signed)yv12_u[i / 2] - 128; |
| signed v = (signed)yv12_v[i / 2] - 128; |
| |
| signed u_b = u * 517; |
| signed u_g = -u * 100; |
| signed v_g = -v * 208; |
| signed v_r = v * 409; |
| |
| signed tmp1 = y1 * 298; |
| signed b1 = clamp_rgb((tmp1 + u_b) / 256); |
| signed g1 = clamp_rgb((tmp1 + v_g + u_g) / 256); |
| signed r1 = clamp_rgb((tmp1 + v_r) / 256); |
| |
| rgb_ptr[(i-left)*rgb_stride] = r1; |
| rgb_ptr[(i-left)*rgb_stride+1] = g1; |
| rgb_ptr[(i-left)*rgb_stride+2] = b1; |
| } |
| } |
| } |
| |
| void copy_rgb_buffer_from_unlocked( |
| char* dst, const char* raw_data, |
| int unlockedWidth, |
| int width, int height, int top, int left, |
| int bpp) { |
| int dst_line_len = width * bpp; |
| int src_line_len = unlockedWidth * bpp; |
| const char *src = raw_data + top*src_line_len + left*bpp; |
| for (int y = 0; y < height; y++) { |
| memcpy(dst, src, dst_line_len); |
| src += src_line_len; |
| dst += dst_line_len; |
| } |
| } |