test/testyuv_cvt.c
changeset 11702 cf166abbde4a
child 11811 5d94cb6b24d3
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/test/testyuv_cvt.c	Sun Nov 12 22:51:12 2017 -0800
     1.3 @@ -0,0 +1,300 @@
     1.4 +/*
     1.5 +  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     1.6 +
     1.7 +  This software is provided 'as-is', without any express or implied
     1.8 +  warranty.  In no event will the authors be held liable for any damages
     1.9 +  arising from the use of this software.
    1.10 +
    1.11 +  Permission is granted to anyone to use this software for any purpose,
    1.12 +  including commercial applications, and to alter it and redistribute it
    1.13 +  freely.
    1.14 +*/
    1.15 +
    1.16 +#include "SDL.h"
    1.17 +
    1.18 +#include "testyuv_cvt.h"
    1.19 +
    1.20 +
    1.21 +static float clip3(float x, float y, float z)
    1.22 +{
    1.23 +    return ((z < x) ? x : ((z > y) ? y : z));
    1.24 +}
    1.25 +
    1.26 +static void RGBtoYUV(Uint8 * rgb, int *yuv, SDL_YUV_CONVERSION_MODE mode, int monochrome, int luminance)
    1.27 +{
    1.28 +    if (mode == SDL_YUV_CONVERSION_JPEG) {
    1.29 +        /* Full range YUV */
    1.30 +        yuv[0] = (int)(0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]);
    1.31 +        yuv[1] = (int)((rgb[2] - yuv[0]) * 0.565 + 128);
    1.32 +        yuv[2] = (int)((rgb[0] - yuv[0]) * 0.713 + 128);
    1.33 +    } else {
    1.34 +        // This formula is from Microsoft's documentation:
    1.35 +        // https://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx
    1.36 +        // L = Kr * R + Kb * B + (1 - Kr - Kb) * G
    1.37 +        // Y =                   floor(2^(M-8) * (219*(L-Z)/S + 16) + 0.5);
    1.38 +        // U = clip3(0, (2^M)-1, floor(2^(M-8) * (112*(B-L) / ((1-Kb)*S) + 128) + 0.5));
    1.39 +        // V = clip3(0, (2^M)-1, floor(2^(M-8) * (112*(R-L) / ((1-Kr)*S) + 128) + 0.5));
    1.40 +        float S, Z, R, G, B, L, Kr, Kb, Y, U, V;
    1.41 +
    1.42 +        if (mode == SDL_YUV_CONVERSION_BT709) {
    1.43 +            /* BT.709 */
    1.44 +            Kr = 0.2126f;
    1.45 +            Kb = 0.0722f;
    1.46 +        } else {
    1.47 +            /* BT.601 */
    1.48 +            Kr = 0.299f;
    1.49 +            Kb = 0.114f;
    1.50 +        }
    1.51 +
    1.52 +        S = 255.0f;
    1.53 +        Z = 0.0f;
    1.54 +        R = rgb[0];
    1.55 +        G = rgb[1];
    1.56 +        B = rgb[2];
    1.57 +        L = Kr * R + Kb * B + (1 - Kr - Kb) * G;
    1.58 +        Y = (Uint8)SDL_floorf((219*(L-Z)/S + 16) + 0.5f);
    1.59 +        U = (Uint8)clip3(0, 255, SDL_floorf((112.0f*(B-L) / ((1.0f-Kb)*S) + 128) + 0.5f));
    1.60 +        V = (Uint8)clip3(0, 255, SDL_floorf((112.0f*(R-L) / ((1.0f-Kr)*S) + 128) + 0.5f));
    1.61 +
    1.62 +        yuv[0] = (Uint8)Y;
    1.63 +        yuv[1] = (Uint8)U;
    1.64 +        yuv[2] = (Uint8)V;
    1.65 +    }
    1.66 +
    1.67 +    if (monochrome) {
    1.68 +        yuv[1] = 128;
    1.69 +        yuv[2] = 128;
    1.70 +    }
    1.71 +
    1.72 +    if (luminance != 100) {
    1.73 +        yuv[0] = yuv[0] * luminance / 100;
    1.74 +        if (yuv[0] > 255)
    1.75 +            yuv[0] = 255;
    1.76 +    }
    1.77 +}
    1.78 +
    1.79 +static void ConvertRGBtoPlanar2x2(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, SDL_YUV_CONVERSION_MODE mode, int monochrome, int luminance)
    1.80 +{
    1.81 +    int x, y;
    1.82 +    int yuv[4][3];
    1.83 +    Uint8 *Y1, *Y2, *U, *V;
    1.84 +    Uint8 *rgb1, *rgb2;
    1.85 +    int rgb_row_advance = (pitch - w*3) + pitch;
    1.86 +    int UV_advance;
    1.87 +
    1.88 +    rgb1 = src;
    1.89 +    rgb2 = src + pitch;
    1.90 +
    1.91 +    Y1 = out;
    1.92 +    Y2 = Y1 + w;
    1.93 +    switch (format) {
    1.94 +    case SDL_PIXELFORMAT_YV12:
    1.95 +        V = (Y1 + h * w);
    1.96 +        U = V + ((h + 1)/2)*((w + 1)/2);
    1.97 +        UV_advance = 1;
    1.98 +        break;
    1.99 +    case SDL_PIXELFORMAT_IYUV:
   1.100 +        U = (Y1 + h * w);
   1.101 +        V = U + ((h + 1)/2)*((w + 1)/2);
   1.102 +        UV_advance = 1;
   1.103 +        break;
   1.104 +    case SDL_PIXELFORMAT_NV12:
   1.105 +        U = (Y1 + h * w);
   1.106 +        V = U + 1;
   1.107 +        UV_advance = 2;
   1.108 +        break;
   1.109 +    case SDL_PIXELFORMAT_NV21:
   1.110 +        V = (Y1 + h * w);
   1.111 +        U = V + 1;
   1.112 +        UV_advance = 2;
   1.113 +        break;
   1.114 +    default:
   1.115 +        SDL_assert(!"Unsupported planar YUV format");
   1.116 +        return;
   1.117 +    }
   1.118 +
   1.119 +    for (y = 0; y < (h - 1); y += 2) {
   1.120 +        for (x = 0; x < (w - 1); x += 2) {
   1.121 +            RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance);
   1.122 +            rgb1 += 3;
   1.123 +            *Y1++ = (Uint8)yuv[0][0];
   1.124 +
   1.125 +            RGBtoYUV(rgb1, yuv[1], mode, monochrome, luminance);
   1.126 +            rgb1 += 3;
   1.127 +            *Y1++ = (Uint8)yuv[1][0];
   1.128 +
   1.129 +            RGBtoYUV(rgb2, yuv[2], mode, monochrome, luminance);
   1.130 +            rgb2 += 3;
   1.131 +            *Y2++ = (Uint8)yuv[2][0];
   1.132 +
   1.133 +            RGBtoYUV(rgb2, yuv[3], mode, monochrome, luminance);
   1.134 +            rgb2 += 3;
   1.135 +            *Y2++ = (Uint8)yuv[3][0];
   1.136 +
   1.137 +            *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[1][1] + yuv[2][1] + yuv[3][1])/4.0f + 0.5f);
   1.138 +            U += UV_advance;
   1.139 +
   1.140 +            *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[1][2] + yuv[2][2] + yuv[3][2])/4.0f + 0.5f);
   1.141 +            V += UV_advance;
   1.142 +        }
   1.143 +        /* Last column */
   1.144 +        if (x == (w - 1)) {
   1.145 +            RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance);
   1.146 +            rgb1 += 3;
   1.147 +            *Y1++ = (Uint8)yuv[0][0];
   1.148 +
   1.149 +            RGBtoYUV(rgb2, yuv[2], mode, monochrome, luminance);
   1.150 +            rgb2 += 3;
   1.151 +            *Y2++ = (Uint8)yuv[2][0];
   1.152 +
   1.153 +            *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[2][1])/2.0f + 0.5f);
   1.154 +            U += UV_advance;
   1.155 +
   1.156 +            *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[2][2])/2.0f + 0.5f);
   1.157 +            V += UV_advance;
   1.158 +        }
   1.159 +        Y1 += w;
   1.160 +        Y2 += w;
   1.161 +        rgb1 += rgb_row_advance;
   1.162 +        rgb2 += rgb_row_advance;
   1.163 +    }
   1.164 +    /* Last row */
   1.165 +    if (y == (h - 1)) {
   1.166 +        for (x = 0; x < (w - 1); x += 2) {
   1.167 +            RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance);
   1.168 +            rgb1 += 3;
   1.169 +            *Y1++ = (Uint8)yuv[0][0];
   1.170 +
   1.171 +            RGBtoYUV(rgb1, yuv[1], mode, monochrome, luminance);
   1.172 +            rgb1 += 3;
   1.173 +            *Y1++ = (Uint8)yuv[1][0];
   1.174 +
   1.175 +            *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[1][1])/2.0f + 0.5f);
   1.176 +            U += UV_advance;
   1.177 +
   1.178 +            *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[1][2])/2.0f + 0.5f);
   1.179 +            V += UV_advance;
   1.180 +        }
   1.181 +        /* Last column */
   1.182 +        if (x == (w - 1)) {
   1.183 +            RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance);
   1.184 +            *Y1++ = (Uint8)yuv[0][0];
   1.185 +
   1.186 +            *U = (Uint8)yuv[0][1];
   1.187 +            U += UV_advance;
   1.188 +
   1.189 +            *V = (Uint8)yuv[0][2];
   1.190 +            V += UV_advance;
   1.191 +        }
   1.192 +    }
   1.193 +}
   1.194 +
   1.195 +static void ConvertRGBtoPacked4(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, SDL_YUV_CONVERSION_MODE mode, int monochrome, int luminance)
   1.196 +{
   1.197 +    int x, y;
   1.198 +    int yuv[2][3];
   1.199 +    Uint8 *Y1, *Y2, *U, *V;
   1.200 +    Uint8 *rgb;
   1.201 +    int rgb_row_advance = (pitch - w*3);
   1.202 +
   1.203 +    rgb = src;
   1.204 +
   1.205 +    switch (format) {
   1.206 +    case SDL_PIXELFORMAT_YUY2:
   1.207 +        Y1 = out;
   1.208 +        U = out+1;
   1.209 +        Y2 = out+2;
   1.210 +        V = out+3;
   1.211 +        break;
   1.212 +    case SDL_PIXELFORMAT_UYVY:
   1.213 +        U = out;
   1.214 +        Y1 = out+1;
   1.215 +        V = out+2;
   1.216 +        Y2 = out+3;
   1.217 +        break;
   1.218 +    case SDL_PIXELFORMAT_YVYU:
   1.219 +        Y1 = out;
   1.220 +        V = out+1;
   1.221 +        Y2 = out+2;
   1.222 +        U = out+3;
   1.223 +        break;
   1.224 +    default:
   1.225 +        SDL_assert(!"Unsupported packed YUV format");
   1.226 +        return;
   1.227 +    }
   1.228 +
   1.229 +    for (y = 0; y < h; ++y) {
   1.230 +        for (x = 0; x < (w - 1); x += 2) {
   1.231 +            RGBtoYUV(rgb, yuv[0], mode, monochrome, luminance);
   1.232 +            rgb += 3;
   1.233 +            *Y1 = (Uint8)yuv[0][0];
   1.234 +            Y1 += 4;
   1.235 +
   1.236 +            RGBtoYUV(rgb, yuv[1], mode, monochrome, luminance);
   1.237 +            rgb += 3;
   1.238 +            *Y2 = (Uint8)yuv[1][0];
   1.239 +            Y2 += 4;
   1.240 +
   1.241 +            *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[1][1])/2.0f + 0.5f);
   1.242 +            U += 4;
   1.243 +
   1.244 +            *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[1][2])/2.0f + 0.5f);
   1.245 +            V += 4;
   1.246 +        }
   1.247 +        /* Last column */
   1.248 +        if (x == (w - 1)) {
   1.249 +            RGBtoYUV(rgb, yuv[0], mode, monochrome, luminance);
   1.250 +            rgb += 3;
   1.251 +            *Y2 = *Y1 = (Uint8)yuv[0][0];
   1.252 +            Y1 += 4;
   1.253 +            Y2 += 4;
   1.254 +
   1.255 +            *U = (Uint8)yuv[0][1];
   1.256 +            U += 4;
   1.257 +
   1.258 +            *V = (Uint8)yuv[0][2];
   1.259 +            V += 4;
   1.260 +        }
   1.261 +        rgb += rgb_row_advance;
   1.262 +    }
   1.263 +}
   1.264 +
   1.265 +SDL_bool ConvertRGBtoYUV(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, SDL_YUV_CONVERSION_MODE mode, int monochrome, int luminance)
   1.266 +{
   1.267 +    switch (format)
   1.268 +    {
   1.269 +    case SDL_PIXELFORMAT_YV12:
   1.270 +    case SDL_PIXELFORMAT_IYUV:
   1.271 +    case SDL_PIXELFORMAT_NV12:
   1.272 +    case SDL_PIXELFORMAT_NV21:
   1.273 +        ConvertRGBtoPlanar2x2(format, src, pitch, out, w, h, mode, monochrome, luminance);
   1.274 +        return SDL_TRUE;
   1.275 +    case SDL_PIXELFORMAT_YUY2:
   1.276 +    case SDL_PIXELFORMAT_UYVY:
   1.277 +    case SDL_PIXELFORMAT_YVYU:
   1.278 +        ConvertRGBtoPacked4(format, src, pitch, out, w, h, mode, monochrome, luminance);
   1.279 +        return SDL_TRUE;
   1.280 +    default:
   1.281 +        return SDL_FALSE;
   1.282 +    }
   1.283 +}
   1.284 +
   1.285 +int CalculateYUVPitch(Uint32 format, int width)
   1.286 +{
   1.287 +    switch (format)
   1.288 +    {
   1.289 +    case SDL_PIXELFORMAT_YV12:
   1.290 +    case SDL_PIXELFORMAT_IYUV:
   1.291 +    case SDL_PIXELFORMAT_NV12:
   1.292 +    case SDL_PIXELFORMAT_NV21:
   1.293 +        return width;
   1.294 +    case SDL_PIXELFORMAT_YUY2:
   1.295 +    case SDL_PIXELFORMAT_UYVY:
   1.296 +    case SDL_PIXELFORMAT_YVYU:
   1.297 +        return 4*((width + 1)/2);
   1.298 +    default:
   1.299 +        return 0;
   1.300 +    }
   1.301 +}
   1.302 +
   1.303 +/* vi: set ts=4 sw=4 expandtab: */