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 | |
32 | namespace ImageDiff { |
33 | |
34 | std::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 | |
50 | std::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 | |
59 | PlatformImage::PlatformImage(cairo_surface_t* surface) |
60 | : m_image(surface) |
61 | { |
62 | } |
63 | |
64 | PlatformImage::~PlatformImage() |
65 | { |
66 | cairo_surface_destroy(m_image); |
67 | } |
68 | |
69 | size_t PlatformImage::width() const |
70 | { |
71 | return cairo_image_surface_get_width(m_image); |
72 | } |
73 | |
74 | size_t PlatformImage::height() const |
75 | { |
76 | return cairo_image_surface_get_height(m_image); |
77 | } |
78 | |
79 | size_t PlatformImage::rowBytes() const |
80 | { |
81 | return cairo_image_surface_get_stride(m_image); |
82 | } |
83 | |
84 | bool 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 | |
92 | unsigned char* PlatformImage::pixels() const |
93 | { |
94 | return cairo_image_surface_get_data(m_image); |
95 | } |
96 | |
97 | void 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 | |