1*ec63e07aSXin Li // Copyright 2020 Google LLC
2*ec63e07aSXin Li //
3*ec63e07aSXin Li // Licensed under the Apache License, Version 2.0 (the "License");
4*ec63e07aSXin Li // you may not use this file except in compliance with the License.
5*ec63e07aSXin Li // You may obtain a copy of the License at
6*ec63e07aSXin Li //
7*ec63e07aSXin Li // https://www.apache.org/licenses/LICENSE-2.0
8*ec63e07aSXin Li //
9*ec63e07aSXin Li // Unless required by applicable law or agreed to in writing, software
10*ec63e07aSXin Li // distributed under the License is distributed on an "AS IS" BASIS,
11*ec63e07aSXin Li // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*ec63e07aSXin Li // See the License for the specific language governing permissions and
13*ec63e07aSXin Li // limitations under the License.
14*ec63e07aSXin Li
15*ec63e07aSXin Li #include "guetzli_entry_points.h" // NOLINT(build/include)
16*ec63e07aSXin Li
17*ec63e07aSXin Li #include <sys/stat.h>
18*ec63e07aSXin Li
19*ec63e07aSXin Li #include <algorithm>
20*ec63e07aSXin Li #include <cstdio>
21*ec63e07aSXin Li #include <iostream>
22*ec63e07aSXin Li #include <string>
23*ec63e07aSXin Li #include <vector>
24*ec63e07aSXin Li
25*ec63e07aSXin Li #include "guetzli/jpeg_data_reader.h"
26*ec63e07aSXin Li #include "guetzli/quality.h"
27*ec63e07aSXin Li #include "png.h" // NOLINT(build/include)
28*ec63e07aSXin Li #include "absl/status/statusor.h"
29*ec63e07aSXin Li #include "sandboxed_api/util/fileops.h"
30*ec63e07aSXin Li
31*ec63e07aSXin Li namespace {
32*ec63e07aSXin Li
33*ec63e07aSXin Li constexpr int kBytesPerPixel = 350;
34*ec63e07aSXin Li constexpr int kLowestMemusageMB = 100;
35*ec63e07aSXin Li
36*ec63e07aSXin Li struct GuetzliInitData {
37*ec63e07aSXin Li std::string in_data;
38*ec63e07aSXin Li guetzli::Params params;
39*ec63e07aSXin Li guetzli::ProcessStats stats;
40*ec63e07aSXin Li };
41*ec63e07aSXin Li
42*ec63e07aSXin Li struct ImageData {
43*ec63e07aSXin Li int xsize;
44*ec63e07aSXin Li int ysize;
45*ec63e07aSXin Li std::vector<uint8_t> rgb;
46*ec63e07aSXin Li };
47*ec63e07aSXin Li
CreateLenValFromData(const void * data,size_t size)48*ec63e07aSXin Li sapi::LenValStruct CreateLenValFromData(const void* data, size_t size) {
49*ec63e07aSXin Li void* new_data = malloc(size);
50*ec63e07aSXin Li memcpy(new_data, data, size);
51*ec63e07aSXin Li return {size, new_data};
52*ec63e07aSXin Li }
53*ec63e07aSXin Li
ReadFromFd(int fd)54*ec63e07aSXin Li absl::StatusOr<std::string> ReadFromFd(int fd) {
55*ec63e07aSXin Li struct stat file_data;
56*ec63e07aSXin Li int status = fstat(fd, &file_data);
57*ec63e07aSXin Li
58*ec63e07aSXin Li if (status < 0) {
59*ec63e07aSXin Li return absl::FailedPreconditionError("Error reading input from fd");
60*ec63e07aSXin Li }
61*ec63e07aSXin Li
62*ec63e07aSXin Li std::string result;
63*ec63e07aSXin Li result.resize(file_data.st_size);
64*ec63e07aSXin Li status = read(fd, result.data(), result.size());
65*ec63e07aSXin Li
66*ec63e07aSXin Li if (status < 0) {
67*ec63e07aSXin Li return absl::FailedPreconditionError("Error reading input from fd");
68*ec63e07aSXin Li }
69*ec63e07aSXin Li
70*ec63e07aSXin Li return result;
71*ec63e07aSXin Li }
72*ec63e07aSXin Li
PrepareDataForProcessing(const ProcessingParams & processing_params)73*ec63e07aSXin Li absl::StatusOr<GuetzliInitData> PrepareDataForProcessing(
74*ec63e07aSXin Li const ProcessingParams& processing_params) {
75*ec63e07aSXin Li absl::StatusOr<std::string> input = ReadFromFd(processing_params.remote_fd);
76*ec63e07aSXin Li
77*ec63e07aSXin Li if (!input.ok()) {
78*ec63e07aSXin Li return input.status();
79*ec63e07aSXin Li }
80*ec63e07aSXin Li
81*ec63e07aSXin Li guetzli::Params guetzli_params;
82*ec63e07aSXin Li guetzli_params.butteraugli_target = static_cast<float>(
83*ec63e07aSXin Li guetzli::ButteraugliScoreForQuality(processing_params.quality));
84*ec63e07aSXin Li
85*ec63e07aSXin Li guetzli::ProcessStats stats;
86*ec63e07aSXin Li
87*ec63e07aSXin Li if (processing_params.verbose) {
88*ec63e07aSXin Li stats.debug_output_file = stderr;
89*ec63e07aSXin Li }
90*ec63e07aSXin Li
91*ec63e07aSXin Li return GuetzliInitData{std::move(input.value()), guetzli_params, stats};
92*ec63e07aSXin Li }
93*ec63e07aSXin Li
BlendOnBlack(const uint8_t val,const uint8_t alpha)94*ec63e07aSXin Li inline uint8_t BlendOnBlack(const uint8_t val, const uint8_t alpha) {
95*ec63e07aSXin Li return (static_cast<int>(val) * static_cast<int>(alpha) + 128) / 255;
96*ec63e07aSXin Li }
97*ec63e07aSXin Li
98*ec63e07aSXin Li // Modified version of ReadPNG from original guetzli.cc
ReadPNG(const std::string & data)99*ec63e07aSXin Li absl::StatusOr<ImageData> ReadPNG(const std::string& data) {
100*ec63e07aSXin Li std::vector<uint8_t> rgb;
101*ec63e07aSXin Li int xsize, ysize;
102*ec63e07aSXin Li png_structp png_ptr =
103*ec63e07aSXin Li png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
104*ec63e07aSXin Li if (!png_ptr) {
105*ec63e07aSXin Li return absl::FailedPreconditionError(
106*ec63e07aSXin Li "Error reading PNG data from input file");
107*ec63e07aSXin Li }
108*ec63e07aSXin Li
109*ec63e07aSXin Li png_infop info_ptr = png_create_info_struct(png_ptr);
110*ec63e07aSXin Li if (!info_ptr) {
111*ec63e07aSXin Li png_destroy_read_struct(&png_ptr, nullptr, nullptr);
112*ec63e07aSXin Li return absl::FailedPreconditionError(
113*ec63e07aSXin Li "Error reading PNG data from input file");
114*ec63e07aSXin Li }
115*ec63e07aSXin Li
116*ec63e07aSXin Li if (setjmp(png_jmpbuf(png_ptr)) != 0) {
117*ec63e07aSXin Li // Ok we are here because of the setjmp.
118*ec63e07aSXin Li png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
119*ec63e07aSXin Li return absl::FailedPreconditionError(
120*ec63e07aSXin Li "Error reading PNG data from input file");
121*ec63e07aSXin Li }
122*ec63e07aSXin Li
123*ec63e07aSXin Li std::istringstream memstream(data, std::ios::in | std::ios::binary);
124*ec63e07aSXin Li png_set_read_fn(
125*ec63e07aSXin Li png_ptr, static_cast<void*>(&memstream),
126*ec63e07aSXin Li [](png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead) {
127*ec63e07aSXin Li std::istringstream& memstream =
128*ec63e07aSXin Li *static_cast<std::istringstream*>(png_get_io_ptr(png_ptr));
129*ec63e07aSXin Li
130*ec63e07aSXin Li memstream.read(reinterpret_cast<char*>(outBytes), byteCountToRead);
131*ec63e07aSXin Li
132*ec63e07aSXin Li if (memstream.eof()) png_error(png_ptr, "unexpected end of data");
133*ec63e07aSXin Li if (memstream.fail()) png_error(png_ptr, "read from memory error");
134*ec63e07aSXin Li });
135*ec63e07aSXin Li
136*ec63e07aSXin Li // The png_transforms flags are as follows:
137*ec63e07aSXin Li // packing == convert 1,2,4 bit images,
138*ec63e07aSXin Li // strip == 16 -> 8 bits / channel,
139*ec63e07aSXin Li // shift == use sBIT dynamics, and
140*ec63e07aSXin Li // expand == palettes -> rgb, grayscale -> 8 bit images, tRNS -> alpha.
141*ec63e07aSXin Li const unsigned int png_transforms =
142*ec63e07aSXin Li PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_16;
143*ec63e07aSXin Li
144*ec63e07aSXin Li png_read_png(png_ptr, info_ptr, png_transforms, nullptr);
145*ec63e07aSXin Li
146*ec63e07aSXin Li png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);
147*ec63e07aSXin Li
148*ec63e07aSXin Li xsize = png_get_image_width(png_ptr, info_ptr);
149*ec63e07aSXin Li ysize = png_get_image_height(png_ptr, info_ptr);
150*ec63e07aSXin Li rgb.resize(3 * xsize * ysize);
151*ec63e07aSXin Li
152*ec63e07aSXin Li const int components = png_get_channels(png_ptr, info_ptr);
153*ec63e07aSXin Li switch (components) {
154*ec63e07aSXin Li case 1: {
155*ec63e07aSXin Li // GRAYSCALE
156*ec63e07aSXin Li for (int y = 0; y < ysize; ++y) {
157*ec63e07aSXin Li const uint8_t* row_in = row_pointers[y];
158*ec63e07aSXin Li uint8_t* row_out = &rgb[3 * y * xsize];
159*ec63e07aSXin Li for (int x = 0; x < xsize; ++x) {
160*ec63e07aSXin Li const uint8_t gray = row_in[x];
161*ec63e07aSXin Li row_out[3 * x + 0] = gray;
162*ec63e07aSXin Li row_out[3 * x + 1] = gray;
163*ec63e07aSXin Li row_out[3 * x + 2] = gray;
164*ec63e07aSXin Li }
165*ec63e07aSXin Li }
166*ec63e07aSXin Li break;
167*ec63e07aSXin Li }
168*ec63e07aSXin Li case 2: {
169*ec63e07aSXin Li // GRAYSCALE + ALPHA
170*ec63e07aSXin Li for (int y = 0; y < ysize; ++y) {
171*ec63e07aSXin Li const uint8_t* row_in = row_pointers[y];
172*ec63e07aSXin Li uint8_t* row_out = &rgb[3 * y * xsize];
173*ec63e07aSXin Li for (int x = 0; x < xsize; ++x) {
174*ec63e07aSXin Li const uint8_t gray = BlendOnBlack(row_in[2 * x], row_in[2 * x + 1]);
175*ec63e07aSXin Li row_out[3 * x + 0] = gray;
176*ec63e07aSXin Li row_out[3 * x + 1] = gray;
177*ec63e07aSXin Li row_out[3 * x + 2] = gray;
178*ec63e07aSXin Li }
179*ec63e07aSXin Li }
180*ec63e07aSXin Li break;
181*ec63e07aSXin Li }
182*ec63e07aSXin Li case 3: {
183*ec63e07aSXin Li // RGB
184*ec63e07aSXin Li for (int y = 0; y < ysize; ++y) {
185*ec63e07aSXin Li const uint8_t* row_in = row_pointers[y];
186*ec63e07aSXin Li uint8_t* row_out = &rgb[3 * y * xsize];
187*ec63e07aSXin Li memcpy(row_out, row_in, 3 * xsize);
188*ec63e07aSXin Li }
189*ec63e07aSXin Li break;
190*ec63e07aSXin Li }
191*ec63e07aSXin Li case 4: {
192*ec63e07aSXin Li // RGBA
193*ec63e07aSXin Li for (int y = 0; y < ysize; ++y) {
194*ec63e07aSXin Li const uint8_t* row_in = row_pointers[y];
195*ec63e07aSXin Li uint8_t* row_out = &rgb[3 * y * xsize];
196*ec63e07aSXin Li for (int x = 0; x < xsize; ++x) {
197*ec63e07aSXin Li const uint8_t alpha = row_in[4 * x + 3];
198*ec63e07aSXin Li row_out[3 * x + 0] = BlendOnBlack(row_in[4 * x + 0], alpha);
199*ec63e07aSXin Li row_out[3 * x + 1] = BlendOnBlack(row_in[4 * x + 1], alpha);
200*ec63e07aSXin Li row_out[3 * x + 2] = BlendOnBlack(row_in[4 * x + 2], alpha);
201*ec63e07aSXin Li }
202*ec63e07aSXin Li }
203*ec63e07aSXin Li break;
204*ec63e07aSXin Li }
205*ec63e07aSXin Li default:
206*ec63e07aSXin Li png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
207*ec63e07aSXin Li return absl::FailedPreconditionError(
208*ec63e07aSXin Li "Error reading PNG data from input file");
209*ec63e07aSXin Li }
210*ec63e07aSXin Li png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
211*ec63e07aSXin Li
212*ec63e07aSXin Li return ImageData{xsize, ysize, std::move(rgb)};
213*ec63e07aSXin Li }
214*ec63e07aSXin Li
CheckMemoryLimitExceeded(int memlimit_mb,int xsize,int ysize)215*ec63e07aSXin Li bool CheckMemoryLimitExceeded(int memlimit_mb, int xsize, int ysize) {
216*ec63e07aSXin Li double pixels = static_cast<double>(xsize) * ysize;
217*ec63e07aSXin Li return memlimit_mb != -1 &&
218*ec63e07aSXin Li (pixels * kBytesPerPixel / (1 << 20) > memlimit_mb ||
219*ec63e07aSXin Li memlimit_mb < kLowestMemusageMB);
220*ec63e07aSXin Li }
221*ec63e07aSXin Li
222*ec63e07aSXin Li } // namespace
223*ec63e07aSXin Li
ProcessJpeg(const ProcessingParams * processing_params,sapi::LenValStruct * output)224*ec63e07aSXin Li extern "C" bool ProcessJpeg(const ProcessingParams* processing_params,
225*ec63e07aSXin Li sapi::LenValStruct* output) {
226*ec63e07aSXin Li auto processing_data = PrepareDataForProcessing(*processing_params);
227*ec63e07aSXin Li
228*ec63e07aSXin Li if (!processing_data.ok()) {
229*ec63e07aSXin Li std::cerr << processing_data.status().ToString() << std::endl;
230*ec63e07aSXin Li return false;
231*ec63e07aSXin Li }
232*ec63e07aSXin Li
233*ec63e07aSXin Li guetzli::JPEGData jpg_header;
234*ec63e07aSXin Li if (!guetzli::ReadJpeg(processing_data->in_data, guetzli::JPEG_READ_HEADER,
235*ec63e07aSXin Li &jpg_header)) {
236*ec63e07aSXin Li std::cerr << "Error reading JPG data from input file" << std::endl;
237*ec63e07aSXin Li return false;
238*ec63e07aSXin Li }
239*ec63e07aSXin Li
240*ec63e07aSXin Li if (CheckMemoryLimitExceeded(processing_params->memlimit_mb, jpg_header.width,
241*ec63e07aSXin Li jpg_header.height)) {
242*ec63e07aSXin Li std::cerr << "Memory limit would be exceeded" << std::endl;
243*ec63e07aSXin Li return false;
244*ec63e07aSXin Li }
245*ec63e07aSXin Li
246*ec63e07aSXin Li std::string out_data;
247*ec63e07aSXin Li if (!guetzli::Process(processing_data->params, &processing_data->stats,
248*ec63e07aSXin Li processing_data->in_data, &out_data)) {
249*ec63e07aSXin Li std::cerr << "Guezli processing failed" << std::endl;
250*ec63e07aSXin Li return false;
251*ec63e07aSXin Li }
252*ec63e07aSXin Li
253*ec63e07aSXin Li *output = CreateLenValFromData(out_data.data(), out_data.size());
254*ec63e07aSXin Li return true;
255*ec63e07aSXin Li }
256*ec63e07aSXin Li
ProcessRgb(const ProcessingParams * processing_params,sapi::LenValStruct * output)257*ec63e07aSXin Li extern "C" bool ProcessRgb(const ProcessingParams* processing_params,
258*ec63e07aSXin Li sapi::LenValStruct* output) {
259*ec63e07aSXin Li auto processing_data = PrepareDataForProcessing(*processing_params);
260*ec63e07aSXin Li
261*ec63e07aSXin Li if (!processing_data.ok()) {
262*ec63e07aSXin Li std::cerr << processing_data.status().ToString() << std::endl;
263*ec63e07aSXin Li return false;
264*ec63e07aSXin Li }
265*ec63e07aSXin Li
266*ec63e07aSXin Li auto png_data = ReadPNG(processing_data->in_data);
267*ec63e07aSXin Li if (!png_data.ok()) {
268*ec63e07aSXin Li std::cerr << "Error reading PNG data from input file" << std::endl;
269*ec63e07aSXin Li return false;
270*ec63e07aSXin Li }
271*ec63e07aSXin Li
272*ec63e07aSXin Li if (CheckMemoryLimitExceeded(processing_params->memlimit_mb, png_data->xsize,
273*ec63e07aSXin Li png_data->ysize)) {
274*ec63e07aSXin Li std::cerr << "Memory limit would be exceeded" << std::endl;
275*ec63e07aSXin Li return false;
276*ec63e07aSXin Li }
277*ec63e07aSXin Li
278*ec63e07aSXin Li std::string out_data;
279*ec63e07aSXin Li if (!guetzli::Process(processing_data->params, &processing_data->stats,
280*ec63e07aSXin Li png_data->rgb, png_data->xsize, png_data->ysize,
281*ec63e07aSXin Li &out_data)) {
282*ec63e07aSXin Li std::cerr << "Guetzli processing failed" << std::endl;
283*ec63e07aSXin Li return false;
284*ec63e07aSXin Li }
285*ec63e07aSXin Li
286*ec63e07aSXin Li *output = CreateLenValFromData(out_data.data(), out_data.size());
287*ec63e07aSXin Li return true;
288*ec63e07aSXin Li }
289*ec63e07aSXin Li
WriteDataToFd(int fd,sapi::LenValStruct * data)290*ec63e07aSXin Li extern "C" bool WriteDataToFd(int fd, sapi::LenValStruct* data) {
291*ec63e07aSXin Li return sandbox2::file_util::fileops::WriteToFD(
292*ec63e07aSXin Li fd, static_cast<const char*>(data->data), data->size);
293*ec63e07aSXin Li }
294