Pillow

Форк
0
/
_webp.c 
833 строки · 24.6 Кб
1
#define PY_SSIZE_T_CLEAN
2
#include <Python.h>
3
#include "libImaging/Imaging.h"
4
#include <webp/encode.h>
5
#include <webp/decode.h>
6
#include <webp/types.h>
7
#include <webp/mux.h>
8
#include <webp/demux.h>
9

10
/*
11
 * Check the versions from mux.h and demux.h, to ensure the WebPAnimEncoder and
12
 * WebPAnimDecoder APIs are present (initial support was added in 0.5.0). The
13
 * very early versions had some significant differences, so we require later
14
 * versions.
15
 */
16
#if WEBP_MUX_ABI_VERSION < 0x0106 || WEBP_DEMUX_ABI_VERSION < 0x0107
17
#error libwebp 0.5.0 and above is required. Upgrade libwebp or build Pillow with --disable-webp flag
18
#endif
19

20
void
21
ImagingSectionEnter(ImagingSectionCookie *cookie) {
22
    *cookie = (PyThreadState *)PyEval_SaveThread();
23
}
24

25
void
26
ImagingSectionLeave(ImagingSectionCookie *cookie) {
27
    PyEval_RestoreThread((PyThreadState *)*cookie);
28
}
29

30
/* -------------------------------------------------------------------- */
31
/* WebP Muxer Error Handling                                            */
32
/* -------------------------------------------------------------------- */
33

34
static const char *const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
35
    "WEBP_MUX_NOT_FOUND",
36
    "WEBP_MUX_INVALID_ARGUMENT",
37
    "WEBP_MUX_BAD_DATA",
38
    "WEBP_MUX_MEMORY_ERROR",
39
    "WEBP_MUX_NOT_ENOUGH_DATA"
40
};
41

42
PyObject *
43
HandleMuxError(WebPMuxError err, char *chunk) {
44
    char message[100];
45
    int message_len;
46
    assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
47

48
    // Check for a memory error first
49
    if (err == WEBP_MUX_MEMORY_ERROR) {
50
        return PyErr_NoMemory();
51
    }
52

53
    // Create the error message
54
    if (chunk == NULL) {
55
        message_len =
56
            sprintf(message, "could not assemble chunks: %s", kErrorMessages[-err]);
57
    } else {
58
        message_len = sprintf(
59
            message, "could not set %.4s chunk: %s", chunk, kErrorMessages[-err]
60
        );
61
    }
62
    if (message_len < 0) {
63
        PyErr_SetString(PyExc_RuntimeError, "failed to construct error message");
64
        return NULL;
65
    }
66

67
    // Set the proper error type
68
    switch (err) {
69
        case WEBP_MUX_NOT_FOUND:
70
        case WEBP_MUX_INVALID_ARGUMENT:
71
            PyErr_SetString(PyExc_ValueError, message);
72
            break;
73

74
        case WEBP_MUX_BAD_DATA:
75
        case WEBP_MUX_NOT_ENOUGH_DATA:
76
            PyErr_SetString(PyExc_OSError, message);
77
            break;
78

79
        default:
80
            PyErr_SetString(PyExc_RuntimeError, message);
81
            break;
82
    }
83
    return NULL;
84
}
85

86
/* -------------------------------------------------------------------- */
87
/* WebP Animation Support                                               */
88
/* -------------------------------------------------------------------- */
89

90
// Encoder type
91
typedef struct {
92
    PyObject_HEAD WebPAnimEncoder *enc;
93
    WebPPicture frame;
94
} WebPAnimEncoderObject;
95

96
static PyTypeObject WebPAnimEncoder_Type;
97

98
// Decoder type
99
typedef struct {
100
    PyObject_HEAD WebPAnimDecoder *dec;
101
    WebPAnimInfo info;
102
    WebPData data;
103
    char *mode;
104
} WebPAnimDecoderObject;
105

106
static PyTypeObject WebPAnimDecoder_Type;
107

