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