| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343 |
- /*
- Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely.
- */
- #include <SDL3/SDL.h>
- #include <SDL3/SDL_main.h>
- #include <SDL3/SDL_test.h>
- #include "testyuv_cvt.h"
- #include "testutils.h"
- /* 422 (YUY2, etc) and I4FL formats are the largest */
- #define MAX_YUV_SURFACE_SIZE(W, H, P) ((H + 1) * ((W + 1) + P) * 3 * 2)
- /* Return true if the YUV format is packed pixels */
- static bool is_packed_yuv_format(Uint32 format)
- {
- return format == SDL_PIXELFORMAT_YUY2 || format == SDL_PIXELFORMAT_UYVY || format == SDL_PIXELFORMAT_YVYU;
- }
- /* Create a surface with a good pattern for verifying YUV conversion */
- static SDL_Surface *generate_test_pattern(int pattern_size)
- {
- SDL_Surface *pattern = SDL_CreateSurface(pattern_size, pattern_size, SDL_PIXELFORMAT_RGB24);
- if (pattern) {
- int i, x, y;
- Uint8 *p, c;
- const int thickness = 2; /* Important so 2x2 blocks of color are the same, to avoid Cr/Cb interpolation over pixels */
- /* R, G, B in alternating horizontal bands */
- for (y = 0; y < pattern->h - (thickness - 1); y += thickness) {
- for (i = 0; i < thickness; ++i) {
- p = (Uint8 *)pattern->pixels + (y + i) * pattern->pitch + ((y / thickness) % 3);
- for (x = 0; x < pattern->w; ++x) {
- *p = 0xFF;
- p += 3;
- }
- }
- }
- /* Black and white in alternating vertical bands */
- c = 0xFF;
- for (x = 1 * thickness; x < pattern->w; x += 2 * thickness) {
- for (i = 0; i < thickness; ++i) {
- p = (Uint8 *)pattern->pixels + (x + i) * 3;
- for (y = 0; y < pattern->h; ++y) {
- SDL_memset(p, c, 3);
- p += pattern->pitch;
- }
- }
- if (c) {
- c = 0x00;
- } else {
- c = 0xFF;
- }
- }
- }
- return pattern;
- }
- static bool verify_yuv_data(Uint32 format, SDL_Colorspace colorspace, const Uint8 *yuv, int yuv_pitch, SDL_Surface *surface, int tolerance)
- {
- const int size = (surface->h * surface->pitch);
- Uint8 *rgb;
- bool result = false;
- rgb = (Uint8 *)SDL_malloc(size);
- if (!rgb) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory");
- return false;
- }
- if (SDL_ConvertPixelsAndColorspace(surface->w, surface->h, format, colorspace, 0, yuv, yuv_pitch, surface->format, SDL_COLORSPACE_SRGB, 0, rgb, surface->pitch)) {
- int x, y;
- result = true;
- for (y = 0; y < surface->h; ++y) {
- const Uint8 *actual = rgb + y * surface->pitch;
- const Uint8 *expected = (const Uint8 *)surface->pixels + y * surface->pitch;
- for (x = 0; x < surface->w; ++x) {
- int deltaR = (int)actual[0] - expected[0];
- int deltaG = (int)actual[1] - expected[1];
- int deltaB = (int)actual[2] - expected[2];
- int distance = (deltaR * deltaR + deltaG * deltaG + deltaB * deltaB);
- if (distance > tolerance) {
- 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", x, y, actual[0], actual[1], actual[2], expected[0], expected[1], expected[2], distance);
- result = false;
- }
- actual += 3;
- expected += 3;
- }
- }
- } else {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s", SDL_GetPixelFormatName(format), SDL_GetPixelFormatName(surface->format), SDL_GetError());
- }
- SDL_free(rgb);
- return result;
- }
- static bool run_automated_tests(int pattern_size, int extra_pitch)
- {
- const Uint32 formats[] = {
- SDL_PIXELFORMAT_YV12,
- SDL_PIXELFORMAT_IYUV,
- SDL_PIXELFORMAT_I444,
- SDL_PIXELFORMAT_NV12,
- SDL_PIXELFORMAT_NV21,
- SDL_PIXELFORMAT_YUY2,
- SDL_PIXELFORMAT_UYVY,
- SDL_PIXELFORMAT_YVYU
- };
- int i, j;
- SDL_Surface *pattern = generate_test_pattern(pattern_size);
- const int yuv_len = MAX_YUV_SURFACE_SIZE(pattern->w, pattern->h, extra_pitch);
- Uint8 *yuv1 = (Uint8 *)SDL_malloc(yuv_len);
- Uint8 *yuv2 = (Uint8 *)SDL_malloc(yuv_len);
- int yuv1_pitch, yuv2_pitch;
- YUV_CONVERSION_MODE mode;
- SDL_Colorspace colorspace;
- const int tight_tolerance = 20;
- const int loose_tolerance = 333;
- bool result = false;
- if (!pattern || !yuv1 || !yuv2) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't allocate test surfaces");
- goto done;
- }
- mode = GetYUVConversionModeForResolution(pattern->w, pattern->h);
- colorspace = GetColorspaceForYUVConversionMode(mode);
- /* Verify conversion from YUV formats */
- for (i = 0; i < SDL_arraysize(formats); ++i) {
- if (!ConvertRGBtoYUV(formats[i], pattern->pixels, pattern->pitch, yuv1, pattern->w, pattern->h, mode, 0, 100)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ConvertRGBtoYUV() doesn't support converting to %s", SDL_GetPixelFormatName(formats[i]));
- goto done;
- }
- yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w);
- if (!verify_yuv_data(formats[i], colorspace, yuv1, yuv1_pitch, pattern, tight_tolerance)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to RGB", SDL_GetPixelFormatName(formats[i]));
- goto done;
- }
- }
- /* Verify conversion to YUV formats */
- for (i = 0; i < SDL_arraysize(formats); ++i) {
- yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch;
- if (!SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, formats[i], colorspace, 0, yuv1, yuv1_pitch)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s", SDL_GetPixelFormatName(pattern->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError());
- goto done;
- }
- if (!verify_yuv_data(formats[i], colorspace, yuv1, yuv1_pitch, pattern, tight_tolerance)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from RGB to %s", SDL_GetPixelFormatName(formats[i]));
- goto done;
- }
- }
- /* Verify conversion between YUV formats */
- for (i = 0; i < SDL_arraysize(formats); ++i) {
- for (j = 0; j < SDL_arraysize(formats); ++j) {
- if (formats[i] != formats[j] && (formats[i] == SDL_PIXELFORMAT_I444 || formats[j] == SDL_PIXELFORMAT_I444)) {
- // Converting between 444 and 420 formats is lossy and not currently supported
- continue;
- }
- yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch;
- yuv2_pitch = CalculateYUVPitch(formats[j], pattern->w) + extra_pitch;
- if (!SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, formats[i], colorspace, 0, yuv1, yuv1_pitch)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s", SDL_GetPixelFormatName(pattern->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError());
- goto done;
- }
- if (!SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, formats[i], colorspace, 0, yuv1, yuv1_pitch, formats[j], colorspace, 0, yuv2, yuv2_pitch)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError());
- goto done;
- }
- if (!verify_yuv_data(formats[j], colorspace, yuv2, yuv2_pitch, pattern, tight_tolerance)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to %s", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]));
- goto done;
- }
- }
- }
- /* Verify conversion between YUV formats in-place */
- for (i = 0; i < SDL_arraysize(formats); ++i) {
- for (j = 0; j < SDL_arraysize(formats); ++j) {
- if (is_packed_yuv_format(formats[i]) != is_packed_yuv_format(formats[j])) {
- /* Can't change plane vs packed pixel layout in-place */
- continue;
- }
- if (formats[i] != formats[j] && (formats[i] == SDL_PIXELFORMAT_I444 || formats[j] == SDL_PIXELFORMAT_I444)) {
- // Converting between 444 and 420 formats is lossy and not currently supported
- continue;
- }
- yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch;
- yuv2_pitch = CalculateYUVPitch(formats[j], pattern->w) + extra_pitch;
- if (!SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, formats[i], colorspace, 0, yuv1, yuv1_pitch)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s", SDL_GetPixelFormatName(pattern->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError());
- goto done;
- }
- if (!SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, formats[i], colorspace, 0, yuv1, yuv1_pitch, formats[j], colorspace, 0, yuv1, yuv2_pitch)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError());
- goto done;
- }
- if (!verify_yuv_data(formats[j], colorspace, yuv1, yuv2_pitch, pattern, tight_tolerance)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to %s", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]));
- goto done;
- }
- }
- }
- /* Verify round trip through BT.2020 */
- colorspace = SDL_COLORSPACE_BT2020_FULL;
- if (!ConvertRGBtoYUV(SDL_PIXELFORMAT_P010, pattern->pixels, pattern->pitch, yuv1, pattern->w, pattern->h, YUV_CONVERSION_BT2020, 0, 100)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ConvertRGBtoYUV() doesn't support converting to %s", SDL_GetPixelFormatName(SDL_PIXELFORMAT_P010));
- goto done;
- }
- yuv1_pitch = CalculateYUVPitch(SDL_PIXELFORMAT_P010, pattern->w);
- if (!verify_yuv_data(SDL_PIXELFORMAT_P010, colorspace, yuv1, yuv1_pitch, pattern, tight_tolerance)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to RGB", SDL_GetPixelFormatName(SDL_PIXELFORMAT_P010));
- goto done;
- }
- /* The pitch needs to be Uint16 aligned for P010 pixels */
- yuv1_pitch = CalculateYUVPitch(SDL_PIXELFORMAT_P010, pattern->w) + ((extra_pitch + 1) & ~1);
- if (!SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, SDL_PIXELFORMAT_P010, colorspace, 0, yuv1, yuv1_pitch)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s", SDL_GetPixelFormatName(pattern->format), SDL_GetPixelFormatName(SDL_PIXELFORMAT_P010), SDL_GetError());
- goto done;
- }
- /* Going through XRGB2101010 format during P010 conversion is slightly lossy, so use looser tolerance here */
- if (!verify_yuv_data(SDL_PIXELFORMAT_P010, colorspace, yuv1, yuv1_pitch, pattern, loose_tolerance)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from RGB to %s", SDL_GetPixelFormatName(SDL_PIXELFORMAT_P010));
- goto done;
- }
- result = true;
- done:
- SDL_free(yuv1);
- SDL_free(yuv2);
- SDL_DestroySurface(pattern);
- return result;
- }
- static bool run_colorspace_test(void)
- {
- bool result = false;
- SDL_Window *window;
- SDL_Renderer *renderer;
- struct {
- const char *name;
- SDL_Colorspace colorspace;
- } colorspaces[] = {
- #define COLORSPACE(X) { #X, X }
- COLORSPACE(SDL_COLORSPACE_JPEG),
- #if 0 /* We don't support converting color primaries here */
- COLORSPACE(SDL_COLORSPACE_BT601_LIMITED),
- COLORSPACE(SDL_COLORSPACE_BT601_FULL),
- #endif
- COLORSPACE(SDL_COLORSPACE_BT709_LIMITED),
- COLORSPACE(SDL_COLORSPACE_BT709_FULL)
- #undef COLORSPACE
- };
- SDL_Surface *rgb = NULL;
- SDL_Surface *yuv = NULL;
- SDL_Texture *texture = NULL;
- int allowed_error = 2;
- int i;
- if (!SDL_CreateWindowAndRenderer("testyuv", 320, 240, 0, &window, &renderer)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window and renderer: %s", SDL_GetError());
- goto done;
- }
- rgb = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_XRGB8888);
- if (!rgb) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create RGB surface: %s", SDL_GetError());
- goto done;
- }
- SDL_FillSurfaceRect(rgb, NULL, SDL_MapSurfaceRGB(rgb, 255, 0, 0));
- for (i = 0; i < SDL_arraysize(colorspaces); ++i) {
- bool next = false;
- Uint8 r, g, b, a;
- SDL_Log("Checking colorspace %s", colorspaces[i].name);
- yuv = SDL_ConvertSurfaceAndColorspace(rgb, SDL_PIXELFORMAT_NV12, NULL, colorspaces[i].colorspace, 0);
- if (!yuv) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create YUV surface: %s", SDL_GetError());
- goto done;
- }
- if (!SDL_ReadSurfacePixel(yuv, 0, 0, &r, &g, &b, &a)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't read YUV surface: %s", SDL_GetError());
- goto done;
- }
- if (SDL_abs((int)r - 255) > allowed_error ||
- SDL_abs((int)g - 0) > allowed_error ||
- SDL_abs((int)b - 0) > allowed_error) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed color conversion, expected 255,0,0, got %d,%d,%d", r, g, b);
- }
- texture = SDL_CreateTextureFromSurface(renderer, yuv);
- if (!texture) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create YUV texture: %s", SDL_GetError());
- goto done;
- }
- SDL_DestroySurface(yuv);
- yuv = NULL;
- SDL_RenderTexture(renderer, texture, NULL, NULL);
- yuv = SDL_RenderReadPixels(renderer, NULL);
- if (!SDL_ReadSurfacePixel(yuv, 0, 0, &r, &g, &b, &a)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't read YUV surface: %s", SDL_GetError());
- goto done;
- }
- if (SDL_abs((int)r - 255) > allowed_error ||
- SDL_abs((int)g - 0) > allowed_error ||
- SDL_abs((int)b - 0) > allowed_error) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed renderer color conversion, expected 255,0,0, got %d,%d,%d", r, g, b);
- }
- while (!next) {
- SDL_Event event;
- while (SDL_PollEvent(&event)) {
- switch (event.type) {
- case SDL_EVENT_KEY_DOWN:
- if (event.key.key == SDLK_ESCAPE) {
- result = true;
- goto done;
- }
- if (event.key.key == SDLK_SPACE) {
- next = true;
- }
- break;
- case SDL_EVENT_QUIT:
- result = true;
- goto done;
- }
- }
- SDL_RenderTexture(renderer, texture, NULL, NULL);
- SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
- SDL_RenderDebugText(renderer, 4, 4, colorspaces[i].name);
- SDL_RenderPresent(renderer);
- SDL_Delay(10);
- }
- SDL_DestroyTexture(texture);
- texture = NULL;
- }
- result = true;
- done:
- SDL_DestroySurface(rgb);
- SDL_DestroySurface(yuv);
- SDL_DestroyTexture(texture);
- SDL_Quit();
- return result;
- }
- // Copied from SDL_yuv_sw.c to verify functionality
- struct SW_YUVTexture
- {
- SDL_PixelFormat format;
- int w, h;
- Uint8 *pixels;
- int pitches[3];
- Uint8 *planes[3];
- };
- typedef struct SW_YUVTexture SW_YUVTexture;
- static SW_YUVTexture *SW_CreateYUVTexture(SDL_PixelFormat format, int w, int h, void *pixels, int pitch)
- {
- SW_YUVTexture *swdata;
- switch (format) {
- case SDL_PIXELFORMAT_YV12:
- case SDL_PIXELFORMAT_IYUV:
- case SDL_PIXELFORMAT_I444:
- case SDL_PIXELFORMAT_I0FL:
- case SDL_PIXELFORMAT_I4FL:
- case SDL_PIXELFORMAT_YUY2:
- case SDL_PIXELFORMAT_UYVY:
- case SDL_PIXELFORMAT_YVYU:
- case SDL_PIXELFORMAT_NV12:
- case SDL_PIXELFORMAT_NV21:
- break;
- default:
- SDL_SetError("Unsupported YUV format");
- return NULL;
- }
- swdata = (SW_YUVTexture *)SDL_calloc(1, sizeof(*swdata));
- if (!swdata) {
- return NULL;
- }
- swdata->format = format;
- swdata->w = w;
- swdata->h = h;
- swdata->pixels = (Uint8 *)pixels;
- // Find the pitch and offset values for the texture
- const int bpp = SDL_BYTESPERPIXEL(format);
- switch (format) {
- case SDL_PIXELFORMAT_YV12:
- case SDL_PIXELFORMAT_IYUV:
- case SDL_PIXELFORMAT_I0FL:
- swdata->pitches[0] = pitch;
- swdata->pitches[1] = (((swdata->pitches[0] / bpp) + 1) / 2) * bpp;
- swdata->pitches[2] = swdata->pitches[1];
- swdata->planes[0] = swdata->pixels;
- swdata->planes[1] = swdata->planes[0] + swdata->pitches[0] * h;
- swdata->planes[2] = swdata->planes[1] + swdata->pitches[1] * ((h + 1) / 2);
- break;
- case SDL_PIXELFORMAT_I444:
- case SDL_PIXELFORMAT_I4FL:
- swdata->pitches[0] = w * bpp;
- swdata->pitches[1] = swdata->pitches[0];
- swdata->pitches[2] = swdata->pitches[1];
- swdata->planes[0] = swdata->pixels;
- swdata->planes[1] = swdata->planes[0] + swdata->pitches[0] * h;
- swdata->planes[2] = swdata->planes[1] + swdata->pitches[1] * h;
- break;
- case SDL_PIXELFORMAT_YUY2:
- case SDL_PIXELFORMAT_UYVY:
- case SDL_PIXELFORMAT_YVYU:
- swdata->pitches[0] = ((w + 1) / 2) * 4;
- swdata->planes[0] = swdata->pixels;
- break;
- case SDL_PIXELFORMAT_NV12:
- case SDL_PIXELFORMAT_NV21:
- swdata->pitches[0] = w;
- swdata->pitches[1] = 2 * ((swdata->pitches[0] + 1) / 2);
- swdata->planes[0] = swdata->pixels;
- swdata->planes[1] = swdata->planes[0] + swdata->pitches[0] * h;
- break;
- default:
- SDL_assert(!"We should never get here (caught above)");
- break;
- }
- // We're all done..
- return swdata;
- }
- static bool SW_UpdateYUVTexture(SW_YUVTexture *swdata, const SDL_Rect *rect, const void *pixels, int pitch)
- {
- const int bpp = SDL_BYTESPERPIXEL(swdata->format);
- switch (swdata->format) {
- case SDL_PIXELFORMAT_YV12:
- case SDL_PIXELFORMAT_IYUV:
- case SDL_PIXELFORMAT_I0FL:
- if (rect->x == 0 && rect->y == 0 &&
- rect->w == swdata->w && rect->h == swdata->h && pitch == swdata->pitches[0]) {
- SDL_memcpy(swdata->pixels, pixels,
- (size_t)(swdata->h * swdata->w * bpp) + 2 * ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2) * bpp);
- } else {
- Uint8 *src, *dst;
- int row;
- size_t length;
- const int UVpitch = ((pitch / bpp + 1) / 2) * bpp;
- // Copy the Y plane
- src = (Uint8 *)pixels;
- dst = swdata->pixels + rect->y * swdata->w * bpp + rect->x * bpp;
- length = rect->w * bpp;
- for (row = 0; row < rect->h; ++row) {
- SDL_memcpy(dst, src, length);
- src += pitch;
- dst += swdata->pitches[0];
- }
- // Copy the next plane
- src = (Uint8 *)pixels + rect->h * pitch;
- dst = swdata->pixels + swdata->h * swdata->pitches[0];
- dst += (rect->y / 2) * swdata->pitches[1] + (rect->x / 2) * bpp;
- length = ((rect->w + 1) / 2) * bpp;
- for (row = 0; row < (rect->h + 1) / 2; ++row) {
- SDL_memcpy(dst, src, length);
- src += UVpitch;
- dst += swdata->pitches[1];
- }
- // Copy the next plane
- src = (Uint8 *)pixels + rect->h * pitch + ((rect->h + 1) / 2) * UVpitch;
- dst = swdata->pixels + swdata->h * swdata->pitches[0] + ((rect->h + 1) / 2) * swdata->pitches[1];
- dst += (rect->y / 2) * swdata->pitches[2] + (rect->x / 2) * bpp;
- length = ((rect->w + 1) / 2) * bpp;
- for (row = 0; row < (rect->h + 1) / 2; ++row) {
- SDL_memcpy(dst, src, length);
- src += UVpitch;
- dst += swdata->pitches[2];
- }
- }
- break;
- case SDL_PIXELFORMAT_I444:
- case SDL_PIXELFORMAT_I4FL:
- if (rect->x == 0 && rect->y == 0 &&
- rect->w == swdata->w && rect->h == swdata->h && pitch == swdata->pitches[0]) {
- SDL_memcpy(swdata->pixels, pixels, (size_t)(swdata->h * pitch * 3));
- } else {
- Uint8 *src, *dst;
- int row;
- size_t length;
- // Copy the Y plane
- src = (Uint8 *)pixels;
- dst = swdata->pixels;
- dst += rect->y * swdata->pitches[0] + rect->x * bpp;
- length = rect->w * bpp;
- for (row = 0; row < rect->h; ++row) {
- SDL_memcpy(dst, src, length);
- src += pitch;
- dst += swdata->pitches[0];
- }
- // Copy the next plane
- dst = swdata->pixels + swdata->h * swdata->pitches[0];
- dst += rect->y * swdata->pitches[1] + rect->x * bpp;
- for (row = 0; row < rect->h; ++row) {
- SDL_memcpy(dst, src, length);
- src += pitch;
- dst += swdata->pitches[1];
- }
- // Copy the next plane
- dst = swdata->pixels + swdata->h * swdata->pitches[0] + swdata->h * swdata->pitches[1];
- dst += rect->y * swdata->pitches[2] + rect->x * bpp;
- for (row = 0; row < rect->h; ++row) {
- SDL_memcpy(dst, src, length);
- src += pitch;
- dst += swdata->pitches[2];
- }
- }
- break;
- case SDL_PIXELFORMAT_YUY2:
- case SDL_PIXELFORMAT_UYVY:
- case SDL_PIXELFORMAT_YVYU:
- {
- Uint8 *src, *dst;
- int row;
- size_t length;
- src = (Uint8 *)pixels;
- dst =
- swdata->planes[0] + rect->y * swdata->pitches[0] +
- rect->x * 2;
- length = 4 * (((size_t)rect->w + 1) / 2);
- for (row = 0; row < rect->h; ++row) {
- SDL_memcpy(dst, src, length);
- src += pitch;
- dst += swdata->pitches[0];
- }
- } break;
- case SDL_PIXELFORMAT_NV12:
- case SDL_PIXELFORMAT_NV21:
- {
- if (rect->x == 0 && rect->y == 0 && rect->w == swdata->w && rect->h == swdata->h) {
- SDL_memcpy(swdata->pixels, pixels,
- (size_t)(swdata->h * swdata->w) + 2 * ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2));
- } else {
- Uint8 *src, *dst;
- int row;
- size_t length;
- // Copy the Y plane
- src = (Uint8 *)pixels;
- dst = swdata->pixels + rect->y * swdata->w + rect->x;
- length = rect->w;
- for (row = 0; row < rect->h; ++row) {
- SDL_memcpy(dst, src, length);
- src += pitch;
- dst += swdata->w;
- }
- // Copy the next plane
- src = (Uint8 *)pixels + rect->h * pitch;
- dst = swdata->pixels + swdata->h * swdata->w;
- dst += 2 * ((rect->y + 1) / 2) * ((swdata->w + 1) / 2) + 2 * (rect->x / 2);
- length = 2 * (((size_t)rect->w + 1) / 2);
- for (row = 0; row < (rect->h + 1) / 2; ++row) {
- SDL_memcpy(dst, src, length);
- src += 2 * ((pitch + 1) / 2);
- dst += 2 * ((swdata->w + 1) / 2);
- }
- }
- } break;
- default:
- return SDL_SetError("Unsupported YUV format");
- }
- return true;
- }
- static void SW_DestroyYUVTexture(SW_YUVTexture *swdata)
- {
- if (swdata) {
- SDL_free(swdata);
- }
- }
- static bool update_locked_texture(SDL_PixelFormat format, int w, int h, const void *pixels, int pitch, void *output_pixels, int output_pitch)
- {
- SW_YUVTexture *swdata = SW_CreateYUVTexture(format, w, h, output_pixels, output_pitch);
- if (!swdata) {
- return false;
- }
- SDL_Rect rect = { 0, 0, w, h };
- bool result = SW_UpdateYUVTexture(swdata, &rect, pixels, pitch);
- SW_DestroyYUVTexture(swdata);
- return result;
- }
- static bool create_textures(SDL_Renderer *renderer, SDL_Surface *original, SDL_PixelFormat yuv_format, SDL_PixelFormat rgb_format, bool planar, bool lock, bool monochrome, int luminance, SDL_Texture *output[3])
- {
- SDL_Colorspace rgb_colorspace = SDL_COLORSPACE_SRGB;
- SDL_Colorspace yuv_colorspace;
- Uint8 *raw_yuv = NULL;
- int pitch;
- SDL_Surface *converted = NULL;
- bool result = false;
- size_t max_size;
- YUV_CONVERSION_MODE yuv_mode = GetYUVConversionModeForResolution(original->w, original->h);
- if (yuv_mode == YUV_CONVERSION_BT2020) {
- rgb_colorspace = SDL_COLORSPACE_HDR10;
- }
- yuv_colorspace = GetColorspaceForYUVConversionMode(yuv_mode);
- max_size = MAX_YUV_SURFACE_SIZE(original->w, original->h, 0);
- raw_yuv = SDL_calloc(1, max_size);
- ConvertRGBtoYUV(yuv_format, original->pixels, original->pitch, raw_yuv, original->w, original->h, yuv_mode, monochrome, luminance);
- pitch = CalculateYUVPitch(yuv_format, original->w);
- converted = SDL_CreateSurface(original->w, original->h, rgb_format);
- if (!converted) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create converted surface: %s", SDL_GetError());
- goto done;
- }
- SDL_ConvertPixelsAndColorspace(original->w, original->h, yuv_format, yuv_colorspace, 0, raw_yuv, pitch, rgb_format, rgb_colorspace, 0, converted->pixels, converted->pitch);
- output[0] = SDL_CreateTextureFromSurface(renderer, original);
- output[1] = SDL_CreateTextureFromSurface(renderer, converted);
- SDL_PropertiesID props = SDL_CreateProperties();
- SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, yuv_colorspace);
- SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, yuv_format);
- SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STREAMING);
- SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, original->w);
- SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, original->h);
- output[2] = SDL_CreateTextureWithProperties(renderer, props);
- SDL_DestroyProperties(props);
- if (!output[0] || !output[1] || !output[2]) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create texture: %s", SDL_GetError());
- goto done;
- }
- if (planar && (yuv_format == SDL_PIXELFORMAT_YV12 || yuv_format == SDL_PIXELFORMAT_IYUV || yuv_format == SDL_PIXELFORMAT_I0FL)) {
- const int bpp = SDL_BYTESPERPIXEL(yuv_format);
- const int Yrows = original->h;
- const int UVrows = ((original->h + 1) / 2);
- const int src_Ypitch = pitch;
- const int src_UVpitch = ((pitch / bpp + 1) / 2) * bpp;
- const Uint8 *src_plane0 = (const Uint8 *)raw_yuv;
- const Uint8 *src_plane1 = src_plane0 + Yrows * src_Ypitch;
- const Uint8 *src_plane2 = src_plane1 + UVrows * src_UVpitch;
- const int Ypitch = pitch + 37;
- const int UVpitch = ((Ypitch / bpp + 1) / 2) * bpp;
- Uint8 *plane0 = (Uint8 *)SDL_calloc(1, Yrows * Ypitch);
- Uint8 *plane1 = (Uint8 *)SDL_calloc(1, UVrows * UVpitch);
- Uint8 *plane2 = (Uint8 *)SDL_calloc(1, UVrows * UVpitch);
- int row;
- const Uint8 *src;
- Uint8 *dst;
- if (!plane0 || !plane1 || !plane0) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create YUV planes: %s", SDL_GetError());
- goto done;
- }
- src = src_plane0;
- dst = plane0;
- for (row = 0; row < Yrows; ++row) {
- SDL_memcpy(dst, src, src_Ypitch);
- src += src_Ypitch;
- dst += Ypitch;
- }
- src = src_plane1;
- dst = plane1;
- for (row = 0; row < UVrows; ++row) {
- SDL_memcpy(dst, src, src_UVpitch);
- src += src_UVpitch;
- dst += UVpitch;
- }
- src = src_plane2;
- dst = plane2;
- for (row = 0; row < UVrows; ++row) {
- SDL_memcpy(dst, src, src_UVpitch);
- src += src_UVpitch;
- dst += UVpitch;
- }
- if (yuv_format == SDL_PIXELFORMAT_YV12) {
- SDL_UpdateYUVTexture(output[2], NULL, plane0, Ypitch, plane2, UVpitch, plane1, UVpitch);
- } else {
- SDL_UpdateYUVTexture(output[2], NULL, plane0, Ypitch, plane1, UVpitch, plane2, UVpitch);
- }
- SDL_free(plane0);
- SDL_free(plane1);
- SDL_free(plane2);
- } else if (planar && (yuv_format == SDL_PIXELFORMAT_I444 || yuv_format == SDL_PIXELFORMAT_I4FL)) {
- const int rows = original->h;
- const Uint8 *src_plane0 = (const Uint8 *)raw_yuv;
- const Uint8 *src_plane1 = src_plane0 + rows * pitch;
- const Uint8 *src_plane2 = src_plane1 + rows * pitch;
- const int Ypitch = pitch + 37;
- const int UVpitch = Ypitch;
- Uint8 *plane0 = (Uint8 *)SDL_calloc(1, rows * Ypitch);
- Uint8 *plane1 = (Uint8 *)SDL_calloc(1, rows * UVpitch);
- Uint8 *plane2 = (Uint8 *)SDL_calloc(1, rows * UVpitch);
- int row;
- const Uint8 *src;
- Uint8 *dst;
- if (!plane0 || !plane1 || !plane0) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create YUV planes: %s", SDL_GetError());
- goto done;
- }
- src = src_plane0;
- dst = plane0;
- for (row = 0; row < rows; ++row) {
- SDL_memcpy(dst, src, pitch);
- src += pitch;
- dst += Ypitch;
- }
- src = src_plane1;
- dst = plane1;
- for (row = 0; row < rows; ++row) {
- SDL_memcpy(dst, src, pitch);
- src += pitch;
- dst += UVpitch;
- }
- src = src_plane2;
- dst = plane2;
- for (row = 0; row < rows; ++row) {
- SDL_memcpy(dst, src, pitch);
- src += pitch;
- dst += UVpitch;
- }
- SDL_UpdateYUVTexture(output[2], NULL, plane0, Ypitch, plane1, UVpitch, plane2, UVpitch);
- SDL_free(plane0);
- SDL_free(plane1);
- SDL_free(plane2);
- } else if (planar && (yuv_format == SDL_PIXELFORMAT_NV12 || yuv_format == SDL_PIXELFORMAT_NV21 || yuv_format == SDL_PIXELFORMAT_P010)) {
- const int Yrows = original->h;
- const int UVrows = ((original->h + 1) / 2);
- const int src_Ypitch = pitch;
- const int src_UVpitch = (yuv_format == SDL_PIXELFORMAT_P010) ? ((pitch + 3) & ~3) : ((pitch + 1) & ~1);
- const Uint8 *src_plane0 = (const Uint8 *)raw_yuv;
- const Uint8 *src_plane1 = src_plane0 + Yrows * src_Ypitch;
- const int Ypitch = pitch + 37;
- const int UVpitch = ((Ypitch + 1) / 2) * 2;
- Uint8 *plane0 = (Uint8 *)SDL_calloc(1, Yrows * Ypitch);
- Uint8 *plane1 = (Uint8 *)SDL_calloc(1, UVrows * UVpitch);
- int row;
- const Uint8 *src;
- Uint8 *dst;
- if (!plane0 || !plane1) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create YUV planes: %s", SDL_GetError());
- goto done;
- }
- src = src_plane0;
- dst = plane0;
- for (row = 0; row < Yrows; ++row) {
- SDL_memcpy(dst, src, src_Ypitch);
- src += src_Ypitch;
- dst += Ypitch;
- }
- src = src_plane1;
- dst = plane1;
- for (row = 0; row < UVrows; ++row) {
- SDL_memcpy(dst, src, src_UVpitch);
- src += src_UVpitch;
- dst += UVpitch;
- }
- SDL_UpdateNVTexture(output[2], NULL, plane0, Ypitch, plane1, UVpitch);
- SDL_free(plane0);
- SDL_free(plane1);
- } else if (lock) {
- void *dst;
- int dst_pitch;
- if (!SDL_LockTexture(output[2], NULL, &dst, &dst_pitch)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't lock YUV texture: %s", SDL_GetError());
- goto done;
- }
- bool updated = update_locked_texture(yuv_format, original->w, original->h, raw_yuv, pitch, dst, dst_pitch);
- SDL_UnlockTexture(output[2]);
- if (!updated) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't update YUV texture: %s", SDL_GetError());
- goto done;
- }
- } else {
- SDL_UpdateTexture(output[2], NULL, raw_yuv, pitch);
- }
- result = true;
- done:
- SDL_DestroySurface(converted);
- SDL_free(raw_yuv);
- return result;
- }
- static bool has_10bit_texture_format(SDL_Renderer *renderer)
- {
- const SDL_PixelFormat *texture_formats = (const SDL_PixelFormat *)SDL_GetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER, NULL);
- if (texture_formats) {
- for (int i = 0; texture_formats[i] != SDL_PIXELFORMAT_UNKNOWN; ++i) {
- if (SDL_ISPIXELFORMAT_10BIT(texture_formats[i])) {
- return true;
- }
- }
- }
- return false;
- }
- static bool check_output(SDL_Renderer *renderer, SDL_Surface *original, SDL_Texture *texture)
- {
- // Clear to yellow to clearly see unfilled pixels
- SDL_SetRenderDrawColor(renderer, 255, 255, 0, SDL_ALPHA_OPAQUE);
- SDL_RenderClear(renderer);
- SDL_Rect rect = { 0, 0, texture->w, texture->h };
- SDL_FRect frect = { 0.0f, 0.0f, (float)texture->w, (float)texture->h };
- SDL_RenderTexture(renderer, texture, &frect, &frect);
- SDL_Surface *output = SDL_RenderReadPixels(renderer, &rect);
- if (!output) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't read pixels: %s", SDL_GetError());
- return false;
- }
- SDL_RenderPresent(renderer);
- // Allow some error for colorspace conversion and differences in color depth
- const int MAX_ALLOWABLE_ERROR = 4096;
- bool result;
- if (SDLTest_CompareSurfaces(output, original, MAX_ALLOWABLE_ERROR) == 0) {
- result = true;
- } else {
- result = false;
- }
- SDL_DestroySurface(output);
- return result;
- }
- static bool run_single_format_test(SDL_Renderer *renderer, SDL_Surface *original, SDL_PixelFormat yuv_format, SDL_PixelFormat rgb_format, bool planar, bool lock)
- {
- SDL_Texture *output[3];
- bool result = true;
- if (!create_textures(renderer, original, yuv_format, rgb_format, planar, lock, false, 100, output)) {
- return false;
- }
- if (!check_output(renderer, original, output[0])) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Original texture didn't match source data, failing");
- result = false;
- }
- if (!check_output(renderer, original, output[1])) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "RGB output didn't match source data, failing");
- result = false;
- }
- if (!check_output(renderer, original, output[2])) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "YUV output didn't match source data, failing");
- result = false;
- }
- for (int i = 0; i < SDL_arraysize(output); ++i) {
- SDL_DestroyTexture(output[i]);
- }
- return result;
- }
- static bool run_all_format_test(SDL_Window *window, const char *requested_renderer, SDL_Surface *original)
- {
- const SDL_PixelFormat yuv_formats[] = {
- SDL_PIXELFORMAT_YV12,
- SDL_PIXELFORMAT_IYUV,
- SDL_PIXELFORMAT_I444,
- SDL_PIXELFORMAT_YUY2,
- SDL_PIXELFORMAT_UYVY,
- SDL_PIXELFORMAT_YVYU,
- SDL_PIXELFORMAT_NV12,
- SDL_PIXELFORMAT_NV21
- };
- const SDL_PixelFormat rgb_formats[] = {
- SDL_PIXELFORMAT_XRGB1555,
- SDL_PIXELFORMAT_RGB565,
- SDL_PIXELFORMAT_RGB24,
- SDL_PIXELFORMAT_ABGR8888,
- SDL_PIXELFORMAT_RGBA8888,
- SDL_PIXELFORMAT_BGRA8888
- };
- const struct
- {
- YUV_CONVERSION_MODE mode;
- const char *name;
- } colorspaces[] = {
- { YUV_CONVERSION_JPEG, "JPEG" },
- { YUV_CONVERSION_BT601, "BT601" },
- { YUV_CONVERSION_BT709, "BT709" },
- //{ YUV_CONVERSION_BT2020, "BT2020" }
- };
- bool quit = false;
- bool result = true;
- for (int i = 0; i < SDL_GetNumRenderDrivers() && !quit; ++i) {
- const char *renderer_name = SDL_GetRenderDriver(i);
- if (requested_renderer && SDL_strcmp(renderer_name, requested_renderer) != 0) {
- continue;
- }
- SDL_Renderer *renderer = SDL_CreateRenderer(window, renderer_name);
- if (!renderer) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create %s renderer: %s", renderer_name, SDL_GetError());
- result = false;
- continue;
- }
- for (int j = 0; j < SDL_arraysize(colorspaces) && !quit; ++j) {
- if (colorspaces[j].mode == YUV_CONVERSION_BT2020 &&
- !has_10bit_texture_format(renderer)) {
- SDL_Log("Skipping %s %s, unsupported", renderer_name, colorspaces[j].name);
- continue;
- }
- SetYUVConversionMode(colorspaces[j].mode);
- for (int m = 0; m < SDL_arraysize(yuv_formats) && !quit; ++m) {
- SDL_PixelFormat yuv_format = yuv_formats[m];
- for (int n = 0; n < SDL_arraysize(rgb_formats) && !quit; ++n) {
- SDL_PixelFormat rgb_format = rgb_formats[n];
- SDL_Log("Testing: %s %s %s %s (planar)", renderer_name, colorspaces[j].name, SDL_GetPixelFormatName(yuv_format), SDL_GetPixelFormatName(rgb_format));
- result &= run_single_format_test(renderer, original, yuv_format, rgb_format, true, false);
- SDL_Log("Testing: %s %s %s %s (packed)", renderer_name, colorspaces[j].name, SDL_GetPixelFormatName(yuv_format), SDL_GetPixelFormatName(rgb_format));
- result &= run_single_format_test(renderer, original, yuv_format, rgb_format, false, false);
- SDL_Log("Testing: %s %s %s %s (locked)", renderer_name, colorspaces[j].name, SDL_GetPixelFormatName(yuv_format), SDL_GetPixelFormatName(rgb_format));
- result &= run_single_format_test(renderer, original, yuv_format, rgb_format, false, true);
- SDL_Event event;
- while (SDL_PollEvent(&event)) {
- if (event.type == SDL_EVENT_QUIT ||
- (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE)) {
- quit = true;
- }
- }
- }
- }
- }
- SDL_DestroyRenderer(renderer);
- }
- return result;
- }
- static bool run_interactive(SDL_Window *window, const char *renderer_name, SDL_Surface *original, SDL_PixelFormat yuv_format, SDL_PixelFormat rgb_format, bool planar, bool lock, bool monochrome, int luminance)
- {
- const char *titles[3] = { "ORIGINAL", "SOFTWARE", "HARDWARE" };
- char title[128];
- const char *yuv_mode_name;
- YUV_CONVERSION_MODE yuv_mode;
- const char *yuv_format_name;
- int current = 0;
- bool quit = false;
- bool result = false;
- SDL_Renderer *renderer = SDL_CreateRenderer(window, renderer_name);
- if (!renderer) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s", SDL_GetError());
- return false;
- }
- renderer_name = SDL_GetRendererName(renderer);
- SDL_Texture *output[3];
- if (!create_textures(renderer, original, yuv_format, rgb_format, planar, lock, monochrome, luminance, output)) {
- goto done;
- }
- yuv_mode = GetYUVConversionModeForResolution(original->w, original->h);
- switch (yuv_mode) {
- case YUV_CONVERSION_JPEG:
- yuv_mode_name = "JPEG";
- break;
- case YUV_CONVERSION_BT601:
- yuv_mode_name = "BT.601";
- break;
- case YUV_CONVERSION_BT709:
- yuv_mode_name = "BT.709";
- break;
- case YUV_CONVERSION_BT2020:
- yuv_mode_name = "BT.2020";
- break;
- default:
- yuv_mode_name = "UNKNOWN";
- break;
- }
- yuv_format_name = SDL_GetPixelFormatName(yuv_format);
- if (SDL_strncmp(yuv_format_name, "SDL_PIXELFORMAT_", 16) == 0) {
- yuv_format_name += 16;
- }
- while (!quit) {
- SDL_Event event;
- while (SDL_PollEvent(&event) > 0) {
- if (event.type == SDL_EVENT_QUIT) {
- quit = true;
- }
- if (event.type == SDL_EVENT_KEY_DOWN) {
- if (event.key.key == SDLK_ESCAPE) {
- quit = true;
- } else if (event.key.key == SDLK_LEFT) {
- --current;
- } else if (event.key.key == SDLK_RIGHT) {
- ++current;
- }
- }
- if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
- if (event.button.x < (original->w / 2)) {
- --current;
- } else {
- ++current;
- }
- }
- }
- /* Handle wrapping */
- if (current < 0) {
- current += SDL_arraysize(output);
- }
- if (current >= SDL_arraysize(output)) {
- current -= SDL_arraysize(output);
- }
- SDL_RenderClear(renderer);
- SDL_RenderTexture(renderer, output[current], NULL, NULL);
- SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
- if (current == 0) {
- SDLTest_DrawString(renderer, 4, 4, titles[current]);
- } else {
- if (SDL_snprintf(title, sizeof(title), "%s %s %s %s", titles[current], yuv_format_name, yuv_mode_name, renderer_name) > 0) {
- SDLTest_DrawString(renderer, 4, 4, title);
- }
- }
- SDL_RenderPresent(renderer);
- SDL_Delay(10);
- }
- result = true;
- done:
- for (int i = 0; i < SDL_arraysize(output); ++i) {
- SDL_DestroyTexture(output[i]);
- }
- SDLTest_CleanupTextDrawing();
- SDL_DestroyRenderer(renderer);
- return result;
- }
- int main(int argc, char **argv)
- {
- static const struct
- {
- bool enable_intrinsics;
- int pattern_size;
- int extra_pitch;
- } automated_test_params[] = {
- /* Test: single pixel */
- { false, 1, 0 },
- /* Test: even width and height */
- { false, 2, 0 },
- { false, 4, 0 },
- /* Test: odd width and height */
- { false, 1, 0 },
- { false, 3, 0 },
- /* Test: even width and height, extra pitch */
- { false, 2, 3 },
- { false, 4, 3 },
- /* Test: odd width and height, extra pitch */
- { false, 1, 3 },
- { false, 3, 3 },
- /* Test: even width and height with intrinsics */
- { true, 32, 0 },
- /* Test: odd width and height with intrinsics */
- { true, 33, 0 },
- { true, 37, 0 },
- /* Test: even width and height with intrinsics, extra pitch */
- { true, 32, 3 },
- /* Test: odd width and height with intrinsics, extra pitch */
- { true, 33, 3 },
- { true, 37, 3 },
- };
- char *filename = NULL;
- SDL_Surface *original = NULL;
- SDL_Surface *png = NULL;
- SDL_Window *window = NULL;
- const char *renderer_name = NULL;
- Uint32 yuv_format = SDL_PIXELFORMAT_YV12;
- Uint32 rgb_format = SDL_PIXELFORMAT_RGBX8888;
- bool planar = false;
- bool lock = false;
- bool monochrome = false;
- int luminance = 100;
- int i;
- bool should_run_automated_tests = false;
- bool should_run_colorspace_test = false;
- bool should_test_all_formats = false;
- SDLTest_CommonState *state;
- int result = 0;
- /* Initialize test framework */
- state = SDLTest_CommonCreateState(argv, 0);
- if (!state) {
- return 1;
- }
- /* Parse command line */
- for (i = 1; i < argc;) {
- int consumed;
- consumed = SDLTest_CommonArg(state, i);
- if (!consumed) {
- if (SDL_strcmp(argv[i], "--all") == 0) {
- should_test_all_formats = true;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--jpeg") == 0) {
- SetYUVConversionMode(YUV_CONVERSION_JPEG);
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--bt601") == 0) {
- SetYUVConversionMode(YUV_CONVERSION_BT601);
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--bt709") == 0) {
- SetYUVConversionMode(YUV_CONVERSION_BT709);
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--auto") == 0) {
- SetYUVConversionMode(YUV_CONVERSION_AUTOMATIC);
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--yv12") == 0) {
- yuv_format = SDL_PIXELFORMAT_YV12;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--iyuv") == 0) {
- yuv_format = SDL_PIXELFORMAT_IYUV;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--i444") == 0) {
- yuv_format = SDL_PIXELFORMAT_I444;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--yuy2") == 0) {
- yuv_format = SDL_PIXELFORMAT_YUY2;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--uyvy") == 0) {
- yuv_format = SDL_PIXELFORMAT_UYVY;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--yvyu") == 0) {
- yuv_format = SDL_PIXELFORMAT_YVYU;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--nv12") == 0) {
- yuv_format = SDL_PIXELFORMAT_NV12;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--nv21") == 0) {
- yuv_format = SDL_PIXELFORMAT_NV21;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--p010") == 0) {
- yuv_format = SDL_PIXELFORMAT_P010;
- rgb_format = SDL_PIXELFORMAT_XBGR2101010;
- SetYUVConversionMode(YUV_CONVERSION_BT2020);
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--i0fl") == 0) {
- yuv_format = SDL_PIXELFORMAT_I0FL;
- rgb_format = SDL_PIXELFORMAT_XBGR2101010;
- SetYUVConversionMode(YUV_CONVERSION_BT2020);
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--i4fl") == 0) {
- yuv_format = SDL_PIXELFORMAT_I4FL;
- rgb_format = SDL_PIXELFORMAT_XBGR2101010;
- SetYUVConversionMode(YUV_CONVERSION_BT2020);
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--rgb555") == 0) {
- rgb_format = SDL_PIXELFORMAT_XRGB1555;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--rgb565") == 0) {
- rgb_format = SDL_PIXELFORMAT_RGB565;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--rgb24") == 0) {
- rgb_format = SDL_PIXELFORMAT_RGB24;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--argb") == 0) {
- rgb_format = SDL_PIXELFORMAT_ARGB8888;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--abgr") == 0) {
- rgb_format = SDL_PIXELFORMAT_ABGR8888;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--rgba") == 0) {
- rgb_format = SDL_PIXELFORMAT_RGBA8888;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--bgra") == 0) {
- rgb_format = SDL_PIXELFORMAT_BGRA8888;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--planar") == 0) {
- planar = true;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--lock") == 0) {
- lock = true;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--monochrome") == 0) {
- monochrome = true;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--luminance") == 0 && argv[i+1]) {
- luminance = SDL_atoi(argv[i+1]);
- consumed = 2;
- } else if (SDL_strcmp(argv[i], "--automated") == 0) {
- should_run_automated_tests = true;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--colorspace-test") == 0) {
- should_run_colorspace_test = true;
- consumed = 1;
- } else if (SDL_strcmp(argv[i], "--renderer") == 0 && argv[i + 1]) {
- renderer_name = argv[i + 1];
- consumed = 2;
- } else if (!filename) {
- filename = argv[i];
- consumed = 1;
- }
- }
- if (consumed <= 0) {
- static const char *options[] = {
- "[--jpeg|--bt601|--bt709|--auto]",
- "[--yv12|--iyuv|--i444|--yuy2|--uyvy|--yvyu|--nv12|--nv21|--p010|--i4fl]",
- "[--rgb555|--rgb565|--rgb24|--argb|--abgr|--rgba|--bgra]",
- "[--monochrome] [--luminance N%] [--planar] [--lock]",
- "[--automated] [--colorspace-test] [--renderer NAME]",
- "[sample.png]",
- NULL,
- };
- SDLTest_CommonLogUsage(state, argv[0], options);
- SDL_Quit();
- SDLTest_CommonDestroyState(state);
- return 1;
- }
- i += consumed;
- }
- /* Run automated tests */
- if (should_run_automated_tests) {
- for (i = 0; i < (int)SDL_arraysize(automated_test_params); ++i) {
- SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Running automated test, pattern size %d, extra pitch %d, intrinsics %s",
- automated_test_params[i].pattern_size,
- automated_test_params[i].extra_pitch,
- automated_test_params[i].enable_intrinsics ? "enabled" : "disabled");
- if (!run_automated_tests(automated_test_params[i].pattern_size, automated_test_params[i].extra_pitch)) {
- result = 2;
- }
- }
- goto done;
- }
- if (should_run_colorspace_test) {
- if (!run_colorspace_test()) {
- result = 2;
- }
- goto done;
- }
- filename = GetResourceFilename(filename, "testyuv.png");
- png = SDL_LoadSurface(filename);
- if (png) {
- original = SDL_ConvertSurface(png, SDL_PIXELFORMAT_RGB24);
- SDL_DestroySurface(png);
- }
- if (!original) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", filename, SDL_GetError());
- result = 3;
- goto done;
- }
- window = SDL_CreateWindow("YUV test", original->w, original->h, 0);
- if (!window) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s", SDL_GetError());
- result = 4;
- goto done;
- }
- if (should_test_all_formats) {
- if (!run_all_format_test(window, renderer_name, original)) {
- result = 5;
- }
- } else {
- if (!run_interactive(window, renderer_name, original, yuv_format, rgb_format, planar, lock, monochrome, luminance)) {
- result = 5;
- }
- }
- done:
- SDL_free(filename);
- SDL_DestroySurface(original);
- if (window) {
- SDL_DestroyWindow(window);
- }
- SDL_Quit();
- SDLTest_CommonDestroyState(state);
- return result;
- }
|