108
// Encoder functions
109
PyObject *
110
_anim_encoder_new(PyObject *self, PyObject *args) {
111
    int width, height;
112
    uint32_t bgcolor;
113
    int loop_count;
114
    int minimize_size;
115
    int kmin, kmax;
116
    int allow_mixed;
117
    int verbose;
118
    WebPAnimEncoderOptions enc_options;
119
    WebPAnimEncoderObject *encp = NULL;
120
    WebPAnimEncoder *enc = NULL;
121

122
    if (!PyArg_ParseTuple(
123
            args,
124
            "iiIiiiiii",
125
            &width,
126
            &height,
127
            &bgcolor,
128
            &loop_count,
129
            &minimize_size,
130
            &kmin,
131
            &kmax,
132
            &allow_mixed,
133
            &verbose
134
        )) {
135
        return NULL;
136
    }
137

138
    // Setup and configure the encoder's options (these are animation-specific)
139
    if (!WebPAnimEncoderOptionsInit(&enc_options)) {
140
        PyErr_SetString(PyExc_RuntimeError, "failed to initialize encoder options");
141
        return NULL;
142
    }
143
    enc_options.anim_params.bgcolor = bgcolor;
144
    enc_options.anim_params.loop_count = loop_count;
145
    enc_options.minimize_size = minimize_size;
146
    enc_options.kmin = kmin;
147
    enc_options.kmax = kmax;
148
    enc_options.allow_mixed = allow_mixed;
149
    enc_options.verbose = verbose;
150

151
    // Validate canvas dimensions
152
    if (width <= 0 || height <= 0) {
153
        PyErr_SetString(PyExc_ValueError, "invalid canvas dimensions");
154
        return NULL;
155
    }
156

157
    // Create a new animation encoder and picture frame
158
    encp = PyObject_New(WebPAnimEncoderObject, &WebPAnimEncoder_Type);
159
    if (encp) {
160
        if (WebPPictureInit(&(encp->frame))) {
161
            enc = WebPAnimEncoderNew(width, height, &enc_options);
162
            if (enc) {
163
                encp->enc = enc;
164
                return (PyObject *)encp;
165
            }
166
            WebPPictureFree(&(encp->frame));
167
        }
168
        PyObject_Del(encp);
169
    }
170
    PyErr_SetString(PyExc_RuntimeError, "could not create encoder object");
171
    return NULL;
172
}
173

174
void
175
_anim_encoder_dealloc(PyObject *self) {
176
    WebPAnimEncoderObject *encp = (WebPAnimEncoderObject *)self;
177
    WebPPictureFree(&(encp->frame));
178
    WebPAnimEncoderDelete(encp->enc);
179
}
180

