Pillow

Форк
0
/
Jpeg2KEncode.c 
661 строка · 18.9 Кб
1
/*
2
 * The Python Imaging Library.
3
 * $Id$
4
 *
5
 * decoder for JPEG2000 image data.
6
 *
7
 * history:
8
 * 2014-03-12 ajh  Created
9
 *
10
 * Copyright (c) 2014 Coriolis Systems Limited
11
 * Copyright (c) 2014 Alastair Houghton
12
 *
13
 * See the README file for details on usage and redistribution.
14
 */
15

16
#include "Imaging.h"
17

18
#ifdef HAVE_OPENJPEG
19

20
#include "Jpeg2K.h"
21

22
#define CINEMA_24_CS_LENGTH 1302083
23
#define CINEMA_48_CS_LENGTH 651041
24
#define COMP_24_CS_MAX_LENGTH 1041666
25
#define COMP_48_CS_MAX_LENGTH 520833
26

27
/* -------------------------------------------------------------------- */
28
/* Error handler                                                        */
29
/* -------------------------------------------------------------------- */
30

31
static void
32
j2k_error(const char *msg, void *client_data) {
33
    JPEG2KENCODESTATE *state = (JPEG2KENCODESTATE *)client_data;
34
    free((void *)state->error_msg);
35
    state->error_msg = strdup(msg);
36
}
37

38
static void
39
j2k_warn(const char *msg, void *client_data) {
40
    // Null handler
41
}
42

43
/* -------------------------------------------------------------------- */
44
/* Buffer output stream                                                 */
45
/* -------------------------------------------------------------------- */
46

47
static OPJ_SIZE_T
48
j2k_write(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) {
49
    ImagingCodecState state = (ImagingCodecState)p_user_data;
50
    unsigned int result;
51

52
    result = _imaging_write_pyFd(state->fd, p_buffer, p_nb_bytes);
53

54
    return result ? result : (OPJ_SIZE_T)-1;
55
}
56

57
static OPJ_OFF_T
58
j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data) {
59
    ImagingCodecState state = (ImagingCodecState)p_user_data;
60
    char *buffer;
61
    int result;
62

63
    /* Explicitly write zeros */
64
    buffer = calloc(p_nb_bytes, 1);
65
    if (!buffer) {
66
        return (OPJ_OFF_T)-1;
67
    }
68

69
    result = _imaging_write_pyFd(state->fd, buffer, p_nb_bytes);
70

71
    free(buffer);
72

73
    return result ? result : p_nb_bytes;
74
}
75

76
static OPJ_BOOL
77
j2k_seek(OPJ_OFF_T p_nb_bytes, void *p_user_data) {
78
    ImagingCodecState state = (ImagingCodecState)p_user_data;
79
    off_t pos = 0;
80

81
    _imaging_seek_pyFd(state->fd, p_nb_bytes, SEEK_SET);
82
    pos = _imaging_tell_pyFd(state->fd);
83

84
    return pos == p_nb_bytes;
85
}
86

87
/* -------------------------------------------------------------------- */
88
/* Encoder                                                              */
89
/* -------------------------------------------------------------------- */
90

91
typedef void (*j2k_pack_tile_t)(
92
    Imaging im, UINT8 *buf, unsigned x0, unsigned y0, unsigned w, unsigned h
93
);
94

95
static void
96
j2k_pack_l(Imaging im, UINT8 *buf, unsigned x0, unsigned y0, unsigned w, unsigned h) {
97
    UINT8 *ptr = buf;
98
    unsigned x, y;
99
    for (y = 0; y < h; ++y) {
100
        UINT8 *data = (UINT8 *)(im->image[y + y0] + x0);
101
        for (x = 0; x < w; ++x) {
102
            *ptr++ = *data++;
103
        }
104
    }
105
}
106

107
static void
108
j2k_pack_i16(Imaging im, UINT8 *buf, unsigned x0, unsigned y0, unsigned w, unsigned h) {
109
    UINT8 *ptr = buf;
110
    unsigned x, y;
111
    for (y = 0; y < h; ++y) {
112
        UINT8 *data = (UINT8 *)(im->image[y + y0] + x0);
113
        for (x = 0; x < w; ++x) {
114
#ifdef WORDS_BIGENDIAN
115
            ptr[0] = data[1];
116
            ptr[1] = data[0];
117
#else
118
            ptr[0] = data[0];
119
            ptr[1] = data[1];
120
#endif
121
            ptr += 2;
122
            data += 2;
123
        }
124
    }
125
}
126

