SDL

Форк
0
/
testoverlay.c 
545 строк · 17.7 Кб
1
/*
2
  Copyright (C) 1997-2024 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
/********************************************************************************
13
 *                                                                              *
14
 * Test of the overlay used for moved pictures, test more closed to real life.  *
15
 * Running trojan moose :) Coded by Mike Gorchak.                               *
16
 *                                                                              *
17
 ********************************************************************************/
18

19
#include <SDL3/SDL_test.h>
20
#include <SDL3/SDL_test_common.h>
21
#include <SDL3/SDL_main.h>
22
#include "testutils.h"
23

24
#ifdef SDL_PLATFORM_EMSCRIPTEN
25
#include <emscripten/emscripten.h>
26
#endif
27

28
#include <stdlib.h>
29

30
#define MOOSEPIC_W 64
31
#define MOOSEPIC_H 88
32

33
#define MOOSEFRAME_SIZE   (MOOSEPIC_W * MOOSEPIC_H)
34
#define MOOSEFRAMES_COUNT 10
35

36
/* *INDENT-OFF* */ /* clang-format off */
37
static SDL_Color MooseColors[84] = {
38
    {49, 49, 49, SDL_ALPHA_OPAQUE}
39
    , {66, 24, 0, SDL_ALPHA_OPAQUE}
40
    , {66, 33, 0, SDL_ALPHA_OPAQUE}
41
    , {66, 66, 66, SDL_ALPHA_OPAQUE}
42
    ,
43
    {66, 115, 49, SDL_ALPHA_OPAQUE}
44
    , {74, 33, 0, SDL_ALPHA_OPAQUE}
45
    , {74, 41, 16, SDL_ALPHA_OPAQUE}
46
    , {82, 33, 8, SDL_ALPHA_OPAQUE}
47
    ,
48
    {82, 41, 8, SDL_ALPHA_OPAQUE}
49
    , {82, 49, 16, SDL_ALPHA_OPAQUE}
50
    , {82, 82, 82, SDL_ALPHA_OPAQUE}
51
    , {90, 41, 8, SDL_ALPHA_OPAQUE}
52
    ,
53
    {90, 41, 16, SDL_ALPHA_OPAQUE}
54
    , {90, 57, 24, SDL_ALPHA_OPAQUE}
55
    , {99, 49, 16, SDL_ALPHA_OPAQUE}
56
    , {99, 66, 24, SDL_ALPHA_OPAQUE}
57
    ,
58
    {99, 66, 33, SDL_ALPHA_OPAQUE}
59
    , {99, 74, 33, SDL_ALPHA_OPAQUE}
60
    , {107, 57, 24, SDL_ALPHA_OPAQUE}
61
    , {107, 82, 41, SDL_ALPHA_OPAQUE}
62
    ,
63
    {115, 57, 33, SDL_ALPHA_OPAQUE}
64
    , {115, 66, 33, SDL_ALPHA_OPAQUE}
65
    , {115, 66, 41, SDL_ALPHA_OPAQUE}
66
    , {115, 74, 0, SDL_ALPHA_OPAQUE}
67
    ,
68
    {115, 90, 49, SDL_ALPHA_OPAQUE}
69
    , {115, 115, 115, SDL_ALPHA_OPAQUE}
70
    , {123, 82, 0, SDL_ALPHA_OPAQUE}
71
    , {123, 99, 57, SDL_ALPHA_OPAQUE}
72
    ,
73
    {132, 66, 41, SDL_ALPHA_OPAQUE}
74
    , {132, 74, 41, SDL_ALPHA_OPAQUE}
75
    , {132, 90, 8, SDL_ALPHA_OPAQUE}
76
    , {132, 99, 33, SDL_ALPHA_OPAQUE}
77
    ,
78
    {132, 99, 66, SDL_ALPHA_OPAQUE}
79
    , {132, 107, 66, SDL_ALPHA_OPAQUE}
80
    , {140, 74, 49, SDL_ALPHA_OPAQUE}
81
    , {140, 99, 16, SDL_ALPHA_OPAQUE}
82
    ,
83
    {140, 107, 74, SDL_ALPHA_OPAQUE}
84
    , {140, 115, 74, SDL_ALPHA_OPAQUE}
85
    , {148, 107, 24, SDL_ALPHA_OPAQUE}
86
    , {148, 115, 82, SDL_ALPHA_OPAQUE}
87
    ,
88
    {148, 123, 74, SDL_ALPHA_OPAQUE}
89
    , {148, 123, 90, SDL_ALPHA_OPAQUE}
90
    , {156, 115, 33, SDL_ALPHA_OPAQUE}
91
    , {156, 115, 90, SDL_ALPHA_OPAQUE}
92
    ,
93
    {156, 123, 82, SDL_ALPHA_OPAQUE}
94
    , {156, 132, 82, SDL_ALPHA_OPAQUE}
95
    , {156, 132, 99, SDL_ALPHA_OPAQUE}
96
    , {156, 156, 156, SDL_ALPHA_OPAQUE}
97
    ,
98
    {165, 123, 49, SDL_ALPHA_OPAQUE}
99
    , {165, 123, 90, SDL_ALPHA_OPAQUE}
100
    , {165, 132, 82, SDL_ALPHA_OPAQUE}
101
    , {165, 132, 90, SDL_ALPHA_OPAQUE}
102
    ,
103
    {165, 132, 99, SDL_ALPHA_OPAQUE}
104
    , {165, 140, 90, SDL_ALPHA_OPAQUE}
105
    , {173, 132, 57, SDL_ALPHA_OPAQUE}
106
    , {173, 132, 99, SDL_ALPHA_OPAQUE}
107
    ,
108
    {173, 140, 107, SDL_ALPHA_OPAQUE}
109
    , {173, 140, 115, SDL_ALPHA_OPAQUE}
110
    , {173, 148, 99, SDL_ALPHA_OPAQUE}
111
    , {173, 173, 173, SDL_ALPHA_OPAQUE}
112
    ,
113
    {181, 140, 74, SDL_ALPHA_OPAQUE}
114
    , {181, 148, 115, SDL_ALPHA_OPAQUE}
115
    , {181, 148, 123, SDL_ALPHA_OPAQUE}
116
    , {181, 156, 107, SDL_ALPHA_OPAQUE}
117
    ,
118
    {189, 148, 123, SDL_ALPHA_OPAQUE}
119
    , {189, 156, 82, SDL_ALPHA_OPAQUE}
120
    , {189, 156, 123, SDL_ALPHA_OPAQUE}
121
    , {189, 156, 132, SDL_ALPHA_OPAQUE}
122
    ,
123
    {189, 189, 189, SDL_ALPHA_OPAQUE}
124
    , {198, 156, 123, SDL_ALPHA_OPAQUE}
125
    , {198, 165, 132, SDL_ALPHA_OPAQUE}
126
    , {206, 165, 99, SDL_ALPHA_OPAQUE}
127
    ,
128
    {206, 165, 132, SDL_ALPHA_OPAQUE}
129
    , {206, 173, 140, SDL_ALPHA_OPAQUE}
130
    , {206, 206, 206, SDL_ALPHA_OPAQUE}
131
    , {214, 173, 115, SDL_ALPHA_OPAQUE}
132
    ,
133
    {214, 173, 140, SDL_ALPHA_OPAQUE}
134
    , {222, 181, 148, SDL_ALPHA_OPAQUE}
135
    , {222, 189, 132, SDL_ALPHA_OPAQUE}
136
    , {222, 189, 156, SDL_ALPHA_OPAQUE}
137
    ,
138
    {222, 222, 222, SDL_ALPHA_OPAQUE}
139
    , {231, 198, 165, SDL_ALPHA_OPAQUE}
140
    , {231, 231, 231, SDL_ALPHA_OPAQUE}
141
    , {239, 206, 173, SDL_ALPHA_OPAQUE}
142
};
143
/* *INDENT-ON* */ /* clang-format on */
144