181
PyObject *
182
_anim_encoder_add(PyObject *self, PyObject *args) {
183
    uint8_t *rgb;
184
    Py_ssize_t size;
185
    int timestamp;
186
    int width;
187
    int height;
188
    char *mode;
189
    int lossless;
190
    float quality_factor;
191
    float alpha_quality_factor;
192
    int method;
193
    WebPConfig config;
194
    WebPAnimEncoderObject *encp = (WebPAnimEncoderObject *)self;
195
    WebPAnimEncoder *enc = encp->enc;
196
    WebPPicture *frame = &(encp->frame);
197

198
    if (!PyArg_ParseTuple(
199
            args,
200
            "z#iiisiffi",
201
            (char **)&rgb,
202
            &size,
203
            &timestamp,
204
            &width,
205
            &height,
206
            &mode,
207
            &lossless,
208
            &quality_factor,
209
            &alpha_quality_factor,
210
            &method
211
        )) {
212
        return NULL;
213
    }
214

215
    // Check for NULL frame, which sets duration of final frame
216
    if (!rgb) {
217
        WebPAnimEncoderAdd(enc, NULL, timestamp, NULL);
218
        Py_RETURN_NONE;
219
    }
220

221
    // Setup config for this frame
222
    if (!WebPConfigInit(&config)) {
223
        PyErr_SetString(PyExc_RuntimeError, "failed to initialize config!");
224
        return NULL;
225
    }
226
    config.lossless = lossless;
227
    config.quality = quality_factor;
228
    config.alpha_quality = alpha_quality_factor;
229
    config.method = method;
230

231
    // Validate the config
232
    if (!WebPValidateConfig(&config)) {
233
        PyErr_SetString(PyExc_ValueError, "invalid configuration");
234
        return NULL;
235
    }
236

237
    // Populate the frame with raw bytes passed to us
238
    frame->width = width;
239
    frame->height = height;
240
    frame->use_argb = 1;  // Don't convert RGB pixels to YUV
241
    if (strcmp(mode, "RGBA") == 0) {
242
        WebPPictureImportRGBA(frame, rgb, 4 * width);
243
    } else if (strcmp(mode, "RGBX") == 0) {
244
        WebPPictureImportRGBX(frame, rgb, 4 * width);
245
    } else {
246
        WebPPictureImportRGB(frame, rgb, 3 * width);
247
    }
248

249
    // Add the frame to the encoder
250
    if (!WebPAnimEncoderAdd(enc, frame, timestamp, &config)) {
251
        PyErr_SetString(PyExc_RuntimeError, WebPAnimEncoderGetError(enc));
252
        return NULL;
253
    }
254

255
    Py_RETURN_NONE;
256
}
257

258
PyObject *
259
_anim_encoder_assemble(PyObject *self, PyObject *args) {
260
    uint8_t *icc_bytes;
261
    uint8_t *exif_bytes;
262
    uint8_t *xmp_bytes;
263
    Py_ssize_t icc_size;
264
    Py_ssize_t exif_size;
265
    Py_ssize_t xmp_size;
266
    WebPData webp_data;
267
    WebPAnimEncoderObject *encp = (WebPAnimEncoderObject *)self;
268
    WebPAnimEncoder *enc = encp->enc;
269
    WebPMux *mux = NULL;
270
    PyObject *ret = NULL;
271

272
    if (!PyArg_ParseTuple(
273
            args,
274
            "s#s#s#",
275
            &icc_bytes,
276
            &icc_size,
277
            &exif_bytes,
278
            &exif_size,
279
            &xmp_bytes,
280
            &xmp_size
281
        )) {
282
        return NULL;
283
    }
284

285
    // Init the output buffer
286
    WebPDataInit(&webp_data);
287

288
    // Assemble everything into the output buffer
289
    if (!WebPAnimEncoderAssemble(enc, &webp_data)) {
290
        PyErr_SetString(PyExc_RuntimeError, WebPAnimEncoderGetError(enc));
291
        return NULL;
292
    }
293

294
    // Re-mux to add metadata as needed
295
    if (icc_size > 0 || exif_size > 0 || xmp_size > 0) {
296
        WebPMuxError err = WEBP_MUX_OK;
297
        int i_icc_size = (int)icc_size;
298
        int i_exif_size = (int)exif_size;
299
        int i_xmp_size = (int)xmp_size;
300
        WebPData icc_profile = {icc_bytes, i_icc_size};
301
        WebPData exif = {exif_bytes, i_exif_size};
302
        WebPData xmp = {xmp_bytes, i_xmp_size};
303

304
        mux = WebPMuxCreate(&webp_data, 1);
305
        if (mux == NULL) {
306
            PyErr_SetString(PyExc_RuntimeError, "could not re-mux to add metadata");
307
            return NULL;
308
        }
309
        WebPDataClear(&webp_data);
310

311
        // Add ICCP chunk
312
        if (i_icc_size > 0) {
313
            err = WebPMuxSetChunk(mux, "ICCP", &icc_profile, 1);
314
            if (err != WEBP_MUX_OK) {
315
                return HandleMuxError(err, "ICCP");
316
            }
317
        }
318

319
        // Add EXIF chunk
320
        if (i_exif_size > 0) {
321
            err = WebPMuxSetChunk(mux, "EXIF", &exif, 1);
322
            if (err != WEBP_MUX_OK) {
323
                return HandleMuxError(err, "EXIF");
324
            }
325
        }
326

327
        // Add XMP chunk
328
        if (i_xmp_size > 0) {
329
            err = WebPMuxSetChunk(mux, "XMP ", &xmp, 1);
330
            if (err != WEBP_MUX_OK) {
331
                return HandleMuxError(err, "XMP");
332
            }
333
        }
334

335
        err = WebPMuxAssemble(mux, &webp_data);
336
        if (err != WEBP_MUX_OK) {
337
            return HandleMuxError(err, NULL);
338
        }
339
    }
340

341
    // Convert to Python bytes
342
    ret = PyBytes_FromStringAndSize((char *)webp_data.bytes, webp_data.size);
343
    WebPDataClear(&webp_data);
344

345
    // If we had to re-mux, we should free it now that we're done with it
346
    if (mux != NULL) {
347
        WebPMuxDelete(mux);
348
    }
349

350
    return ret;
351
}
352