127
static void
128
j2k_pack_la(Imaging im, UINT8 *buf, unsigned x0, unsigned y0, unsigned w, unsigned h) {
129
    UINT8 *ptr = buf;
130
    UINT8 *ptra = buf + w * h;
131
    unsigned x, y;
132
    for (y = 0; y < h; ++y) {
133
        UINT8 *data = (UINT8 *)(im->image[y + y0] + 4 * x0);
134
        for (x = 0; x < w; ++x) {
135
            *ptr++ = data[0];
136
            *ptra++ = data[3];
137
            data += 4;
138
        }
139
    }
140
}
141

142
static void
143
j2k_pack_rgb(Imaging im, UINT8 *buf, unsigned x0, unsigned y0, unsigned w, unsigned h) {
144
    UINT8 *pr = buf;
145
    UINT8 *pg = pr + w * h;
146
    UINT8 *pb = pg + w * h;
147
    unsigned x, y;
148
    for (y = 0; y < h; ++y) {
149
        UINT8 *data = (UINT8 *)(im->image[y + y0] + 4 * x0);
150
        for (x = 0; x < w; ++x) {
151
            *pr++ = data[0];
152
            *pg++ = data[1];
153
            *pb++ = data[2];
154
            data += 4;
155
        }
156
    }
157
}
158

159
static void
160
j2k_pack_rgba(
161
    Imaging im, UINT8 *buf, unsigned x0, unsigned y0, unsigned w, unsigned h
162
) {
163
    UINT8 *pr = buf;
164
    UINT8 *pg = pr + w * h;
165
    UINT8 *pb = pg + w * h;
166
    UINT8 *pa = pb + w * h;
167
    unsigned x, y;
168
    for (y = 0; y < h; ++y) {
169
        UINT8 *data = (UINT8 *)(im->image[y + y0] + 4 * x0);
170
        for (x = 0; x < w; ++x) {
171
            *pr++ = *data++;
172
            *pg++ = *data++;
173
            *pb++ = *data++;
174
            *pa++ = *data++;
175
        }
176
    }
177
}
178

179
enum {
180
    J2K_STATE_START = 0,
181
    J2K_STATE_ENCODING = 1,
182
    J2K_STATE_DONE = 2,
183
    J2K_STATE_FAILED = 3,
184
};
185

