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