353
// Decoder functions
354
PyObject *
355
_anim_decoder_new(PyObject *self, PyObject *args) {
356
    PyBytesObject *webp_string;
357
    const uint8_t *webp;
358
    Py_ssize_t size;
359
    WebPData webp_src;
360
    char *mode;
361
    WebPDecoderConfig config;
362
    WebPAnimDecoderObject *decp = NULL;
363
    WebPAnimDecoder *dec = NULL;
364

365
    if (!PyArg_ParseTuple(args, "S", &webp_string)) {
366
        return NULL;
367
    }
368
    PyBytes_AsStringAndSize((PyObject *)webp_string, (char **)&webp, &size);
369
    webp_src.bytes = webp;
370
    webp_src.size = size;
371

372
    // Sniff the mode, since the decoder API doesn't tell us
373
    mode = "RGBA";
374
    if (WebPGetFeatures(webp, size, &config.input) == VP8_STATUS_OK) {
375
        if (!config.input.has_alpha) {
376
            mode = "RGBX";
377
        }
378
    }
379

380
    // Create the decoder (default mode is RGBA, if no options passed)
381
    decp = PyObject_New(WebPAnimDecoderObject, &WebPAnimDecoder_Type);
382
    if (decp) {
383
        decp->mode = mode;
384
        if (WebPDataCopy(&webp_src, &(decp->data))) {
385
            dec = WebPAnimDecoderNew(&(decp->data), NULL);
386
            if (dec) {
387
                if (WebPAnimDecoderGetInfo(dec, &(decp->info))) {
388
                    decp->dec = dec;
389
                    return (PyObject *)decp;
390
                }
391
            }
392
            WebPDataClear(&(decp->data));
393
        }
394
        PyObject_Del(decp);
395
    }
396
    PyErr_SetString(PyExc_OSError, "could not create decoder object");
397
    return NULL;
398
}
399

400
void
401
_anim_decoder_dealloc(PyObject *self) {
402
    WebPAnimDecoderObject *decp = (WebPAnimDecoderObject *)self;
403
    WebPDataClear(&(decp->data));
404
    WebPAnimDecoderDelete(decp->dec);
405
}
406

407
PyObject *
408
_anim_decoder_get_info(PyObject *self) {
409
    WebPAnimDecoderObject *decp = (WebPAnimDecoderObject *)self;
410
    WebPAnimInfo *info = &(decp->info);
411

412
    return Py_BuildValue(
413
        "IIIIIs",
414
        info->canvas_width,
415
        info->canvas_height,
416
        info->loop_count,
417
        info->bgcolor,
418
        info->frame_count,
419
        decp->mode
420
    );
421
}
422

