Image-WebP

 view release on metacpan or  search on metacpan

WebP.xs  view on Meta::CPAN

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include <limits.h>
#include <stdint.h>
#include <webp/decode.h>
#include <webp/encode.h>

MODULE = Image::WebP PACKAGE = Image::WebP
PROTOTYPES: DISABLE

void
xs_WebPGetInfo(data_sv, data_size)
    SV* data_sv
    size_t data_size
INIT:
    STRLEN actual_size;
    const uint8_t *data;
    int width = 0, height = 0, res;
PPCODE:
    data = (const uint8_t *)SvPVbyte(data_sv, actual_size);
    if (data_size > (size_t)actual_size) {
        croak("WebP data sizee exceeds the input buffer");
    }
    res = WebPGetInfo(data, data_size, &width, &height);
    XPUSHs(sv_2mortal(newSVnv(res)));
    XPUSHs(sv_2mortal(newSVnv(width)));
    XPUSHs(sv_2mortal(newSVnv(height)));


void
xs_WebPGetFeatures(data_sv, data_size)
    SV* data_sv
    size_t data_size
INIT:
      STRLEN actual_size;
      const uint8_t *data;
      int res;
      WebPBitstreamFeatures features = { 0 };
PPCODE:
    data = (const uint8_t *)SvPVbyte(data_sv, actual_size);
    if (data_size > (size_t)actual_size) {
        croak("WebP data size exceeds the input buffer");
    }
    res = WebPGetFeatures(data, data_size, &features);
    XPUSHs(sv_2mortal(newSVnv(res)));
    XPUSHs(sv_2mortal(newSVnv(features.width)));
    XPUSHs(sv_2mortal(newSVnv(features.height)));
    XPUSHs(sv_2mortal(newSVnv(features.has_alpha)));


void
xs_WebPDecodeSimple(data_sv, data_size, format)
    SV* data_sv
    size_t data_size
    unsigned char format
INIT:
    STRLEN actual_size;
    const uint8_t *data;
    int width = 0, height = 0;
    int channels;
    size_t output_size;
    uint8_t *rgb_data;
    SV *decoded;
PPCODE:
    data = (const uint8_t *)SvPVbyte(data_sv, actual_size);
    if (data_size > (size_t)actual_size) {
        croak("WebP data size exceeds the input buffer");
    }
    switch (format) {
    case 1: channels = 4; rgb_data = WebPDecodeRGBA(data, data_size, &width, &height); break;
    case 2: channels = 4; rgb_data = WebPDecodeARGB(data, data_size, &width, &height); break;
    case 3: channels = 4; rgb_data = WebPDecodeBGRA(data, data_size, &width, &height); break;
    case 4: channels = 3; rgb_data = WebPDecodeRGB(data, data_size, &width, &height);  break;
    case 5: channels = 3; rgb_data = WebPDecodeBGR(data, data_size, &width, &height);  break;
    default: croak("unsupported WebP decode format");
    }

    if (rgb_data == NULL || width <= 0 || height <= 0) {
        croak("failed to decode WebP data");
    }
    if ((size_t)width > SIZE_MAX / (size_t)height
        || (size_t)width * (size_t)height > SIZE_MAX / (size_t)channels) {
        WebPFree(rgb_data);
        croak("decoded WebP dimensions exceed the supported buffer size");
    }

    output_size = (size_t)width * (size_t)height * (size_t)channels;
    decoded = newSVpvn((const char *)rgb_data, output_size);
    WebPFree(rgb_data);

    XPUSHs(sv_2mortal(decoded));
    XPUSHs(sv_2mortal(newSViv(width)));
    XPUSHs(sv_2mortal(newSViv(height)));


void
xs_WebPEncode(rgb_sv, width, height, stride, format, enc_type, quality)
    SV* rgb_sv
    int width
    int height
    int stride
    int format
    int enc_type
    float quality
INIT:
    STRLEN rgb_length;
    const uint8_t *rgb_data;
    uint8_t *data = NULL;
    int channels;
    size_t minimum_length;
    size_t size = 0;
    SV *encoded;
PPCODE:
    rgb_data = (const uint8_t *)SvPVbyte(rgb_sv, rgb_length);

    if (width <= 0 || height <= 0) {
        croak("WebP dimensions must be positive");
    }
    if (format == 1 || format == 2) {
        channels = 3;
    }
    else if (format == 3 || format == 4) {
        channels = 4;
    }
    else {
        croak("unsupported WebP encode format");
    }
    if (width > INT_MAX / channels || stride < width * channels) {
        croak("WebP stride is smaller than one input row");
    }
    if ((size_t)(height - 1) > (SIZE_MAX - (size_t)width * (size_t)channels) / (size_t)stride) {
        croak("WebP input dimensions exceed the supported buffer size");
    }
    minimum_length = (size_t)(height - 1) * (size_t)stride
        + (size_t)width * (size_t)channels;
    if ((size_t)rgb_length < minimum_length) {
        croak("WebP input buffer is shorter than its dimensions and stride require");
    }
    if (quality < 0.0f || quality > 100.0f) {
        croak("WebP quality must be between 0 and 100");
    }

    if (enc_type == 1) {
        switch (format) {
        case 1: size = WebPEncodeRGB(rgb_data, width, height, stride, quality, &data); break;
        case 2: size = WebPEncodeBGR(rgb_data, width, height, stride, quality, &data); break;
        case 3: size = WebPEncodeRGBA(rgb_data, width, height, stride, quality, &data); break;
        case 4: size = WebPEncodeBGRA(rgb_data, width, height, stride, quality, &data); break;
        }
    }
    else if (enc_type == 2) {
        switch (format) {
        case 1: size = WebPEncodeLosslessRGB(rgb_data, width, height, stride, &data); break;
        case 2: size = WebPEncodeLosslessBGR(rgb_data, width, height, stride, &data); break;
        case 3: size = WebPEncodeLosslessRGBA(rgb_data, width, height, stride, &data); break;
        case 4: size = WebPEncodeLosslessBGRA(rgb_data, width, height, stride, &data); break;
        }
    }
    else {
        croak("unsupported WebP encoding type");
    }

    if (size == 0 || data == NULL) {
        croak("failed to encode WebP data");
    }

    encoded = newSVpvn((const char *)data, size);
    WebPFree(data);

    XPUSHs(sv_2mortal(newSVuv(size)));
    XPUSHs(sv_2mortal(encoded));



( run in 0.796 second using v1.01-cache-2.11-cpan-4e7a2411597 )