145
static SDLTest_CommonState *state;
146
static Uint64 next_fps_check;
147
static Uint32 frames;
148
static const Uint32 fps_check_delay = 5000;
149

150
static Uint32 yuv_format = SDL_PIXELFORMAT_YV12;
151
static SDL_Surface *MooseYUVSurfaces[MOOSEFRAMES_COUNT];
152
static SDL_Texture *MooseTexture = NULL;
153
static SDL_FRect displayrect;
154
static int window_w;
155
static int window_h;
156
static int paused = 0;
157
static int done = 0;
158
static int fpsdelay;
159
static SDL_bool streaming = SDL_TRUE;
160
static Uint8 *RawMooseData = NULL;
161

162
/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
163
static void
164
quit(int rc)
165
{
166
    int i;
167

168
    /* If rc is 0, just let main return normally rather than calling exit.
169
     * This allows testing of platforms where SDL_main is required and does meaningful cleanup.
170
     */
171

172
    SDL_free(RawMooseData);
173

174
    for (i = 0; i < MOOSEFRAMES_COUNT; i++) {
175
         SDL_DestroySurface(MooseYUVSurfaces[i]);
176
    }
177

178
    SDLTest_CommonQuit(state);
179

180
    /* Let 'main()' return normally */
181
    if (rc != 0) {
182
         exit(rc);
183
    }
184
}
185