423
PyObject *
424
_anim_decoder_get_chunk(PyObject *self, PyObject *args) {
425
    char *mode;
426
    WebPAnimDecoderObject *decp = (WebPAnimDecoderObject *)self;
427
    const WebPDemuxer *demux;
428
    WebPChunkIterator iter;
429
    PyObject *ret;
430

431
    if (!PyArg_ParseTuple(args, "s", &mode)) {
432
        return NULL;
433
    }
434

435
    demux = WebPAnimDecoderGetDemuxer(decp->dec);
436
    if (!WebPDemuxGetChunk(demux, mode, 1, &iter)) {
437
        Py_RETURN_NONE;
438
    }
439

440
    ret = PyBytes_FromStringAndSize((const char *)iter.chunk.bytes, iter.chunk.size);
441
    WebPDemuxReleaseChunkIterator(&iter);
442

443
    return ret;
444
}
445

446
PyObject *
447
_anim_decoder_get_next(PyObject *self) {
448
    uint8_t *buf;
449
    int timestamp;
450
    int ok;
451
    PyObject *bytes;
452
    PyObject *ret;
453
    ImagingSectionCookie cookie;
454
    WebPAnimDecoderObject *decp = (WebPAnimDecoderObject *)self;
455

456
    ImagingSectionEnter(&cookie);
457
    ok = WebPAnimDecoderGetNext(decp->dec, &buf, &timestamp);
458
    ImagingSectionLeave(&cookie);
459
    if (!ok) {
460
        PyErr_SetString(PyExc_OSError, "failed to read next frame");
461
        return NULL;
462
    }
463

464
    bytes = PyBytes_FromStringAndSize(
465
        (char *)buf, decp->info.canvas_width * 4 * decp->info.canvas_height
466
    );
467

468
    ret = Py_BuildValue("Si", bytes, timestamp);
469

470
    Py_DECREF(bytes);
471
    return ret;
472
}
473

474
PyObject *
475
_anim_decoder_reset(PyObject *self) {
476
    WebPAnimDecoderObject *decp = (WebPAnimDecoderObject *)self;
477
    WebPAnimDecoderReset(decp->dec);
478
    Py_RETURN_NONE;
479
}
480

481
/* -------------------------------------------------------------------- */
482
/* Type Definitions                                                     */
483
/* -------------------------------------------------------------------- */
484

485
// WebPAnimEncoder methods
486
static struct PyMethodDef _anim_encoder_methods[] = {
487
    {"add", (PyCFunction)_anim_encoder_add, METH_VARARGS, "add"},
488
    {"assemble", (PyCFunction)_anim_encoder_assemble, METH_VARARGS, "assemble"},
489
    {NULL, NULL} /* sentinel */
490
};
491

492
// WebPAnimEncoder type definition
493
static PyTypeObject WebPAnimEncoder_Type = {
494
    PyVarObject_HEAD_INIT(NULL, 0) "WebPAnimEncoder", /*tp_name */
495
    sizeof(WebPAnimEncoderObject),                    /*tp_basicsize */
496
    0,                                                /*tp_itemsize */
497
    /* methods */
498
    (destructor)_anim_encoder_dealloc, /*tp_dealloc*/
499
    0,                                 /*tp_vectorcall_offset*/
500
    0,                                 /*tp_getattr*/
501
    0,                                 /*tp_setattr*/
502
    0,                                 /*tp_as_async*/
503
    0,                                 /*tp_repr*/
504
    0,                                 /*tp_as_number*/
505
    0,                                 /*tp_as_sequence*/
506
    0,                                 /*tp_as_mapping*/
507
    0,                                 /*tp_hash*/
508
    0,                                 /*tp_call*/
509
    0,                                 /*tp_str*/
510
    0,                                 /*tp_getattro*/
511
    0,                                 /*tp_setattro*/
512
    0,                                 /*tp_as_buffer*/
513
    Py_TPFLAGS_DEFAULT,                /*tp_flags*/
514
    0,                                 /*tp_doc*/
515
    0,                                 /*tp_traverse*/
516
    0,                                 /*tp_clear*/
517
    0,                                 /*tp_richcompare*/
518
    0,                                 /*tp_weaklistoffset*/
519
    0,                                 /*tp_iter*/
520
    0,                                 /*tp_iternext*/
521
    _anim_encoder_methods,             /*tp_methods*/
522
    0,                                 /*tp_members*/
523
    0,                                 /*tp_getset*/
524
};
525

