1/*
2 * Copyright (C) 2017 Igalia S.L.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "PlatformImage.h"
27
28#include <cairo.h>
29#include <stdio.h>
30#include <stdlib.h>
31
32namespace ImageDiff {
33
34std::unique_ptr<PlatformImage> PlatformImage::createFromStdin(size_t)
35{
36 cairo_surface_t* surface = cairo_image_surface_create_from_png_stream(
37 [](void*, unsigned char* data, unsigned length) -> cairo_status_t {
38 size_t readBytes = fread(data, 1, length, stdin);
39 return readBytes == length ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_READ_ERROR;
40 }, nullptr);
41
42 if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
43 cairo_surface_destroy(surface);
44 return nullptr;
45 }
46
47 return std::make_unique<PlatformImage>(surface);
48}
49
50std::unique_ptr<PlatformImage> PlatformImage::createFromDiffData(void* data, size_t width, size_t height)
51{
52 cairo_surface_t* surface = cairo_image_surface_create_for_data(reinterpret_cast<unsigned char*>(data), CAIRO_FORMAT_A8,
53 width, height, cairo_format_stride_for_width(CAIRO_FORMAT_A8, width));
54 static cairo_user_data_key_t imageDataKey;
55 cairo_surface_set_user_data(surface, &imageDataKey, data, [](void* data) { free(data); });
56 return std::make_unique<PlatformImage>(surface);
57}
58
59PlatformImage::PlatformImage(cairo_surface_t* surface)
60 : m_image(surface)
61{
62}
63
64PlatformImage::~PlatformImage()
65{
66 cairo_surface_destroy(m_image);
67}
68
69size_t PlatformImage::width() const
70{
71 return cairo_image_surface_get_width(m_image);
72}
73
74size_t PlatformImage::height() const
75{
76 return cairo_image_surface_get_height(m_image);
77}
78
79size_t PlatformImage::rowBytes() const
80{
81 return cairo_image_surface_get_stride(m_image);
82}
83
84bool PlatformImage::hasAlpha() const
85{
86 // What matters here is whether the image data has an alpha channel. In cairo, both
87 // CAIRO_FORMAT_ARGB32 and CAIRO_FORMAT_RGB24 have an alpha channel even if it's
88 // always 0 in the CAIRO_FORMAT_RGB24 case.
89 return cairo_image_surface_get_format(m_image) == CAIRO_FORMAT_ARGB32 || cairo_image_surface_get_format(m_image) == CAIRO_FORMAT_RGB24;
90}
91
92unsigned char* PlatformImage::pixels() const
93{
94 return cairo_image_surface_get_data(m_image);
95}
96
97void PlatformImage::writeAsPNGToStdout()
98{
99 struct WriteContext {
100 unsigned long writtenBytes { 0 };
101 } context;
102
103 // First we sum up the bytes that are to be written.
104 cairo_surface_write_to_png_stream(m_image,
105 [](void* closure, const unsigned char*, unsigned length) -> cairo_status_t {
106 auto& context = *static_cast<WriteContext*>(closure);
107 context.writtenBytes += length;
108 return CAIRO_STATUS_SUCCESS;
109 }, &context);
110 fprintf(stdout, "Content-Length: %lu\n", context.writtenBytes);
111 cairo_surface_write_to_png_stream(m_image,
112 [](void*, const unsigned char* data, unsigned length) -> cairo_status_t {
113 size_t writtenBytes = fwrite(data, 1, length, stdout);
114 return writtenBytes == length ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR;
115 }, nullptr);
116}
117
118} // namespace ImageDiff
119