186
static void MoveSprites(SDL_Renderer *renderer)
187
{
188
    static int i = 0;
189

190
    if (streaming) {
191
         if (!paused) {
192
             i = (i + 1) % MOOSEFRAMES_COUNT;
193
             /* Test both upload paths for NV12/NV21 formats */
194
             if ((yuv_format == SDL_PIXELFORMAT_NV12 || yuv_format == SDL_PIXELFORMAT_NV21) &&
195
                 (i % 2) == 0) {
196
#ifdef TEST_RECT_UPDATE
197
                 SDL_Rect rect;
198

199
                 if (i == 0) {
200
                     rect.x = 0;
201
                     rect.y = 0;
202
                     rect.w = MOOSEPIC_W;
203
                     rect.h = MOOSEPIC_H;
204
                 } else {
205
                     rect.x = MOOSEPIC_W / 4;
206
                     rect.y = MOOSEPIC_H / 4;
207
                     rect.w = MOOSEPIC_W / 2;
208
                     rect.h = MOOSEPIC_H / 2;
209
                 }
210
                 SDL_UpdateNVTexture(MooseTexture, &rect,
211
                                     (Uint8 *)MooseYUVSurfaces[i]->pixels + rect.y * MooseYUVSurfaces[i]->pitch + rect.x, MooseYUVSurfaces[i]->pitch,
212
                                     (Uint8 *)MooseYUVSurfaces[i]->pixels + MOOSEFRAME_SIZE + (rect.y + 1) / 2 * MooseYUVSurfaces[i]->pitch + (rect.x + 1) / 2, MooseYUVSurfaces[i]->pitch);
213
#else
214
                 SDL_UpdateNVTexture(MooseTexture, NULL,
215
                                     MooseYUVSurfaces[i]->pixels, MooseYUVSurfaces[i]->pitch,
216
                                     (Uint8 *)MooseYUVSurfaces[i]->pixels + MOOSEFRAME_SIZE, MooseYUVSurfaces[i]->pitch);
217
#endif
218
             } else {
219
                 SDL_UpdateTexture(MooseTexture, NULL, MooseYUVSurfaces[i]->pixels, MooseYUVSurfaces[i]->pitch);
220
             }
221
         }
222
         SDL_RenderClear(renderer);
223
         SDL_RenderTexture(renderer, MooseTexture, NULL, &displayrect);
224
         SDL_RenderPresent(renderer);
225
    } else {
226
         SDL_Texture *tmp;
227

228
         /* Test SDL_CreateTextureFromSurface */
229
         if (!paused) {
230
             i = (i + 1) % MOOSEFRAMES_COUNT;
231
         }
232

233
         tmp = SDL_CreateTextureFromSurface(renderer, MooseYUVSurfaces[i]);
234
         if (!tmp) {
235
             SDL_Log("Error %s", SDL_GetError());
236
             quit(7);
237
         }
238

239
         SDL_RenderClear(renderer);
240
         SDL_RenderTexture(renderer, tmp, NULL, &displayrect);
241
         SDL_RenderPresent(renderer);
242
         SDL_DestroyTexture(tmp);
243
    }
244
}
245