526
// WebPAnimDecoder methods
527
static struct PyMethodDef _anim_decoder_methods[] = {
528
    {"get_info", (PyCFunction)_anim_decoder_get_info, METH_NOARGS, "get_info"},
529
    {"get_chunk", (PyCFunction)_anim_decoder_get_chunk, METH_VARARGS, "get_chunk"},
530
    {"get_next", (PyCFunction)_anim_decoder_get_next, METH_NOARGS, "get_next"},
531
    {"reset", (PyCFunction)_anim_decoder_reset, METH_NOARGS, "reset"},
532
    {NULL, NULL} /* sentinel */
533
};
534

535
// WebPAnimDecoder type definition
536
static PyTypeObject WebPAnimDecoder_Type = {
537
    PyVarObject_HEAD_INIT(NULL, 0) "WebPAnimDecoder", /*tp_name */
538
    sizeof(WebPAnimDecoderObject),                    /*tp_basicsize */
539
    0,                                                /*tp_itemsize */
540
    /* methods */
541
    (destructor)_anim_decoder_dealloc, /*tp_dealloc*/
542
    0,                                 /*tp_vectorcall_offset*/
543
    0,                                 /*tp_getattr*/
544
    0,                                 /*tp_setattr*/
545
    0,                                 /*tp_as_async*/
546
    0,                                 /*tp_repr*/
547
    0,                                 /*tp_as_number*/
548
    0,                                 /*tp_as_sequence*/
549
    0,                                 /*tp_as_mapping*/
550
    0,                                 /*tp_hash*/
551
    0,                                 /*tp_call*/
552
    0,                                 /*tp_str*/
553
    0,                                 /*tp_getattro*/
554
    0,                                 /*tp_setattro*/
555
    0,                                 /*tp_as_buffer*/
556
    Py_TPFLAGS_DEFAULT,                /*tp_flags*/
557
    0,                                 /*tp_doc*/
558
    0,                                 /*tp_traverse*/
559
    0,                                 /*tp_clear*/
560
    0,                                 /*tp_richcompare*/
561
    0,                                 /*tp_weaklistoffset*/
562
    0,                                 /*tp_iter*/
563
    0,                                 /*tp_iternext*/
564
    _anim_decoder_methods,             /*tp_methods*/
565
    0,                                 /*tp_members*/
566
    0,                                 /*tp_getset*/
567
};
568

569
/* -------------------------------------------------------------------- */
570
/* Legacy WebP Support                                                  */
571
/* -------------------------------------------------------------------- */
572