186
static void
187
j2k_set_cinema_params(Imaging im, int components, opj_cparameters_t *params) {
188
    float rate;
189
    int n;
190

191
    /* These settings have been copied from opj_compress in the OpenJPEG
192
       sources. */
193

194
    params->tile_size_on = OPJ_FALSE;
195
    params->cp_tdx = params->cp_tdy = 1;
196
    params->tp_flag = 'C';
197
    params->tp_on = 1;
198
    params->cp_tx0 = params->cp_ty0 = 0;
199
    params->image_offset_x0 = params->image_offset_y0 = 0;
200
    params->cblockw_init = 32;
201
    params->cblockh_init = 32;
202
    params->csty |= 0x01;
203
    params->prog_order = OPJ_CPRL;
204
    params->roi_compno = -1;
205
    params->subsampling_dx = params->subsampling_dy = 1;
206
    params->irreversible = 1;
207

208
    if (params->cp_cinema == OPJ_CINEMA4K_24) {
209
        float max_rate =
210
            ((float)(components * im->xsize * im->ysize * 8) / (CINEMA_24_CS_LENGTH * 8)
211
            );
212

213
        params->POC[0].tile = 1;
214
        params->POC[0].resno0 = 0;
215
        params->POC[0].compno0 = 0;
216
        params->POC[0].layno1 = 1;
217
        params->POC[0].resno1 = params->numresolution - 1;
218
        params->POC[0].compno1 = 3;
219
        params->POC[0].prg1 = OPJ_CPRL;
220
        params->POC[1].tile = 1;
221
        params->POC[1].resno0 = 0;
222
        params->POC[1].compno0 = 0;
223
        params->POC[1].layno1 = 1;
224
        params->POC[1].resno1 = params->numresolution - 1;
225
        params->POC[1].compno1 = 3;
226
        params->POC[1].prg1 = OPJ_CPRL;
227
        params->numpocs = 2;
228

229
        for (n = 0; n < params->tcp_numlayers; ++n) {
230
            rate = 0;
231
            if (params->tcp_rates[0] == 0) {
232
                params->tcp_rates[n] = max_rate;
233
            } else {
234
                rate =
235
                    ((float)(components * im->xsize * im->ysize * 8) /
236
                     (params->tcp_rates[n] * 8));
237
                if (rate > CINEMA_24_CS_LENGTH) {
238
                    params->tcp_rates[n] = max_rate;
239
                }
240
            }
241
        }
242

243
        params->max_comp_size = COMP_24_CS_MAX_LENGTH;
244
    } else {
245
        float max_rate =
246
            ((float)(components * im->xsize * im->ysize * 8) / (CINEMA_48_CS_LENGTH * 8)
247
            );
248

249
        for (n = 0; n < params->tcp_numlayers; ++n) {
250
            rate = 0;
251
            if (params->tcp_rates[0] == 0) {
252
                params->tcp_rates[n] = max_rate;
253
            } else {
254
                rate =
255
                    ((float)(components * im->xsize * im->ysize * 8) /
256
                     (params->tcp_rates[n] * 8));
257
                if (rate > CINEMA_48_CS_LENGTH) {
258
                    params->tcp_rates[n] = max_rate;
259
                }
260
            }
261
        }
262

263
        params->max_comp_size = COMP_48_CS_MAX_LENGTH;
264
    }
265
}
266