246

247
static void loop(void)
248
{
249
    Uint64 now;
250
    int i;
251
    SDL_Event event;
252

253
    SDL_Renderer *renderer = state->renderers[0]; /* only 1 window */
254

255
    /* Check for events */
256
    while (SDL_PollEvent(&event)) {
257
        SDLTest_CommonEvent(state, &event, &done);
258

259
        switch (event.type) {
260
        case SDL_EVENT_WINDOW_RESIZED:
261
            SDL_SetRenderViewport(renderer, NULL);
262
            window_w = event.window.data1;
263
            window_h = event.window.data2;
264
            displayrect.w = (float)window_w;
265
            displayrect.h = (float)window_h;
266
            break;
267
        case SDL_EVENT_MOUSE_BUTTON_DOWN:
268
            displayrect.x = event.button.x - window_w / 2;
269
            displayrect.y = event.button.y - window_h / 2;
270
            break;
271
        case SDL_EVENT_MOUSE_MOTION:
272
            if (event.motion.state) {
273
                displayrect.x = event.motion.x - window_w / 2;
274
                displayrect.y = event.motion.y - window_h / 2;
275
            }
276
            break;
277
        case SDL_EVENT_KEY_DOWN:
278
            if (event.key.key == SDLK_SPACE) {
279
                paused = !paused;
280
                break;
281
            }
282
            if (event.key.key != SDLK_ESCAPE) {
283
                break;
284
            }
285
            break;
286
        default:
287
            break;
288
        }
289
    }
290

291
#ifndef SDL_PLATFORM_EMSCRIPTEN
292
    SDL_Delay(fpsdelay);
293
#endif
294

295
    for (i = 0; i < state->num_windows; ++i) {
296
        if (state->windows[i] == NULL) {
297
            continue;
298
        }
299
        MoveSprites(state->renderers[i]);
300
    }
301
#ifdef SDL_PLATFORM_EMSCRIPTEN
302
    if (done) {
303
        emscripten_cancel_main_loop();
304
    }
305
#endif
306

307
    frames++;
308
    now = SDL_GetTicks();
309
    if (now >= next_fps_check) {
310
        /* Print out some timing information */
311
        const Uint64 then = next_fps_check - fps_check_delay;
312
        const double fps = ((double)frames * 1000) / (now - then);
313
        SDL_Log("%2.2f frames per second\n", fps);
314
        next_fps_check = now + fps_check_delay;
315
        frames = 0;
316
    }
317
}
318

319

320
int main(int argc, char **argv)
321
{
322
    SDL_IOStream *handle;
323
    int i;
324
    int j;
325
    int fps = 12;
326
    int nodelay = 0;
327
    int scale = 5;
328
    char *filename = NULL;
329

330
    /* Initialize test framework */
331
    state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
332
    if (!state) {
333
        return 1;
334
    }
335

336
    SDL_zeroa(MooseYUVSurfaces);
337

338
    for (i = 1; i < argc;) {
339
        int consumed;
340

341
        consumed = SDLTest_CommonArg(state, i);
342
        if (consumed == 0) {
343
            consumed = -1;
344
            if (SDL_strcmp(argv[i], "--fps") == 0) {
345
                if (argv[i + 1]) {
346
                    consumed = 2;
347
                    fps = SDL_atoi(argv[i + 1]);
348
                    if (fps == 0) {
349
                        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "The --fps option requires an argument [from 1 to 1000], default is 12.\n");
350
                        quit(10);
351
                    }
352
                    if ((fps < 0) || (fps > 1000)) {
353
                        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "The --fps option must be in range from 1 to 1000, default is 12.\n");
354
                        quit(10);
355
                    }
356
                } else {
357
                    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "The --fps option requires an argument [from 1 to 1000], default is 12.\n");
358
                    quit(10);
359
                }
360
            } else if (SDL_strcmp(argv[i], "--nodelay") == 0) {
361
                consumed = 1;
362
                nodelay = 1;
363
            } else if (SDL_strcmp(argv[i], "--nostreaming") == 0) {
364
                consumed = 1;
365
                streaming = SDL_FALSE;
366
            } else if (SDL_strcmp(argv[i], "--scale") == 0) {
367
                consumed = 2;
368
                if (argv[i + 1]) {
369
                    scale = SDL_atoi(argv[i + 1]);
370
                    if (scale == 0) {
371
                        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "The --scale option requires an argument [from 1 to 50], default is 5.\n");
372
                        quit(10);
373
                    }
374
                    if ((scale < 0) || (scale > 50)) {
375
                        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "The --scale option must be in range from 1 to 50, default is 5.\n");
376
                        quit(10);
377
                    }