573
PyObject *
574
WebPEncode_wrapper(PyObject *self, PyObject *args) {
575
    int width;
576
    int height;
577
    int lossless;
578
    float quality_factor;
579
    float alpha_quality_factor;
580
    int method;
581
    int exact;
582
    uint8_t *rgb;
583
    uint8_t *icc_bytes;
584
    uint8_t *exif_bytes;
585
    uint8_t *xmp_bytes;
586
    uint8_t *output;
587
    char *mode;
588
    Py_ssize_t size;
589
    Py_ssize_t icc_size;
590
    Py_ssize_t exif_size;
591
    Py_ssize_t xmp_size;
592
    size_t ret_size;
593
    int rgba_mode;
594
    int channels;
595
    int ok;
596
    ImagingSectionCookie cookie;
597
    WebPConfig config;
598
    WebPMemoryWriter writer;
599
    WebPPicture pic;
600

601
    if (!PyArg_ParseTuple(
602
            args,
603
            "y#iiiffss#iis#s#",
604
            (char **)&rgb,
605
            &size,
606
            &width,
607
            &height,
608
            &lossless,
609
            &quality_factor,
610
            &alpha_quality_factor,
611
            &mode,
612
            &icc_bytes,
613
            &icc_size,
614
            &method,
615
            &exact,
616
            &exif_bytes,
617
            &exif_size,
618
            &xmp_bytes,
619
            &xmp_size
620
        )) {
621
        return NULL;
622
    }
623

624
    rgba_mode = strcmp(mode, "RGBA") == 0;
625
    if (!rgba_mode && strcmp(mode, "RGB") != 0) {
626
        Py_RETURN_NONE;
627
    }
628

629
    channels = rgba_mode ? 4 : 3;
630
    if (size < width * height * channels) {
631
        Py_RETURN_NONE;
632
    }
633

634
    // Setup config for this frame
635
    if (!WebPConfigInit(&config)) {
636
        PyErr_SetString(PyExc_RuntimeError, "failed to initialize config!");
637
        return NULL;
638
    }
639
    config.lossless = lossless;
640
    config.quality = quality_factor;
641
    config.alpha_quality = alpha_quality_factor;
642
    config.method = method;
643
    config.exact = exact;
644

645
    // Validate the config
646
    if (!WebPValidateConfig(&config)) {
647
        PyErr_SetString(PyExc_ValueError, "invalid configuration");
648
        return NULL;
649
    }
650

651
    if (!WebPPictureInit(&pic)) {
652
        PyErr_SetString(PyExc_ValueError, "could not initialise picture");
653
        return NULL;
654
    }
655
    pic.width = width;
656
    pic.height = height;
657
    pic.use_argb = 1;  // Don't convert RGB pixels to YUV
658

659
    if (rgba_mode) {
660
        WebPPictureImportRGBA(&pic, rgb, channels * width);
661
    } else {
662
        WebPPictureImportRGB(&pic, rgb, channels * width);
663
    }
664

665
    WebPMemoryWriterInit(&writer);
666
    pic.writer = WebPMemoryWrite;
667
    pic.custom_ptr = &writer;
668

669
    ImagingSectionEnter(&cookie);
670
    ok = WebPEncode(&config, &pic);
671
    ImagingSectionLeave(&cookie);
672

673
    WebPPictureFree(&pic);
674
    if (!ok) {
675
        int error_code = (&pic)->error_code;
676
        char message[50] = "";
677
        if (error_code == VP8_ENC_ERROR_BAD_DIMENSION) {
678
            sprintf(
679
                message,
680
                ": Image size exceeds WebP limit of %d pixels",
681
                WEBP_MAX_DIMENSION
682
            );
683
        }
684
        PyErr_Format(PyExc_ValueError, "encoding error %d%s", error_code, message);
685
        return NULL;
686
    }
687
    output = writer.mem;
688
    ret_size = writer.size;
689

690
    {
691
        /* I want to truncate the *_size items that get passed into WebP
692
           data. Pypy2.1.0 had some issues where the Py_ssize_t items had
693
           data in the upper byte. (Not sure why, it shouldn't have been there)
694
        */
695
        int i_icc_size = (int)icc_size;
696
        int i_exif_size = (int)exif_size;
697
        int i_xmp_size = (int)xmp_size;
698
        WebPData output_data = {0};
699
        WebPData image = {output, ret_size};
700
        WebPData icc_profile = {icc_bytes, i_icc_size};
701
        WebPData exif = {exif_bytes, i_exif_size};
702
        WebPData xmp = {xmp_bytes, i_xmp_size};
703
        WebPMuxError err;
704
        int dbg = 0;
705

706
        int copy_data = 0;  // value 1 indicates given data WILL be copied to the mux
707
                            // and value 0 indicates data will NOT be copied.
708

709
        WebPMux *mux = WebPMuxNew();
710
        WebPMuxSetImage(mux, &image, copy_data);
711

712
        if (dbg) {
713
            /* was getting %ld icc_size == 0, icc_size>0 was true */
714
            fprintf(stderr, "icc size %d, %d \n", i_icc_size, i_icc_size > 0);
715
        }
716

717
        if (i_icc_size > 0) {
718
            if (dbg) {
719
                fprintf(stderr, "Adding ICC Profile\n");
720
            }
721
            err = WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data);
722
            if (err != WEBP_MUX_OK) {
723
                return HandleMuxError(err, "ICCP");
724
            }
725
        }
726

727
        if (dbg) {
728
            fprintf(stderr, "exif size %d \n", i_exif_size);
729
        }
730
        if (i_exif_size > 0) {
731
            if (dbg) {
732
                fprintf(stderr, "Adding Exif Data\n");
733
            }
734
            err = WebPMuxSetChunk(mux, "EXIF", &exif, copy_data);
735
            if (err != WEBP_MUX_OK) {
736
                return HandleMuxError(err, "EXIF");
737
            }
738
        }
739

740
        if (dbg) {
741
            fprintf(stderr, "xmp size %d \n", i_xmp_size);
742
        }
743
        if (i_xmp_size > 0) {
744
            if (dbg) {
745
                fprintf(stderr, "Adding XMP Data\n");
746
            }
747
            err = WebPMuxSetChunk(mux, "XMP ", &xmp, copy_data);
748
            if (err != WEBP_MUX_OK) {
749
                return HandleMuxError(err, "XMP ");
750
            }
751
        }
752

753
        WebPMuxAssemble(mux, &output_data);
754
        WebPMuxDelete(mux);
755
        free(output);
756

757
        ret_size = output_data.size;
758
        if (ret_size > 0) {
759
            PyObject *ret =
760
                PyBytes_FromStringAndSize((char *)output_data.bytes, ret_size);
761
            WebPDataClear(&output_data);
762
            return ret;
763
        }
764
    }
