sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

testyuv.c (18775B)


      1 /*
      2   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
      3 
      4   This software is provided 'as-is', without any express or implied
      5   warranty.  In no event will the authors be held liable for any damages
      6   arising from the use of this software.
      7 
      8   Permission is granted to anyone to use this software for any purpose,
      9   including commercial applications, and to alter it and redistribute it
     10   freely.
     11 */
     12 #include <stdlib.h>
     13 #include <stdio.h>
     14 #include <string.h>
     15 
     16 #include "SDL.h"
     17 #include "SDL_test_font.h"
     18 #include "testyuv_cvt.h"
     19 
     20 
     21 /* 422 (YUY2, etc) formats are the largest */
     22 #define MAX_YUV_SURFACE_SIZE(W, H, P)  (H*4*(W+P+1)/2)
     23 
     24 
     25 /* Return true if the YUV format is packed pixels */
     26 static SDL_bool is_packed_yuv_format(Uint32 format)
     27 {
     28     return (format == SDL_PIXELFORMAT_YUY2 ||
     29             format == SDL_PIXELFORMAT_UYVY ||
     30             format == SDL_PIXELFORMAT_YVYU);
     31 }
     32 
     33 /* Create a surface with a good pattern for verifying YUV conversion */
     34 static SDL_Surface *generate_test_pattern(int pattern_size)
     35 {
     36     SDL_Surface *pattern = SDL_CreateRGBSurfaceWithFormat(0, pattern_size, pattern_size, 0, SDL_PIXELFORMAT_RGB24);
     37 
     38     if (pattern) {
     39         int i, x, y;
     40         Uint8 *p, c;
     41         const int thickness = 2;    /* Important so 2x2 blocks of color are the same, to avoid Cr/Cb interpolation over pixels */
     42 
     43         /* R, G, B in alternating horizontal bands */
     44         for (y = 0; y < pattern->h; y += thickness) {
     45             for (i = 0; i < thickness; ++i) {
     46                 p = (Uint8 *)pattern->pixels + (y + i) * pattern->pitch + ((y/thickness) % 3);
     47                 for (x = 0; x < pattern->w; ++x) {
     48                     *p = 0xFF;
     49                     p += 3;
     50                 }
     51             }
     52         }
     53 
     54         /* Black and white in alternating vertical bands */
     55         c = 0xFF;
     56         for (x = 1*thickness; x < pattern->w; x += 2*thickness) {
     57             for (i = 0; i < thickness; ++i) {
     58                 p = (Uint8 *)pattern->pixels + (x + i)*3;
     59                 for (y = 0; y < pattern->h; ++y) {
     60                     SDL_memset(p, c, 3);
     61                     p += pattern->pitch;
     62                 }
     63             }
     64             if (c) {
     65                 c = 0x00;
     66             } else {
     67                 c = 0xFF;
     68             }
     69         }
     70     }
     71     return pattern;
     72 }
     73 
     74 static SDL_bool verify_yuv_data(Uint32 format, const Uint8 *yuv, int yuv_pitch, SDL_Surface *surface)
     75 {
     76     const int tolerance = 20;
     77     const int size = (surface->h * surface->pitch);
     78     Uint8 *rgb;
     79     SDL_bool result = SDL_FALSE;
     80 
     81     rgb = (Uint8 *)SDL_malloc(size);
     82     if (!rgb) {
     83         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory");
     84         return SDL_FALSE;
     85     }
     86 
     87     if (SDL_ConvertPixels(surface->w, surface->h, format, yuv, yuv_pitch, surface->format->format, rgb, surface->pitch) == 0) {
     88         int x, y;
     89         result = SDL_TRUE;
     90         for (y = 0; y < surface->h; ++y) {
     91             const Uint8 *actual = rgb + y * surface->pitch;
     92             const Uint8 *expected = (const Uint8 *)surface->pixels + y * surface->pitch;
     93             for (x = 0; x < surface->w; ++x) {
     94                 int deltaR = (int)actual[0] - expected[0];
     95                 int deltaG = (int)actual[1] - expected[1];
     96                 int deltaB = (int)actual[2] - expected[2];
     97                 int distance = (deltaR * deltaR + deltaG * deltaG + deltaB * deltaB);
     98                 if (distance > tolerance) {
     99                     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Pixel at %d,%d was 0x%.2x,0x%.2x,0x%.2x, expected 0x%.2x,0x%.2x,0x%.2x, distance = %d\n", x, y, actual[0], actual[1], actual[2], expected[0], expected[1], expected[2], distance);
    100                     result = SDL_FALSE;
    101                 }
    102                 actual += 3;
    103                 expected += 3;
    104             }
    105         }
    106     } else {
    107         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(format), SDL_GetPixelFormatName(surface->format->format), SDL_GetError());
    108     }
    109     SDL_free(rgb);
    110 
    111     return result;
    112 }
    113 
    114 static int run_automated_tests(int pattern_size, int extra_pitch)
    115 {
    116     const Uint32 formats[] = {
    117         SDL_PIXELFORMAT_YV12,
    118         SDL_PIXELFORMAT_IYUV,
    119         SDL_PIXELFORMAT_NV12,
    120         SDL_PIXELFORMAT_NV21,
    121         SDL_PIXELFORMAT_YUY2,
    122         SDL_PIXELFORMAT_UYVY,
    123         SDL_PIXELFORMAT_YVYU
    124     };
    125     int i, j;
    126     SDL_Surface *pattern = generate_test_pattern(pattern_size);
    127     const int yuv_len = MAX_YUV_SURFACE_SIZE(pattern->w, pattern->h, extra_pitch);
    128     Uint8 *yuv1 = (Uint8 *)SDL_malloc(yuv_len);
    129     Uint8 *yuv2 = (Uint8 *)SDL_malloc(yuv_len);
    130     int yuv1_pitch, yuv2_pitch;
    131     int result = -1;
    132     
    133     if (!pattern || !yuv1 || !yuv2) {
    134         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't allocate test surfaces");
    135         goto done;
    136     }
    137 
    138     /* Verify conversion from YUV formats */
    139     for (i = 0; i < SDL_arraysize(formats); ++i) {
    140         if (!ConvertRGBtoYUV(formats[i], pattern->pixels, pattern->pitch, yuv1, pattern->w, pattern->h, SDL_GetYUVConversionModeForResolution(pattern->w, pattern->h), 0, 100)) {
    141             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ConvertRGBtoYUV() doesn't support converting to %s\n", SDL_GetPixelFormatName(formats[i]));
    142             goto done;
    143         }
    144         yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w);
    145         if (!verify_yuv_data(formats[i], yuv1, yuv1_pitch, pattern)) {
    146             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to RGB\n", SDL_GetPixelFormatName(formats[i]));
    147             goto done;
    148         }
    149     }
    150 
    151     /* Verify conversion to YUV formats */
    152     for (i = 0; i < SDL_arraysize(formats); ++i) {
    153         yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch;
    154         if (SDL_ConvertPixels(pattern->w, pattern->h, pattern->format->format, pattern->pixels, pattern->pitch, formats[i], yuv1, yuv1_pitch) < 0) {
    155             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError());
    156             goto done;
    157         }
    158         if (!verify_yuv_data(formats[i], yuv1, yuv1_pitch, pattern)) {
    159             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from RGB to %s\n", SDL_GetPixelFormatName(formats[i]));
    160             goto done;
    161         }
    162     }
    163 
    164     /* Verify conversion between YUV formats */
    165     for (i = 0; i < SDL_arraysize(formats); ++i) {
    166         for (j = 0; j < SDL_arraysize(formats); ++j) {
    167             yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch;
    168             yuv2_pitch = CalculateYUVPitch(formats[j], pattern->w) + extra_pitch;
    169             if (SDL_ConvertPixels(pattern->w, pattern->h, pattern->format->format, pattern->pixels, pattern->pitch, formats[i], yuv1, yuv1_pitch) < 0) {
    170                 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError());
    171                 goto done;
    172             }
    173             if (SDL_ConvertPixels(pattern->w, pattern->h, formats[i], yuv1, yuv1_pitch, formats[j], yuv2, yuv2_pitch) < 0) {
    174                 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError());
    175                 goto done;
    176             }
    177             if (!verify_yuv_data(formats[j], yuv2, yuv2_pitch, pattern)) {
    178                 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]));
    179                 goto done;
    180             }
    181         }
    182     }
    183 
    184     /* Verify conversion between YUV formats in-place */
    185     for (i = 0; i < SDL_arraysize(formats); ++i) {
    186         for (j = 0; j < SDL_arraysize(formats); ++j) {
    187             if (is_packed_yuv_format(formats[i]) != is_packed_yuv_format(formats[j])) {
    188                 /* Can't change plane vs packed pixel layout in-place */
    189                 continue;
    190             }
    191 
    192             yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch;
    193             yuv2_pitch = CalculateYUVPitch(formats[j], pattern->w) + extra_pitch;
    194             if (SDL_ConvertPixels(pattern->w, pattern->h, pattern->format->format, pattern->pixels, pattern->pitch, formats[i], yuv1, yuv1_pitch) < 0) {
    195                 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError());
    196                 goto done;
    197             }
    198             if (SDL_ConvertPixels(pattern->w, pattern->h, formats[i], yuv1, yuv1_pitch, formats[j], yuv1, yuv2_pitch) < 0) {
    199                 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError());
    200                 goto done;
    201             }
    202             if (!verify_yuv_data(formats[j], yuv1, yuv2_pitch, pattern)) {
    203                 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]));
    204                 goto done;
    205             }
    206         }
    207     }
    208 
    209 
    210     result = 0;
    211 
    212 done:
    213     SDL_free(yuv1);
    214     SDL_free(yuv2);
    215     SDL_FreeSurface(pattern);
    216     return result;
    217 }
    218 
    219 int
    220 main(int argc, char **argv)
    221 {
    222     struct {
    223         SDL_bool enable_intrinsics;
    224         int pattern_size;
    225         int extra_pitch;
    226     } automated_test_params[] = {
    227         /* Test: even width and height */
    228         { SDL_FALSE, 2, 0 },
    229         { SDL_FALSE, 4, 0 },
    230         /* Test: odd width and height */
    231         { SDL_FALSE, 1, 0 },
    232         { SDL_FALSE, 3, 0 },
    233         /* Test: even width and height, extra pitch */
    234         { SDL_FALSE, 2, 3 },
    235         { SDL_FALSE, 4, 3 },
    236         /* Test: odd width and height, extra pitch */
    237         { SDL_FALSE, 1, 3 },
    238         { SDL_FALSE, 3, 3 },
    239         /* Test: even width and height with intrinsics */
    240         { SDL_TRUE, 32, 0 },
    241         /* Test: odd width and height with intrinsics */
    242         { SDL_TRUE, 33, 0 },
    243         { SDL_TRUE, 37, 0 },
    244         /* Test: even width and height with intrinsics, extra pitch */
    245         { SDL_TRUE, 32, 3 },
    246         /* Test: odd width and height with intrinsics, extra pitch */
    247         { SDL_TRUE, 33, 3 },
    248         { SDL_TRUE, 37, 3 },
    249     };
    250     int arg = 1;
    251     const char *filename;
    252     SDL_Surface *original;
    253     SDL_Surface *converted;
    254     SDL_Window *window;
    255     SDL_Renderer *renderer;
    256     SDL_Texture *output[3];
    257     const char *titles[3] = { "ORIGINAL", "SOFTWARE", "HARDWARE" };
    258     char title[128];
    259     const char *yuv_name;
    260     const char *yuv_mode;
    261     Uint32 rgb_format = SDL_PIXELFORMAT_RGBX8888;
    262     Uint32 yuv_format = SDL_PIXELFORMAT_YV12;
    263     int current = 0;
    264     int pitch;
    265     Uint8 *raw_yuv;
    266     Uint32 then, now, i, iterations = 100;
    267     SDL_bool should_run_automated_tests = SDL_FALSE;
    268 
    269     while (argv[arg] && *argv[arg] == '-') {
    270         if (SDL_strcmp(argv[arg], "--jpeg") == 0) {
    271             SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_JPEG);
    272         } else if (SDL_strcmp(argv[arg], "--bt601") == 0) {
    273             SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_BT601);
    274         } else if (SDL_strcmp(argv[arg], "--bt709") == 0) {
    275             SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_BT709);
    276         } else if (SDL_strcmp(argv[arg], "--auto") == 0) {
    277             SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_AUTOMATIC);
    278         } else if (SDL_strcmp(argv[arg], "--yv12") == 0) {
    279             yuv_format = SDL_PIXELFORMAT_YV12;
    280         } else if (SDL_strcmp(argv[arg], "--iyuv") == 0) {
    281             yuv_format = SDL_PIXELFORMAT_IYUV;
    282         } else if (SDL_strcmp(argv[arg], "--yuy2") == 0) {
    283             yuv_format = SDL_PIXELFORMAT_YUY2;
    284         } else if (SDL_strcmp(argv[arg], "--uyvy") == 0) {
    285             yuv_format = SDL_PIXELFORMAT_UYVY;
    286         } else if (SDL_strcmp(argv[arg], "--yvyu") == 0) {
    287             yuv_format = SDL_PIXELFORMAT_YVYU;
    288         } else if (SDL_strcmp(argv[arg], "--nv12") == 0) {
    289             yuv_format = SDL_PIXELFORMAT_NV12;
    290         } else if (SDL_strcmp(argv[arg], "--nv21") == 0) {
    291             yuv_format = SDL_PIXELFORMAT_NV21;
    292         } else if (SDL_strcmp(argv[arg], "--rgb555") == 0) {
    293             rgb_format = SDL_PIXELFORMAT_RGB555;
    294         } else if (SDL_strcmp(argv[arg], "--rgb565") == 0) {
    295             rgb_format = SDL_PIXELFORMAT_RGB565;
    296         } else if (SDL_strcmp(argv[arg], "--rgb24") == 0) {
    297             rgb_format = SDL_PIXELFORMAT_RGB24;
    298         } else if (SDL_strcmp(argv[arg], "--argb") == 0) {
    299             rgb_format = SDL_PIXELFORMAT_ARGB8888;
    300         } else if (SDL_strcmp(argv[arg], "--abgr") == 0) {
    301             rgb_format = SDL_PIXELFORMAT_ABGR8888;
    302         } else if (SDL_strcmp(argv[arg], "--rgba") == 0) {
    303             rgb_format = SDL_PIXELFORMAT_RGBA8888;
    304         } else if (SDL_strcmp(argv[arg], "--bgra") == 0) {
    305             rgb_format = SDL_PIXELFORMAT_BGRA8888;
    306         } else if (SDL_strcmp(argv[arg], "--automated") == 0) {
    307             should_run_automated_tests = SDL_TRUE;
    308         } else {
    309             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Usage: %s [--jpeg|--bt601|-bt709|--auto] [--yv12|--iyuv|--yuy2|--uyvy|--yvyu|--nv12|--nv21] [--rgb555|--rgb565|--rgb24|--argb|--abgr|--rgba|--bgra] [image_filename]\n", argv[0]);
    310             return 1;
    311         }
    312         ++arg;
    313     }
    314 
    315     /* Run automated tests */
    316     if (should_run_automated_tests) {
    317         for (i = 0; i < SDL_arraysize(automated_test_params); ++i) {
    318             SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Running automated test, pattern size %d, extra pitch %d, intrinsics %s\n", 
    319                 automated_test_params[i].pattern_size,
    320                 automated_test_params[i].extra_pitch,
    321                 automated_test_params[i].enable_intrinsics ? "enabled" : "disabled");
    322             if (run_automated_tests(automated_test_params[i].pattern_size, automated_test_params[i].extra_pitch) < 0) {
    323                 return 2;
    324             }
    325         }
    326         return 0;
    327     }
    328 
    329     if (argv[arg]) {
    330         filename = argv[arg];
    331     } else {
    332         filename = "testyuv.bmp";
    333     }
    334     original = SDL_ConvertSurfaceFormat(SDL_LoadBMP(filename), SDL_PIXELFORMAT_RGB24, 0);
    335     if (!original) {
    336         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename, SDL_GetError());
    337         return 3;
    338     }
    339 
    340     raw_yuv = SDL_calloc(1, MAX_YUV_SURFACE_SIZE(original->w, original->h, 0));
    341     ConvertRGBtoYUV(yuv_format, original->pixels, original->pitch, raw_yuv, original->w, original->h,
    342         SDL_GetYUVConversionModeForResolution(original->w, original->h),
    343         0, 100);
    344     pitch = CalculateYUVPitch(yuv_format, original->w);
    345 
    346     converted = SDL_CreateRGBSurfaceWithFormat(0, original->w, original->h, 0, rgb_format);
    347     if (!converted) {
    348         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create converted surface: %s\n", SDL_GetError());
    349         return 3;
    350     }
    351 
    352     then = SDL_GetTicks();
    353     for ( i = 0; i < iterations; ++i ) {
    354         SDL_ConvertPixels(original->w, original->h, yuv_format, raw_yuv, pitch, rgb_format, converted->pixels, converted->pitch);
    355     }
    356     now = SDL_GetTicks();
    357     SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%d iterations in %d ms, %.2fms each\n", iterations, (now - then), (float)(now - then)/iterations);
    358 
    359     window = SDL_CreateWindow("YUV test",
    360                               SDL_WINDOWPOS_UNDEFINED,
    361                               SDL_WINDOWPOS_UNDEFINED,
    362                               original->w, original->h,
    363                               0);
    364     if (!window) {
    365         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
    366         return 4;
    367     }
    368 
    369     renderer = SDL_CreateRenderer(window, -1, 0);
    370     if (!renderer) {
    371         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
    372         return 4;
    373     }
    374 
    375     output[0] = SDL_CreateTextureFromSurface(renderer, original);
    376     output[1] = SDL_CreateTextureFromSurface(renderer, converted);
    377     output[2] = SDL_CreateTexture(renderer, yuv_format, SDL_TEXTUREACCESS_STREAMING, original->w, original->h);
    378     if (!output[0] || !output[1] || !output[2]) {
    379         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create texture: %s\n", SDL_GetError());
    380         return 5;
    381     }
    382     SDL_UpdateTexture(output[2], NULL, raw_yuv, pitch);
    383     
    384     yuv_name = SDL_GetPixelFormatName(yuv_format);
    385     if (SDL_strncmp(yuv_name, "SDL_PIXELFORMAT_", 16) == 0) {
    386         yuv_name += 16;
    387     }
    388 
    389     switch (SDL_GetYUVConversionModeForResolution(original->w, original->h)) {
    390     case SDL_YUV_CONVERSION_JPEG:
    391         yuv_mode = "JPEG";
    392         break;
    393     case SDL_YUV_CONVERSION_BT601:
    394         yuv_mode = "BT.601";
    395         break;
    396     case SDL_YUV_CONVERSION_BT709:
    397         yuv_mode = "BT.709";
    398         break;
    399     default:
    400         yuv_mode = "UNKNOWN";
    401         break;
    402     }
    403 
    404     { int done = 0;
    405         while ( !done )
    406         {
    407             SDL_Event event;
    408             while (SDL_PollEvent(&event) > 0) {
    409                 if (event.type == SDL_QUIT) {
    410                     done = 1;
    411                 }
    412                 if (event.type == SDL_KEYDOWN) {
    413                     if (event.key.keysym.sym == SDLK_ESCAPE) {
    414                         done = 1;
    415                     } else if (event.key.keysym.sym == SDLK_LEFT) {
    416                         --current;
    417                     } else if (event.key.keysym.sym == SDLK_RIGHT) {
    418                         ++current;
    419                     }
    420                 }
    421                 if (event.type == SDL_MOUSEBUTTONDOWN) {
    422                     if (event.button.x < (original->w/2)) {
    423                         --current;
    424                     } else {
    425                         ++current;
    426                     }
    427                 }
    428             }
    429 
    430             /* Handle wrapping */
    431             if (current < 0) {
    432                 current += SDL_arraysize(output);
    433             }
    434             if (current >= SDL_arraysize(output)) {
    435                 current -= SDL_arraysize(output);
    436             }
    437 
    438             SDL_RenderClear(renderer);
    439             SDL_RenderCopy(renderer, output[current], NULL, NULL);
    440             SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
    441             if (current == 0) {
    442                 SDLTest_DrawString(renderer, 4, 4, titles[current]);
    443             } else {
    444                 SDL_snprintf(title, sizeof(title), "%s %s %s", titles[current], yuv_name, yuv_mode);
    445                 SDLTest_DrawString(renderer, 4, 4, title);
    446             }
    447             SDL_RenderPresent(renderer);
    448             SDL_Delay(10);
    449         }
    450     }
    451     SDL_Quit();
    452     return 0;
    453 }
    454 
    455 /* vi: set ts=4 sw=4 expandtab: */