378
                } else {
379
                    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "The --fps option requires an argument [from 1 to 1000], default is 12.\n");
380
                    quit(10);
381
                }
382
            } else if (SDL_strcmp(argv[i], "--yuvformat") == 0) {
383
                consumed = 2;
384
                if (argv[i + 1]) {
385
                    char *fmt = argv[i + 1];
386

387
                    if (SDL_strcmp(fmt, "YV12") == 0) {
388
                        yuv_format = SDL_PIXELFORMAT_YV12;
389
                    } else if (SDL_strcmp(fmt, "IYUV") == 0) {
390
                        yuv_format = SDL_PIXELFORMAT_IYUV;
391
                    } else if (SDL_strcmp(fmt, "YUY2") == 0) {
392
                        yuv_format = SDL_PIXELFORMAT_YUY2;
393
                    } else if (SDL_strcmp(fmt, "UYVY") == 0) {
394
                        yuv_format = SDL_PIXELFORMAT_UYVY;
395
                    } else if (SDL_strcmp(fmt, "YVYU") == 0) {
396
                        yuv_format = SDL_PIXELFORMAT_YVYU;
397
                    } else if (SDL_strcmp(fmt, "NV12") == 0) {
398
                        yuv_format = SDL_PIXELFORMAT_NV12;
399
                    } else if (SDL_strcmp(fmt, "NV21") == 0) {
400
                        yuv_format = SDL_PIXELFORMAT_NV21;
401
                    } else {
402
                        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "The --yuvformat option requires one of the: YV12 (default), IYUV, YUY2, UYVY, YVYU, NV12, NV21)\n");
403
                        quit(10);
404
                    }
405
                } else {
406
                    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "The --yuvformat option requires one of the: YV12 (default), IYUV, YUY2, UYVY, YVYU, NV12, NV21)\n");
407
                    quit(10);
408
                }
409
            }
410

411
        }
412
        if (consumed < 0) {
413
            static const char *options[] = {
414
                "[--fps <frames per second>]",
415
                "[--nodelay]",
416
                "[--yuvformat <fmt>] (one of the: YV12 (default), IYUV, YUY2, UYVY, YVYU, NV12, NV21)",
417
                "[--scale <scale factor>] (initial scale of the overlay)",
418
                "[--nostreaming] path that use SDL_CreateTextureFromSurface() not STREAMING texture",
419
                NULL
420
            };
421
            SDLTest_CommonLogUsage(state, argv[0], options);
422
            quit(1);
423
        }
424
        i += consumed;
425
    }
426

427
    /* Force window size */
428
    state->window_w = MOOSEPIC_W * scale;
429
    state->window_h = MOOSEPIC_H * scale;
430

431
    if (!SDLTest_CommonInit(state)) {
432
        quit(2);
433
    }
434

435
    RawMooseData = (Uint8 *)SDL_malloc(MOOSEFRAME_SIZE * MOOSEFRAMES_COUNT);
436
    if (!RawMooseData) {
437
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Can't allocate memory for movie !\n");
438
        quit(1);
439
    }
