| /* |
| * Copyright (C) 2017 Igalia S.L. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "PlatformImage.h" |
| |
| #include <cairo.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #ifdef _WIN32 |
| #define FORMAT_SIZE_T "Iu" |
| #else |
| #define FORMAT_SIZE_T "zu" |
| #endif |
| |
| namespace ImageDiff { |
| |
| std::unique_ptr<PlatformImage> PlatformImage::createFromStdin(size_t imageSize) |
| { |
| struct ReadContext { |
| char buffer[2048]; |
| unsigned long incomingBytes; |
| unsigned long readBytes; |
| } context { { }, imageSize, 0 }; |
| |
| cairo_surface_t* surface = cairo_image_surface_create_from_png_stream( |
| [](void* closure, unsigned char* data, unsigned length) -> cairo_status_t { |
| auto& context = *static_cast<ReadContext*>(closure); |
| context.readBytes += length; |
| |
| size_t readBytes = fread(data, 1, length, stdin); |
| return readBytes == length ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_READ_ERROR; |
| }, &context); |
| |
| if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) { |
| cairo_surface_destroy(surface); |
| return nullptr; |
| } |
| |
| return std::make_unique<PlatformImage>(surface); |
| } |
| |
| std::unique_ptr<PlatformImage> PlatformImage::createFromDiffData(void* data, size_t width, size_t height) |
| { |
| cairo_surface_t* surface = cairo_image_surface_create_for_data(reinterpret_cast<unsigned char*>(data), CAIRO_FORMAT_A8, |
| width, height, cairo_format_stride_for_width(CAIRO_FORMAT_A8, width)); |
| static cairo_user_data_key_t imageDataKey; |
| cairo_surface_set_user_data(surface, &imageDataKey, data, [](void* data) { free(data); }); |
| return std::make_unique<PlatformImage>(surface); |
| } |
| |
| PlatformImage::PlatformImage(cairo_surface_t* surface) |
| : m_image(surface) |
| { |
| } |
| |
| PlatformImage::~PlatformImage() |
| { |
| cairo_surface_destroy(m_image); |
| } |
| |
| size_t PlatformImage::width() const |
| { |
| return cairo_image_surface_get_width(m_image); |
| } |
| |
| size_t PlatformImage::height() const |
| { |
| return cairo_image_surface_get_height(m_image); |
| } |
| |
| size_t PlatformImage::rowBytes() const |
| { |
| return cairo_image_surface_get_stride(m_image); |
| } |
| |
| bool PlatformImage::hasAlpha() const |
| { |
| // What matters here is whether the image data has an alpha channel. In cairo, both |
| // CAIRO_FORMAT_ARGB32 and CAIRO_FORMAT_RGB24 have an alpha channel even if it's |
| // always 0 in the CAIRO_FORMAT_RGB24 case. |
| return cairo_image_surface_get_format(m_image) == CAIRO_FORMAT_ARGB32 || cairo_image_surface_get_format(m_image) == CAIRO_FORMAT_RGB24; |
| } |
| |
| unsigned char* PlatformImage::pixels() const |
| { |
| return cairo_image_surface_get_data(m_image); |
| } |
| |
| void PlatformImage::writeAsPNGToStdout() |
| { |
| struct WriteContext { |
| unsigned long writtenBytes { 0 }; |
| } context; |
| |
| // First we sum up the bytes that are to be written. |
| cairo_surface_write_to_png_stream(m_image, |
| [](void* closure, const unsigned char*, unsigned length) -> cairo_status_t { |
| auto& context = *static_cast<WriteContext*>(closure); |
| context.writtenBytes += length; |
| return CAIRO_STATUS_SUCCESS; |
| }, &context); |
| fprintf(stdout, "Content-Length: %" FORMAT_SIZE_T "\n", context.writtenBytes); |
| cairo_surface_write_to_png_stream(m_image, |
| [](void*, const unsigned char* data, unsigned length) -> cairo_status_t { |
| size_t writtenBytes = fwrite(data, 1, length, stdout); |
| return writtenBytes == length ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR; |
| }, nullptr); |
| } |
| |
| } // namespace ImageDiff |