cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

encode-webp.c (7019B)


      1/*
      2 * Licensed to the Apache Software Foundation (ASF) under one
      3 * or more contributor license agreements.  See the NOTICE file
      4 * distributed with this work for additional information
      5 * regarding copyright ownership.  The ASF licenses this file
      6 * to you under the Apache License, Version 2.0 (the
      7 * "License"); you may not use this file except in compliance
      8 * with the License.  You may obtain a copy of the License at
      9 *
     10 *   http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing,
     13 * software distributed under the License is distributed on an
     14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     15 * KIND, either express or implied.  See the License for the
     16 * specific language governing permissions and limitations
     17 * under the License.
     18 */
     19
     20#include "config.h"
     21
     22#include "encode-webp.h"
     23#include "guacamole/error.h"
     24#include "guacamole/protocol.h"
     25#include "guacamole/stream.h"
     26#include "palette.h"
     27
     28#include <cairo/cairo.h>
     29#include <webp/encode.h>
     30
     31#include <assert.h>
     32#include <inttypes.h>
     33#include <stdint.h>
     34#include <stdlib.h>
     35#include <string.h>
     36
     37/**
     38 * Structure which describes the current state of the WebP image writer.
     39 */
     40typedef struct guac_webp_stream_writer {
     41
     42    /**
     43     * The socket over which all WebP blobs will be written.
     44     */
     45    guac_socket* socket;
     46
     47    /**
     48     * The Guacamole stream to associate with each WebP blob.
     49     */
     50    guac_stream* stream;
     51
     52    /**
     53     * Buffer of pending WebP data.
     54     */
     55    char buffer[GUAC_PROTOCOL_BLOB_MAX_LENGTH];
     56
     57    /**
     58     * The number of bytes currently stored in the buffer.
     59     */
     60    int buffer_size;
     61
     62} guac_webp_stream_writer;
     63
     64/**
     65 * Writes the contents of the WebP stream writer as a blob to its associated
     66 * socket.
     67 *
     68 * @param writer
     69 *     The writer structure to flush.
     70 */
     71static void guac_webp_flush_data(guac_webp_stream_writer* writer) {
     72
     73    /* Send blob */
     74    guac_protocol_send_blob(writer->socket, writer->stream,
     75            writer->buffer, writer->buffer_size);
     76
     77    /* Clear buffer */
     78    writer->buffer_size = 0;
     79
     80}
     81
     82/**
     83 * Configures the given stream writer object to use the given Guacamole stream
     84 * object for WebP output.
     85 *
     86 * @param writer
     87 *     The Guacamole WebP stream writer structure to configure.
     88 *
     89 * @param socket
     90 *     The Guacamole socket to use when sending blob instructions.
     91 *
     92 * @param stream
     93 *     The stream over which WebP-encoded blobs of image data should be sent.
     94 */
     95static void guac_webp_stream_writer_init(guac_webp_stream_writer* writer,
     96        guac_socket* socket, guac_stream* stream) {
     97
     98    writer->buffer_size = 0;
     99
    100    /* Store Guacamole-specific objects */
    101    writer->socket = socket;
    102    writer->stream = stream;
    103
    104}
    105
    106/**
    107 * WebP output function which appends the given WebP data to the internal
    108 * buffer of the Guacamole stream writer structure, automatically flushing the
    109 * writer as necessary.
    110 *
    111 * @param data
    112 *     The segment of data to write.
    113 *
    114 * @param data_size
    115 *     The size of segment of data to write.
    116 *
    117 * @param picture
    118 *     The WebP picture associated with this write operation. Provides access to
    119 *     picture->custom_ptr which contains the Guacamole stream writer structure.
    120 *
    121 * @return
    122 *     Non-zero if writing was successful, zero on failure.
    123 */
    124static int guac_webp_stream_write(const uint8_t* data, size_t data_size,
    125        const WebPPicture* picture) {
    126
    127    guac_webp_stream_writer* const writer =
    128        (guac_webp_stream_writer*) picture->custom_ptr;
    129    assert(writer != NULL);
    130
    131    const unsigned char* current = data;
    132    int length = data_size;
    133
    134    /* Append all data given */
    135    while (length > 0) {
    136
    137        /* Calculate space remaining */
    138        int remaining = sizeof(writer->buffer) - writer->buffer_size;
    139
    140        /* If no space remains, flush buffer to make room */
    141        if (remaining == 0) {
    142            guac_webp_flush_data(writer);
    143            remaining = sizeof(writer->buffer);
    144        }
    145
    146        /* Calculate size of next block of data to append */
    147        int block_size = remaining;
    148        if (block_size > length)
    149            block_size = length;
    150
    151        /* Append block */
    152        memcpy(writer->buffer + writer->buffer_size,
    153               current, block_size);
    154
    155        /* Next block */
    156        current += block_size;
    157        writer->buffer_size += block_size;
    158        length -= block_size;
    159
    160    }
    161
    162    return 1;
    163}
    164
    165int guac_webp_write(guac_socket* socket, guac_stream* stream,
    166        cairo_surface_t* surface, int quality, int lossless) {
    167
    168    guac_webp_stream_writer writer;
    169    WebPPicture picture;
    170    uint32_t* argb_output;
    171
    172    int x, y;
    173
    174    int width = cairo_image_surface_get_width(surface);
    175    int height = cairo_image_surface_get_height(surface);
    176    int stride = cairo_image_surface_get_stride(surface);
    177    cairo_format_t format = cairo_image_surface_get_format(surface);
    178    unsigned char* data = cairo_image_surface_get_data(surface);
    179
    180    if (format != CAIRO_FORMAT_RGB24 && format != CAIRO_FORMAT_ARGB32) {
    181        guac_error = GUAC_STATUS_INTERNAL_ERROR;
    182        guac_error_message = "Invalid Cairo image format. Unable to create WebP.";
    183        return -1;
    184    }
    185
    186    /* Flush pending operations to surface */
    187    cairo_surface_flush(surface);
    188
    189    /* Configure WebP compression bits */
    190    WebPConfig config;
    191    if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality))
    192        return -1;
    193
    194    /* Add additional tuning */
    195    config.lossless = lossless;
    196    config.quality = quality;
    197    config.thread_level = 1; /* Multi threaded */
    198    config.method = 2; /* Compression method (0=fast/larger, 6=slow/smaller) */
    199
    200    /* Validate configuration */
    201    WebPValidateConfig(&config);
    202
    203    /* Set up WebP picture */
    204    WebPPictureInit(&picture);
    205    picture.use_argb = 1;
    206    picture.width = width;
    207    picture.height = height;
    208
    209    /* Allocate and init writer */
    210    WebPPictureAlloc(&picture);
    211    picture.writer = guac_webp_stream_write;
    212    picture.custom_ptr = &writer;
    213    guac_webp_stream_writer_init(&writer, socket, stream);
    214
    215    /* Copy image data into WebP picture */
    216    argb_output = picture.argb;
    217    for (y = 0; y < height; y++) {
    218
    219        /* Get pixels at start of each row */
    220        uint32_t* src = (uint32_t*) data;
    221        uint32_t* dst = argb_output;
    222
    223        /* For each pixel in row */
    224        for (x = 0; x < width; x++) {
    225
    226            /* Pull pixel data, removing alpha channel if necessary */
    227            uint32_t src_pixel = *src;
    228            if (format != CAIRO_FORMAT_ARGB32)
    229                src_pixel |= 0xFF000000;
    230
    231            /* Store converted pixel data */
    232            *dst = src_pixel;
    233
    234            /* Next pixel */
    235            src++;
    236            dst++;
    237
    238        }
    239
    240        /* Next row */
    241        data += stride;
    242        argb_output += picture.argb_stride;
    243
    244    }
    245
    246    /* Encode image */
    247    WebPEncode(&config, &picture);
    248
    249    /* Free picture */
    250    WebPPictureFree(&picture);
    251
    252    /* Ensure all data is written */
    253    guac_webp_flush_data(&writer);
    254
    255    return 0;
    256
    257}
    258