765
    Py_RETURN_NONE;
766
}
767

768
// Version as string
769
const char *
770
WebPDecoderVersion_str(void) {
771
    static char version[20];
772
    int version_number = WebPGetDecoderVersion();
773
    sprintf(
774
        version,
775
        "%d.%d.%d",
776
        version_number >> 16,
777
        (version_number >> 8) % 0x100,
778
        version_number % 0x100
779
    );
780
    return version;
781
}
782

783
/* -------------------------------------------------------------------- */
784
/* Module Setup                                                         */
785
/* -------------------------------------------------------------------- */
786

787
static PyMethodDef webpMethods[] = {
788
    {"WebPAnimDecoder", _anim_decoder_new, METH_VARARGS, "WebPAnimDecoder"},
789
    {"WebPAnimEncoder", _anim_encoder_new, METH_VARARGS, "WebPAnimEncoder"},
790
    {"WebPEncode", WebPEncode_wrapper, METH_VARARGS, "WebPEncode"},
791
    {NULL, NULL}
792
};
793

794
static int
795
setup_module(PyObject *m) {
796
    /* Ready object types */
797
    if (PyType_Ready(&WebPAnimDecoder_Type) < 0 ||
798
        PyType_Ready(&WebPAnimEncoder_Type) < 0) {
799
        return -1;
800
    }
801

802
    PyObject *d = PyModule_GetDict(m);
803
    PyObject *v = PyUnicode_FromString(WebPDecoderVersion_str());
804
    PyDict_SetItemString(d, "webpdecoder_version", v ? v : Py_None);
805
    Py_XDECREF(v);
806

807
    return 0;
808
}
809

810
PyMODINIT_FUNC
811
PyInit__webp(void) {
812
    PyObject *m;
813

814
    static PyModuleDef module_def = {
815
        PyModuleDef_HEAD_INIT,
816
        "_webp",     /* m_name */
817
        NULL,        /* m_doc */
818
        -1,          /* m_size */
819
        webpMethods, /* m_methods */
820
    };
821

822
    m = PyModule_Create(&module_def);
823
    if (setup_module(m) < 0) {
824
        Py_DECREF(m);
825
        return NULL;
826
    }
827

828
#ifdef Py_GIL_DISABLED
829
    PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
830
#endif
831

832
    return m;
833
}
834

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

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

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

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