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 <array>
16*ec63e07aSXin Li #include <cstdint>
17*ec63e07aSXin Li #include <cstring>
18*ec63e07aSXin Li #include <iostream>
19*ec63e07aSXin Li #include <vector>
20*ec63e07aSXin Li
21*ec63e07aSXin Li #include "../sandboxed.h" // NOLINT(build/include)
22*ec63e07aSXin Li #include "absl/flags/parse.h"
23*ec63e07aSXin Li #include "absl/log/globals.h"
24*ec63e07aSXin Li #include "absl/log/initialize.h"
25*ec63e07aSXin Li #include "sandboxed_api/util/fileops.h"
26*ec63e07aSXin Li #include "sandboxed_api/util/path.h"
27*ec63e07aSXin Li #include "sandboxed_api/vars.h"
28*ec63e07aSXin Li #include "tiffio.h" // NOLINT(build/include)
29*ec63e07aSXin Li
30*ec63e07aSXin Li // sapi functions:
31*ec63e07aSXin Li // TIFFTileSize
32*ec63e07aSXin Li // TIFFOpen
33*ec63e07aSXin Li // TIFFReadEncodedTile
34*ec63e07aSXin Li // TIFFSetField
35*ec63e07aSXin Li // TIFFClose
36*ec63e07aSXin Li // TIFFReadRGBATile
37*ec63e07aSXin Li // TIFFGetField
38*ec63e07aSXin Li
39*ec63e07aSXin Li namespace {
40*ec63e07aSXin Li
41*ec63e07aSXin Li constexpr std::array<uint8_t, 6> kCluster0 = {0, 0, 2, 0, 138, 139};
42*ec63e07aSXin Li constexpr std::array<uint8_t, 6> kCluster64 = {0, 0, 9, 6, 134, 119};
43*ec63e07aSXin Li constexpr std::array<uint8_t, 6> kCluster128 = {44, 40, 63, 59, 230, 95};
44*ec63e07aSXin Li
45*ec63e07aSXin Li template <typename ArrayT>
CheckCluster(int cluster,const sapi::v::Array<uint8_t> & buffer,const ArrayT & expected_cluster)46*ec63e07aSXin Li int CheckCluster(int cluster, const sapi::v::Array<uint8_t>& buffer,
47*ec63e07aSXin Li const ArrayT& expected_cluster) {
48*ec63e07aSXin Li uint8_t* target = buffer.GetData() + cluster * 6;
49*ec63e07aSXin Li
50*ec63e07aSXin Li if (!std::memcmp(target, expected_cluster.data(), 6)) {
51*ec63e07aSXin Li return 0;
52*ec63e07aSXin Li }
53*ec63e07aSXin Li
54*ec63e07aSXin Li std::cerr << "Cluster " << cluster << " did not match expected results.\n"
55*ec63e07aSXin Li << "Expect: " << expected_cluster[0] << "\t" << expected_cluster[1]
56*ec63e07aSXin Li << "\t" << expected_cluster[4] << "\t" << expected_cluster[5]
57*ec63e07aSXin Li << "\t" << expected_cluster[2] << "\t" << expected_cluster[3]
58*ec63e07aSXin Li << "\n"
59*ec63e07aSXin Li << "Got: " << target[0] << "\t" << target[1] << "\t" << target[4]
60*ec63e07aSXin Li << "\t" << target[5] << "\t" << target[2] << "\t" << target[3]
61*ec63e07aSXin Li << '\n';
62*ec63e07aSXin Li
63*ec63e07aSXin Li return 1;
64*ec63e07aSXin Li }
65*ec63e07aSXin Li
CheckRgbPixel(int pixel,int min_red,int max_red,int min_green,int max_green,int min_blue,int max_blue,const sapi::v::Array<uint8_t> & buffer)66*ec63e07aSXin Li int CheckRgbPixel(int pixel, int min_red, int max_red, int min_green,
67*ec63e07aSXin Li int max_green, int min_blue, int max_blue,
68*ec63e07aSXin Li const sapi::v::Array<uint8_t>& buffer) {
69*ec63e07aSXin Li uint8_t* rgb = buffer.GetData() + 3 * pixel;
70*ec63e07aSXin Li
71*ec63e07aSXin Li if (rgb[0] >= min_red && rgb[0] <= max_red && rgb[1] >= min_green &&
72*ec63e07aSXin Li rgb[1] <= max_green && rgb[2] >= min_blue && rgb[2] <= max_blue) {
73*ec63e07aSXin Li return 0;
74*ec63e07aSXin Li }
75*ec63e07aSXin Li
76*ec63e07aSXin Li std::cerr << "Pixel " << pixel << " did not match expected results.\n"
77*ec63e07aSXin Li << "Got R=" << rgb[0] << " (expected " << min_red << ".." << max_red
78*ec63e07aSXin Li << "), G=" << rgb[1] << " (expected " << min_green << ".."
79*ec63e07aSXin Li << max_green << "), B=" << rgb[2] << " (expected " << min_blue
80*ec63e07aSXin Li << ".." << max_blue << ")\n";
81*ec63e07aSXin Li return 1;
82*ec63e07aSXin Li }
83*ec63e07aSXin Li
CheckRgbaPixel(int pixel,int min_red,int max_red,int min_green,int max_green,int min_blue,int max_blue,int min_alpha,int max_alpha,const sapi::v::Array<uint32_t> & buffer)84*ec63e07aSXin Li int CheckRgbaPixel(int pixel, int min_red, int max_red, int min_green,
85*ec63e07aSXin Li int max_green, int min_blue, int max_blue, int min_alpha,
86*ec63e07aSXin Li int max_alpha, const sapi::v::Array<uint32_t>& buffer) {
87*ec63e07aSXin Li // RGBA images are upside down - adjust for normal ordering
88*ec63e07aSXin Li int adjusted_pixel = pixel % 128 + (127 - (pixel / 128)) * 128;
89*ec63e07aSXin Li uint32_t rgba = buffer[adjusted_pixel];
90*ec63e07aSXin Li
91*ec63e07aSXin Li if (TIFFGetR(rgba) >= static_cast<uint32_t>(min_red) &&
92*ec63e07aSXin Li TIFFGetR(rgba) <= static_cast<uint32_t>(max_red) &&
93*ec63e07aSXin Li TIFFGetG(rgba) >= static_cast<uint32_t>(min_green) &&
94*ec63e07aSXin Li TIFFGetG(rgba) <= static_cast<uint32_t>(max_green) &&
95*ec63e07aSXin Li TIFFGetB(rgba) >= static_cast<uint32_t>(min_blue) &&
96*ec63e07aSXin Li TIFFGetB(rgba) <= static_cast<uint32_t>(max_blue) &&
97*ec63e07aSXin Li TIFFGetA(rgba) >= static_cast<uint32_t>(min_alpha) &&
98*ec63e07aSXin Li TIFFGetA(rgba) <= static_cast<uint32_t>(max_alpha)) {
99*ec63e07aSXin Li return 0;
100*ec63e07aSXin Li }
101*ec63e07aSXin Li
102*ec63e07aSXin Li std::cerr << "Pixel " << pixel << " did not match expected results.\n"
103*ec63e07aSXin Li << "Got R=" << TIFFGetR(rgba) << " (expected " << min_red << ".."
104*ec63e07aSXin Li << max_red << "), G=" << TIFFGetG(rgba) << " (expected "
105*ec63e07aSXin Li << min_green << ".." << max_green << "), B=" << TIFFGetB(rgba)
106*ec63e07aSXin Li << " (expected " << min_blue << ".." << max_blue
107*ec63e07aSXin Li << "), A=" << TIFFGetA(rgba) << " (expected " << min_alpha << ".."
108*ec63e07aSXin Li << max_alpha << ")\n";
109*ec63e07aSXin Li return 1;
110*ec63e07aSXin Li }
GetFilePath(const std::string & dir,const std::string & filename)111*ec63e07aSXin Li std::string GetFilePath(const std::string& dir, const std::string& filename) {
112*ec63e07aSXin Li return sapi::file::JoinPath(dir, "test", "images", filename);
113*ec63e07aSXin Li }
114*ec63e07aSXin Li
GetCWD()115*ec63e07aSXin Li std::string GetCWD() {
116*ec63e07aSXin Li char cwd[PATH_MAX];
117*ec63e07aSXin Li getcwd(cwd, sizeof(cwd));
118*ec63e07aSXin Li return cwd;
119*ec63e07aSXin Li }
120*ec63e07aSXin Li
GetFilePath(const std::string filename)121*ec63e07aSXin Li std::string GetFilePath(const std::string filename) {
122*ec63e07aSXin Li std::string cwd = GetCWD();
123*ec63e07aSXin Li auto find = cwd.rfind("build");
124*ec63e07aSXin Li
125*ec63e07aSXin Li std::string project_path;
126*ec63e07aSXin Li if (find == std::string::npos) {
127*ec63e07aSXin Li std::cerr << "Something went wrong: CWD don't contain build dir. "
128*ec63e07aSXin Li << "Please run tests from build dir or send project dir as a "
129*ec63e07aSXin Li << "parameter: ./sandboxed /absolute/path/to/project/dir \n";
130*ec63e07aSXin Li project_path = cwd;
131*ec63e07aSXin Li } else {
132*ec63e07aSXin Li project_path = cwd.substr(0, find);
133*ec63e07aSXin Li }
134*ec63e07aSXin Li
135*ec63e07aSXin Li return sapi::file::JoinPath(project_path, "test", "images", filename);
136*ec63e07aSXin Li }
137*ec63e07aSXin Li
138*ec63e07aSXin Li } // namespace
139*ec63e07aSXin Li
main(int argc,char * argv[])140*ec63e07aSXin Li int main(int argc, char* argv[]) {
141*ec63e07aSXin Li absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo);
142*ec63e07aSXin Li absl::ParseCommandLine(argc, argv);
143*ec63e07aSXin Li absl::InitializeLog();
144*ec63e07aSXin Li
145*ec63e07aSXin Li std::string srcfile;
146*ec63e07aSXin Li // "test/images/quad-tile.jpg.tiff"
147*ec63e07aSXin Li std::string srcfilerel = "quad-tile.jpg.tiff";
148*ec63e07aSXin Li
149*ec63e07aSXin Li if (argc < 2) {
150*ec63e07aSXin Li srcfile = GetFilePath(srcfilerel);
151*ec63e07aSXin Li } else {
152*ec63e07aSXin Li srcfile = GetFilePath(argv[1], srcfilerel);
153*ec63e07aSXin Li }
154*ec63e07aSXin Li
155*ec63e07aSXin Li // without addDir to sandbox. to add dir use
156*ec63e07aSXin Li // sandbox(absolute_path_to_dir, srcfile) or
157*ec63e07aSXin Li // sandbox(absolute_path_to_dir). file and dir should be exists.
158*ec63e07aSXin Li // srcfile must also be absolute_path_to_file
159*ec63e07aSXin Li TiffSapiSandbox sandbox("", srcfile);
160*ec63e07aSXin Li
161*ec63e07aSXin Li // initialize sapi vars after constructing TiffSapiSandbox
162*ec63e07aSXin Li sapi::v::UShort h, v;
163*ec63e07aSXin Li absl::StatusOr<TIFF*> status_or_tif;
164*ec63e07aSXin Li absl::StatusOr<int> status_or_int;
165*ec63e07aSXin Li absl::StatusOr<tmsize_t> status_or_long;
166*ec63e07aSXin Li
167*ec63e07aSXin Li auto status = sandbox.Init();
168*ec63e07aSXin Li if (!status.ok()) {
169*ec63e07aSXin Li fprintf(stderr, "Couldn't initialize Sandboxed API: %s\n", status);
170*ec63e07aSXin Li }
171*ec63e07aSXin Li
172*ec63e07aSXin Li TiffApi api(&sandbox);
173*ec63e07aSXin Li sapi::v::ConstCStr srcfile_var(srcfile.c_str());
174*ec63e07aSXin Li sapi::v::ConstCStr r_var("r");
175*ec63e07aSXin Li
176*ec63e07aSXin Li status_or_tif = api.TIFFOpen(srcfile_var.PtrBefore(), r_var.PtrBefore());
177*ec63e07aSXin Li if (!status_or_tif.ok()) {
178*ec63e07aSXin Li std::cerr << "Could not open " << srcfile << ", TIFFError\n";
179*ec63e07aSXin Li return 1;
180*ec63e07aSXin Li }
181*ec63e07aSXin Li
182*ec63e07aSXin Li sapi::v::RemotePtr tif(status_or_tif.value());
183*ec63e07aSXin Li if (!tif.GetValue()) {
184*ec63e07aSXin Li // tif is NULL
185*ec63e07aSXin Li std::cerr << "Could not open " << srcfile << "\n";
186*ec63e07aSXin Li return 1;
187*ec63e07aSXin Li }
188*ec63e07aSXin Li
189*ec63e07aSXin Li status_or_int = api.TIFFGetField2(&tif, TIFFTAG_YCBCRSUBSAMPLING, h.PtrBoth(),
190*ec63e07aSXin Li v.PtrBoth());
191*ec63e07aSXin Li if (!status_or_int.ok() || status_or_int.value() == 0 || h.GetValue() != 2 ||
192*ec63e07aSXin Li v.GetValue() != 2) {
193*ec63e07aSXin Li std::cerr << "Could not retrieve subsampling tag\n";
194*ec63e07aSXin Li return 1;
195*ec63e07aSXin Li }
196*ec63e07aSXin Li
197*ec63e07aSXin Li status_or_long = api.TIFFTileSize(&tif);
198*ec63e07aSXin Li if (!status_or_int.ok() || status_or_long.value() != 24576) {
199*ec63e07aSXin Li std::cerr << "tiles are " << status_or_long.value() << " bytes\n";
200*ec63e07aSXin Li exit(1);
201*ec63e07aSXin Li }
202*ec63e07aSXin Li tsize_t sz = status_or_long.value();
203*ec63e07aSXin Li
204*ec63e07aSXin Li sapi::v::Array<uint8_t> buffer_(sz);
205*ec63e07aSXin Li status_or_long = api.TIFFReadEncodedTile(&tif, 9, buffer_.PtrBoth(), sz);
206*ec63e07aSXin Li if (!status_or_long.ok() || status_or_long.value() != sz) {
207*ec63e07aSXin Li std::cerr << "Did not get expected result code from"
208*ec63e07aSXin Li << "TIFFReadEncodedTile(): (" << status_or_long.value()
209*ec63e07aSXin Li << " instead of " << sz << ")\n";
210*ec63e07aSXin Li return 1;
211*ec63e07aSXin Li }
212*ec63e07aSXin Li
213*ec63e07aSXin Li if (CheckCluster(0, buffer_, kCluster0) ||
214*ec63e07aSXin Li CheckCluster(64, buffer_, kCluster64) ||
215*ec63e07aSXin Li CheckCluster(128, buffer_, kCluster128)) {
216*ec63e07aSXin Li return 1;
217*ec63e07aSXin Li }
218*ec63e07aSXin Li
219*ec63e07aSXin Li status_or_int =
220*ec63e07aSXin Li api.TIFFSetFieldU1(&tif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
221*ec63e07aSXin Li if (!status_or_int.ok() || !status_or_int.value()) {
222*ec63e07aSXin Li std::cerr << "TIFFSetFieldU1 not available\n";
223*ec63e07aSXin Li }
224*ec63e07aSXin Li
225*ec63e07aSXin Li status_or_long = api.TIFFTileSize(&tif);
226*ec63e07aSXin Li if (!status_or_long.ok() || status_or_long.value() != 128 * 128 * 3) {
227*ec63e07aSXin Li std::cerr << "tiles are " << status_or_long.value() << " bytes\n";
228*ec63e07aSXin Li return 1;
229*ec63e07aSXin Li }
230*ec63e07aSXin Li sz = status_or_long.value();
231*ec63e07aSXin Li
232*ec63e07aSXin Li sapi::v::Array<uint8_t> buffer2_(sz);
233*ec63e07aSXin Li
234*ec63e07aSXin Li status_or_long = api.TIFFReadEncodedTile(&tif, 9, buffer2_.PtrBoth(), sz);
235*ec63e07aSXin Li if (!status_or_long.ok() || status_or_long.value() != sz) {
236*ec63e07aSXin Li std::cerr << "Did not get expected result code from "
237*ec63e07aSXin Li << "TIFFReadEncodedTile(): (" << status_or_long.value()
238*ec63e07aSXin Li << " instead of " << sz << ")\n";
239*ec63e07aSXin Li return 1;
240*ec63e07aSXin Li }
241*ec63e07aSXin Li
242*ec63e07aSXin Li uint32_t pixel_status = 0;
243*ec63e07aSXin Li pixel_status |= CheckRgbPixel(0, 15, 18, 0, 0, 18, 41, buffer2_);
244*ec63e07aSXin Li pixel_status |= CheckRgbPixel(64, 0, 0, 0, 0, 0, 2, buffer2_);
245*ec63e07aSXin Li pixel_status |= CheckRgbPixel(512, 5, 6, 34, 36, 182, 196, buffer2_);
246*ec63e07aSXin Li
247*ec63e07aSXin Li if (!api.TIFFClose(&tif).ok()) {
248*ec63e07aSXin Li std::cerr << "TIFFClose error\n";
249*ec63e07aSXin Li }
250*ec63e07aSXin Li
251*ec63e07aSXin Li status_or_tif = api.TIFFOpen(srcfile_var.PtrBefore(), r_var.PtrBefore());
252*ec63e07aSXin Li if (!status_or_tif.ok()) {
253*ec63e07aSXin Li std::cerr << "Could not reopen " << srcfile << "\n";
254*ec63e07aSXin Li return 1;
255*ec63e07aSXin Li }
256*ec63e07aSXin Li
257*ec63e07aSXin Li sapi::v::RemotePtr tif2(status_or_tif.value());
258*ec63e07aSXin Li if (!tif2.GetValue()) { // tif is NULL
259*ec63e07aSXin Li std::cerr << "Could not reopen " << srcfile << "\n";
260*ec63e07aSXin Li return 1;
261*ec63e07aSXin Li }
262*ec63e07aSXin Li
263*ec63e07aSXin Li sapi::v::Array<uint32> rgba_buffer_(128 * 128);
264*ec63e07aSXin Li
265*ec63e07aSXin Li status_or_int =
266*ec63e07aSXin Li api.TIFFReadRGBATile(&tif2, 1 * 128, 2 * 128, rgba_buffer_.PtrBoth());
267*ec63e07aSXin Li if (!status_or_int.ok() || !status_or_int.value()) {
268*ec63e07aSXin Li fprintf(stderr, "TIFFReadRGBATile() returned failure code.\n");
269*ec63e07aSXin Li return 1;
270*ec63e07aSXin Li }
271*ec63e07aSXin Li
272*ec63e07aSXin Li pixel_status |=
273*ec63e07aSXin Li CheckRgbaPixel(0, 15, 18, 0, 0, 18, 41, 255, 255, rgba_buffer_);
274*ec63e07aSXin Li pixel_status |= CheckRgbaPixel(64, 0, 0, 0, 0, 0, 2, 255, 255, rgba_buffer_);
275*ec63e07aSXin Li pixel_status |=
276*ec63e07aSXin Li CheckRgbaPixel(512, 5, 6, 34, 36, 182, 196, 255, 255, rgba_buffer_);
277*ec63e07aSXin Li
278*ec63e07aSXin Li if (!api.TIFFClose(&tif2).ok()) {
279*ec63e07aSXin Li std::cerr << "TIFFClose erro\n";
280*ec63e07aSXin Li }
281*ec63e07aSXin Li
282*ec63e07aSXin Li if (pixel_status) {
283*ec63e07aSXin Li std::cerr << "pixel_status is true, expected false";
284*ec63e07aSXin Li return 1;
285*ec63e07aSXin Li }
286*ec63e07aSXin Li
287*ec63e07aSXin Li return 0;
288*ec63e07aSXin Li }
289