267
static int
268
j2k_encode_entry(Imaging im, ImagingCodecState state) {
269
    JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context;
270
    opj_stream_t *stream = NULL;
271
    opj_image_t *image = NULL;
272
    opj_codec_t *codec = NULL;
273
    opj_cparameters_t params;
274
    unsigned components;
275
    OPJ_COLOR_SPACE color_space;
276
    opj_image_cmptparm_t image_params[4];
277
    unsigned xsiz, ysiz;
278
    unsigned tile_width, tile_height;
279
    unsigned tiles_x, tiles_y;
280
    unsigned x, y, tile_ndx;
281
    unsigned n;
282
    j2k_pack_tile_t pack;
283
    int ret = -1;
284

285
    unsigned prec = 8;
286
    unsigned _overflow_scale_factor;
287

288
    stream = opj_stream_create(BUFFER_SIZE, OPJ_FALSE);
289

290
    if (!stream) {
291
        state->errcode = IMAGING_CODEC_BROKEN;
292
        state->state = J2K_STATE_FAILED;
293
        goto quick_exit;
294
    }
295

296
    opj_stream_set_write_function(stream, j2k_write);
297
    opj_stream_set_skip_function(stream, j2k_skip);
298
    opj_stream_set_seek_function(stream, j2k_seek);
299

300
    /* OpenJPEG 2.0 doesn't have OPJ_VERSION_MAJOR */
301
#ifndef OPJ_VERSION_MAJOR
302
    opj_stream_set_user_data(stream, state);
303
#else
304
    opj_stream_set_user_data(stream, state, NULL);
305
#endif
306

307
    /* Setup an opj_image */
308
    if (strcmp(im->mode, "L") == 0) {
309
        components = 1;
310
        color_space = OPJ_CLRSPC_GRAY;
311
        pack = j2k_pack_l;
312
    } else if (strcmp(im->mode, "I;16") == 0 || strcmp(im->mode, "I;16B") == 0) {
313
        components = 1;
314
        color_space = OPJ_CLRSPC_GRAY;
315
        pack = j2k_pack_i16;
316
        prec = 16;
317
    } else if (strcmp(im->mode, "LA") == 0) {
318
        components = 2;
319
        color_space = OPJ_CLRSPC_GRAY;
320
        pack = j2k_pack_la;
321
    } else if (strcmp(im->mode, "RGB") == 0) {
322
        components = 3;
323
        color_space = OPJ_CLRSPC_SRGB;
324
        pack = j2k_pack_rgb;
325
    } else if (strcmp(im->mode, "YCbCr") == 0) {
326
        components = 3;
327
        color_space = OPJ_CLRSPC_SYCC;
328
        pack = j2k_pack_rgb;
329
    } else if (strcmp(im->mode, "RGBA") == 0) {
330
        components = 4;
331
        color_space = OPJ_CLRSPC_SRGB;
332
        pack = j2k_pack_rgba;
333
    } else {
334
        state->errcode = IMAGING_CODEC_BROKEN;
335
        state->state = J2K_STATE_FAILED;
336
        goto quick_exit;
337
    }
338

339
    for (n = 0; n < components; ++n) {
340
        image_params[n].dx = image_params[n].dy = 1;
341
        image_params[n].w = im->xsize;
342
        image_params[n].h = im->ysize;
343
        image_params[n].x0 = image_params[n].y0 = 0;
344
        image_params[n].prec = prec;
345
        image_params[n].sgnd = context->sgnd == 0 ? 0 : 1;
346
    }
347

348
    image = opj_image_create(components, image_params, color_space);
349
    if (!image) {
350
        state->errcode = IMAGING_CODEC_BROKEN;
351
        state->state = J2K_STATE_FAILED;
352
        goto quick_exit;
353
    }
354

355
    /* Setup compression context */
356
    context->error_msg = NULL;
357

358
    opj_set_default_encoder_parameters(&params);
359

360
    params.image_offset_x0 = context->offset_x;
361
    params.image_offset_y0 = context->offset_y;
362

363
    if (context->tile_size_x && context->tile_size_y) {
364
        params.tile_size_on = OPJ_TRUE;
365
        params.cp_tx0 = context->tile_offset_x;
366
        params.cp_ty0 = context->tile_offset_y;
367
        params.cp_tdx = context->tile_size_x;
368
        params.cp_tdy = context->tile_size_y;
369

370
        tile_width = params.cp_tdx;
371
        tile_height = params.cp_tdy;
372
    } else {
373
        params.cp_tx0 = 0;
374
        params.cp_ty0 = 0;
375
        params.cp_tdx = 1;
376
        params.cp_tdy = 1;
377

378
        tile_width = im->xsize;
379
        tile_height = im->ysize;
380
    }
381

382
    if (context->quality_layers && PySequence_Check(context->quality_layers)) {
383
        Py_ssize_t len = PySequence_Length(context->quality_layers);
384
        Py_ssize_t n;
385
        float *pq;
386

387
        if (len > 0) {
388
            if ((size_t)len > sizeof(params.tcp_rates) / sizeof(params.tcp_rates[0])) {
389
                len = sizeof(params.tcp_rates) / sizeof(params.tcp_rates[0]);
390
            }
391

392
            params.tcp_numlayers = (int)len;
393

394
            if (context->quality_is_in_db) {
395
                params.cp_disto_alloc = params.cp_fixed_alloc = 0;
396
                params.cp_fixed_quality = 1;
397
                pq = params.tcp_distoratio;
398
            } else {
399
                params.cp_disto_alloc = 1;
400
                params.cp_fixed_alloc = params.cp_fixed_quality = 0;
401
                pq = params.tcp_rates;
402
            }
403

404
            for (n = 0; n < len; ++n) {
405
                PyObject *obj = PySequence_ITEM(context->quality_layers, n);
406
                pq[n] = PyFloat_AsDouble(obj);
407
            }
408
        }
409
    } else {
410
        params.tcp_numlayers = 1;
411
        params.tcp_rates[0] = 0;
412
        params.cp_disto_alloc = 1;
413
    }
414

415
    if (context->num_resolutions) {
416
        params.numresolution = context->num_resolutions;
417
    }
418

419
    if (context->cblk_width >= 4 && context->cblk_width <= 1024 &&
420
        context->cblk_height >= 4 && context->cblk_height <= 1024 &&
421
        context->cblk_width * context->cblk_height <= 4096) {
422
        params.cblockw_init = context->cblk_width;
423
        params.cblockh_init = context->cblk_height;
424
    }
425

426
    if (context->precinct_width >= 4 && context->precinct_height >= 4 &&
427
        context->precinct_width >= context->cblk_width &&
428
        context->precinct_height > context->cblk_height) {
429
        params.prcw_init[0] = context->precinct_width;
430
        params.prch_init[0] = context->precinct_height;
431
        params.res_spec = 1;
432
        params.csty |= 0x01;
433
    }
434

435
    params.irreversible = context->irreversible;
436
    if (components == 3) {
437
        params.tcp_mct = context->mct;
438
    }
439

440
    if (context->comment) {
441
        params.cp_comment = context->comment;
442
    }
443

444
    params.prog_order = context->progression;
445

446
    params.cp_cinema = context->cinema_mode;
447

448
    switch (params.cp_cinema) {
449
        case OPJ_OFF:
450
            params.cp_rsiz = OPJ_STD_RSIZ;
451
            break;
452
        case OPJ_CINEMA2K_24:
453
        case OPJ_CINEMA2K_48:
454
            params.cp_rsiz = OPJ_CINEMA2K;
455
            if (params.numresolution > 6) {
456
                params.numresolution = 6;
457
            }
458
            break;
459
        case OPJ_CINEMA4K_24:
460
            params.cp_rsiz = OPJ_CINEMA4K;
461
            if (params.numresolution > 7) {
462
                params.numresolution = 7;
463
            }
464
            break;
465
    }
466

467
    if (!context->num_resolutions) {
468
        while (tile_width < (1U << (params.numresolution - 1U)) ||
469
               tile_height < (1U << (params.numresolution - 1U))) {
470
            params.numresolution -= 1;
471
        }
472
    }
473

474
    if (context->cinema_mode != OPJ_OFF) {
475
        j2k_set_cinema_params(im, components, &params);
476
    }
477

478
    /* Set up the reference grid in the image */
479
    image->x0 = params.image_offset_x0;
480
    image->y0 = params.image_offset_y0;
481
    image->x1 = xsiz = im->xsize + params.image_offset_x0;
482
    image->y1 = ysiz = im->ysize + params.image_offset_y0;
483

484
    /* Create the compressor */
485
    codec = opj_create_compress(context->format);
486

487
    if (!codec) {
488
        state->errcode = IMAGING_CODEC_BROKEN;
489
        state->state = J2K_STATE_FAILED;
490
        goto quick_exit;
491
    }
492

493
    if (strcmp(im->mode, "RGBA") == 0) {
494
        image->comps[3].alpha = 1;
495
    } else if (strcmp(im->mode, "LA") == 0) {
496
        image->comps[1].alpha = 1;
497
    }
498

499
    opj_set_error_handler(codec, j2k_error, context);
500
    opj_set_info_handler(codec, j2k_warn, context);
501
    opj_set_warning_handler(codec, j2k_warn, context);
502
    opj_setup_encoder(codec, &params, image);
503

504
    /* Enabling PLT markers only supported in OpenJPEG 2.4.0 and up */
505
#if ((OPJ_VERSION_MAJOR == 2 && OPJ_VERSION_MINOR >= 4) || OPJ_VERSION_MAJOR > 2)
506
    if (context->plt) {
507
        const char *plt_option[2] = {"PLT=YES", NULL};
508
        opj_encoder_set_extra_options(codec, plt_option);
509
    }
510
#endif
511

512
    /* Start encoding */
513
    if (!opj_start_compress(codec, image, stream)) {
514
        state->errcode = IMAGING_CODEC_BROKEN;
515
        state->state = J2K_STATE_FAILED;
516
        goto quick_exit;
517
    }
518

519
    /* Write each tile */
520
    tiles_x = (im->xsize + (params.image_offset_x0 - params.cp_tx0) + tile_width - 1) /
521
              tile_width;
522
    tiles_y = (im->ysize + (params.image_offset_y0 - params.cp_ty0) + tile_height - 1) /
523
              tile_height;
524

525
    /* check for integer overflow for the malloc line, checking any expression
526
       that may multiply either tile_width or tile_height */
527
    _overflow_scale_factor = components * prec;
528
    if ((tile_width > UINT_MAX / _overflow_scale_factor) ||
529
        (tile_height > UINT_MAX / _overflow_scale_factor) ||
530
        (tile_width > UINT_MAX / (tile_height * _overflow_scale_factor)) ||
531
        (tile_height > UINT_MAX / (tile_width * _overflow_scale_factor))) {
532
        state->errcode = IMAGING_CODEC_BROKEN;
533
        state->state = J2K_STATE_FAILED;
534
        goto quick_exit;
535
    }
536
    /* malloc check ok, checked for overflow above */
537
    state->buffer = malloc(tile_width * tile_height * components * prec / 8);
538
    if (!state->buffer) {
539
        state->errcode = IMAGING_CODEC_BROKEN;
540
        state->state = J2K_STATE_FAILED;
541
        goto quick_exit;
542
    }
543

544
    tile_ndx = 0;
545
    for (y = 0; y < tiles_y; ++y) {
546
        int ty0 = params.cp_ty0 + y * tile_height;
547
        unsigned ty1 = ty0 + tile_height;
548
        unsigned pixy, pixh;
549

550
        if (ty0 < params.image_offset_y0) {
551
            ty0 = params.image_offset_y0;
552
        }
553
        if (ty1 > ysiz) {
554
            ty1 = ysiz;
555
        }
556

557
        pixy = ty0 - params.image_offset_y0;
558
        pixh = ty1 - ty0;
559

560
        for (x = 0; x < tiles_x; ++x) {
561
            int tx0 = params.cp_tx0 + x * tile_width;
562
            unsigned tx1 = tx0 + tile_width;
563
            unsigned pixx, pixw;
564
            unsigned data_size;
565

566
            if (tx0 < params.image_offset_x0) {
567
                tx0 = params.image_offset_x0;
568
            }
569
            if (tx1 > xsiz) {
570
                tx1 = xsiz;
571
            }
572

573
            pixx = tx0 - params.image_offset_x0;
574
            pixw = tx1 - tx0;
575

576
            pack(im, state->buffer, pixx, pixy, pixw, pixh);
577

578
            data_size = pixw * pixh * components * prec / 8;
579

580
            if (!opj_write_tile(codec, tile_ndx++, state->buffer, data_size, stream)) {
581
                state->errcode = IMAGING_CODEC_BROKEN;
582
                state->state = J2K_STATE_FAILED;
583
                goto quick_exit;
584
            }
585
        }
586
    }
587

588
    if (!opj_end_compress(codec, stream)) {
589
        state->errcode = IMAGING_CODEC_BROKEN;
590
        state->state = J2K_STATE_FAILED;
591
        goto quick_exit;
592
    }
593

594
    state->errcode = IMAGING_CODEC_END;
595
    state->state = J2K_STATE_DONE;
596
    ret = -1;
597

598
quick_exit:
599
    if (codec) {
600
        opj_destroy_codec(codec);
601
    }
602
    if (image) {
603
        opj_image_destroy(image);
604
    }
605
    if (stream) {
606
        opj_stream_destroy(stream);
607
    }
608

609
    return ret;
610
}
611

612
int
613
ImagingJpeg2KEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
614
    if (state->state == J2K_STATE_FAILED) {
615
        return -1;
616
    }
617

618
    if (state->state == J2K_STATE_START) {
619
        state->state = J2K_STATE_ENCODING;
620

621
        return j2k_encode_entry(im, state);
622
    }
623

624
    return -1;
625
}
626

627
/* -------------------------------------------------------------------- */
628
/* Cleanup                                                              */
629
/* -------------------------------------------------------------------- */
630

631
int
632
ImagingJpeg2KEncodeCleanup(ImagingCodecState state) {
633
    JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context;
634

635
    if (context->quality_layers) {
636
        Py_XDECREF(context->quality_layers);
637
        context->quality_layers = NULL;
638
    }
639

640
    if (context->error_msg) {
641
        free((void *)context->error_msg);
642
    }
643

644
    if (context->comment) {
645
        free((void *)context->comment);
646
    }
647

648
    context->error_msg = NULL;
649
    context->comment = NULL;
650

651
    return -1;
652
}
653

654
#endif /* HAVE_OPENJPEG */
655

656
/*
657
 * Local Variables:
658
 * c-basic-offset: 4
659
 * End:
660
 *
661
 */
662

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

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

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

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