blob: b8dc8f0a20f9e8f90ac1ad59dd14602c7734d571 [file] [log] [blame]
/*
* 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