440

441
    /* load the trojan moose images */
442
    filename = GetResourceFilename(NULL, "moose.dat");
443
    if (!filename) {
444
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory\n");
445
        quit(2);
446
    }
447
    handle = SDL_IOFromFile(filename, "rb");
448
    SDL_free(filename);
449
    if (!handle) {
450
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Can't find the file moose.dat !\n");
451
        quit(2);
452
    }
453

454
    SDL_ReadIO(handle, RawMooseData, MOOSEFRAME_SIZE * MOOSEFRAMES_COUNT);
455

456
    SDL_CloseIO(handle);
457

458
    /* Create the window and renderer */
459
    window_w = MOOSEPIC_W * scale;
460
    window_h = MOOSEPIC_H * scale;
461

462
    if (state->num_windows != 1) {
463
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Only one window allowed\n");
464
        quit(1);
465
    }
466

467
    for (i = 0; i < state->num_windows; ++i) {
468
        SDL_Renderer *renderer = state->renderers[i];
469

470
        if (streaming) {
471
            MooseTexture = SDL_CreateTexture(renderer, yuv_format, SDL_TEXTUREACCESS_STREAMING, MOOSEPIC_W, MOOSEPIC_H);
472
            if (!MooseTexture) {
473
                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create texture: %s\n", SDL_GetError());
474
                quit(5);
475
            }
476
        }
477
    }
478

479
    /* Uncomment this to check vertex color with a YUV texture */
480
    /* SDL_SetTextureColorMod(MooseTexture, 0xff, 0x80, 0x80); */
481

482
    for (i = 0; i < MOOSEFRAMES_COUNT; i++) {
483
        /* Create RGB SDL_Surface */
484
        SDL_Surface *mooseRGBSurface = SDL_CreateSurface(MOOSEPIC_W, MOOSEPIC_H, SDL_PIXELFORMAT_RGB24);
485
        if (!mooseRGBSurface) {
486
            quit(6);
487
        }
488

489
        /* Load Moose into a RGB SDL_Surface */
490
        {
491
            Uint8 *rgb = mooseRGBSurface->pixels;
492
            Uint8 *frame = RawMooseData + i * MOOSEFRAME_SIZE;
493
            for (j = 0; j < MOOSEFRAME_SIZE; ++j) {
494
                rgb[0] = MooseColors[frame[j]].r;
495
                rgb[1] = MooseColors[frame[j]].g;
496
                rgb[2] = MooseColors[frame[j]].b;
497
                rgb += 3;
498
            }
499
        }
500

501
        /* Convert to YUV SDL_Surface */
502
        MooseYUVSurfaces[i] = SDL_ConvertSurface(mooseRGBSurface, yuv_format);
503
        if (MooseYUVSurfaces[i] == NULL) {
504
            quit(7);
505
        }
506

507
        SDL_DestroySurface(mooseRGBSurface);
508
    }
509

510
    SDL_free(RawMooseData);
511
    RawMooseData = NULL;
512

513
    /* set the start frame */
514
    i = 0;
515
    if (nodelay) {
516
        fpsdelay = 0;
517
    } else {
518
        fpsdelay = 1000 / fps;
519
    }
520

521
    displayrect.x = 0;
522
    displayrect.y = 0;
523
    displayrect.w = (float)window_w;
524
    displayrect.h = (float)window_h;
525

526
    /* Ignore key up events, they don't even get filtered */
527
    SDL_SetEventEnabled(SDL_EVENT_KEY_UP, SDL_FALSE);
528

529
    /* Main render loop */
530
    frames = 0;
531
    next_fps_check = SDL_GetTicks() + fps_check_delay;
532
    done = 0;
533

534
    /* Loop, waiting for QUIT or RESIZE */
535
#ifdef SDL_PLATFORM_EMSCRIPTEN
536
    emscripten_set_main_loop(loop, nodelay ? 0 : fps, 1);
537
#else
538
    while (!done) {
539
        loop();
540
    }
541
#endif
542

543
    quit(0);
544
    return 0;
545
}
546

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.