ultrahdr: updates to jpegr impl - 2
- clang-format the code to a common guideline
- test resources are automatically pushed by pusher
- minor cleanup
Bug: 294218453
Test: atest ultrahdr_unit_test
Change-Id: I917e9385f2a4b92add8667248a88789b5332da76
diff --git a/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h b/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
index 8b5499a..1f0c38f 100644
--- a/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
@@ -26,6 +26,8 @@
#include <utils/Errors.h>
#include <vector>
+// constraint on max width and max height is only due to device alloc constraints
+// Can tune these values basing on the target device
static const int kMaxWidth = 8192;
static const int kMaxHeight = 8192;
@@ -94,9 +96,8 @@
/*
* Decompresses metadata of the image. All vectors are owned by the caller.
*/
- bool getCompressedImageParameters(const void* image, int length,
- size_t* pWidth, size_t* pHeight,
- std::vector<uint8_t>* iccData,
+ bool getCompressedImageParameters(const void* image, int length, size_t* pWidth,
+ size_t* pHeight, std::vector<uint8_t>* iccData,
std::vector<uint8_t>* exifData);
private:
diff --git a/libs/ultrahdr/jpegdecoderhelper.cpp b/libs/ultrahdr/jpegdecoderhelper.cpp
index 33bf9ef..7ccf1f3 100644
--- a/libs/ultrahdr/jpegdecoderhelper.cpp
+++ b/libs/ultrahdr/jpegdecoderhelper.cpp
@@ -26,11 +26,11 @@
namespace android::ultrahdr {
-#define ALIGNM(x, m) ((((x) + ((m) - 1)) / (m)) * (m))
+#define ALIGNM(x, m) ((((x) + ((m)-1)) / (m)) * (m))
-const uint32_t kAPP0Marker = JPEG_APP0; // JFIF
-const uint32_t kAPP1Marker = JPEG_APP0 + 1; // EXIF, XMP
-const uint32_t kAPP2Marker = JPEG_APP0 + 2; // ICC
+const uint32_t kAPP0Marker = JPEG_APP0; // JFIF
+const uint32_t kAPP1Marker = JPEG_APP0 + 1; // EXIF, XMP
+const uint32_t kAPP2Marker = JPEG_APP0 + 2; // ICC
const std::string kXmpNameSpace = "http://ns.adobe.com/xap/1.0/";
const std::string kExifIdCode = "Exif";
@@ -76,8 +76,8 @@
static void jpegr_term_source(j_decompress_ptr /*cinfo*/) {}
-jpegr_source_mgr::jpegr_source_mgr(const uint8_t* ptr, int len) :
- mBufferPtr(ptr), mBufferLength(len) {
+jpegr_source_mgr::jpegr_source_mgr(const uint8_t* ptr, int len)
+ : mBufferPtr(ptr), mBufferLength(len) {
init_source = jpegr_init_source;
fill_input_buffer = jpegr_fill_input_buffer;
skip_input_data = jpegr_skip_input_data;
@@ -92,25 +92,18 @@
longjmp(err->setjmp_buffer, 1);
}
-JpegDecoderHelper::JpegDecoderHelper() {
-}
+JpegDecoderHelper::JpegDecoderHelper() {}
-JpegDecoderHelper::~JpegDecoderHelper() {
-}
+JpegDecoderHelper::~JpegDecoderHelper() {}
bool JpegDecoderHelper::decompressImage(const void* image, int length, bool decodeToRGBA) {
if (image == nullptr || length <= 0) {
ALOGE("Image size can not be handled: %d", length);
return false;
}
-
mResultBuffer.clear();
mXMPBuffer.clear();
- if (!decode(image, length, decodeToRGBA)) {
- return false;
- }
-
- return true;
+ return decode(image, length, decodeToRGBA);
}
void* JpegDecoderHelper::getDecompressedImagePtr() {
@@ -154,26 +147,28 @@
}
bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA) {
- jpeg_decompress_struct cinfo;
- jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
- jpegrerror_mgr myerr;
bool status = true;
-
+ jpeg_decompress_struct cinfo;
+ jpegrerror_mgr myerr;
cinfo.err = jpeg_std_error(&myerr.pub);
myerr.pub.error_exit = jpegrerror_exit;
-
if (setjmp(myerr.setjmp_buffer)) {
jpeg_destroy_decompress(&cinfo);
return false;
}
+
jpeg_create_decompress(&cinfo);
jpeg_save_markers(&cinfo, kAPP0Marker, 0xFFFF);
jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
+ jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
cinfo.src = &mgr;
- jpeg_read_header(&cinfo, TRUE);
+ if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
+ jpeg_destroy_decompress(&cinfo);
+ return false;
+ }
// Save XMP data, EXIF data, and ICC data.
// Here we only handle the first XMP / EXIF / ICC package.
@@ -184,31 +179,24 @@
bool xmpAppears = false;
bool iccAppears = false;
for (jpeg_marker_struct* marker = cinfo.marker_list;
- marker && !(exifAppears && xmpAppears && iccAppears);
- marker = marker->next) {
-
+ marker && !(exifAppears && xmpAppears && iccAppears); marker = marker->next) {
if (marker->marker != kAPP1Marker && marker->marker != kAPP2Marker) {
continue;
}
const unsigned int len = marker->data_length;
- if (!xmpAppears &&
- len > kXmpNameSpace.size() &&
- !strncmp(reinterpret_cast<const char*>(marker->data),
- kXmpNameSpace.c_str(),
+ if (!xmpAppears && len > kXmpNameSpace.size() &&
+ !strncmp(reinterpret_cast<const char*>(marker->data), kXmpNameSpace.c_str(),
kXmpNameSpace.size())) {
- mXMPBuffer.resize(len+1, 0);
+ mXMPBuffer.resize(len + 1, 0);
memcpy(static_cast<void*>(mXMPBuffer.data()), marker->data, len);
xmpAppears = true;
- } else if (!exifAppears &&
- len > kExifIdCode.size() &&
- !strncmp(reinterpret_cast<const char*>(marker->data),
- kExifIdCode.c_str(),
+ } else if (!exifAppears && len > kExifIdCode.size() &&
+ !strncmp(reinterpret_cast<const char*>(marker->data), kExifIdCode.c_str(),
kExifIdCode.size())) {
mEXIFBuffer.resize(len, 0);
memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
exifAppears = true;
- } else if (!iccAppears &&
- len > sizeof(kICCSig) &&
+ } else if (!iccAppears && len > sizeof(kICCSig) &&
!memcmp(marker->data, kICCSig, sizeof(kICCSig))) {
mICCBuffer.resize(len, 0);
memcpy(static_cast<void*>(mICCBuffer.data()), marker->data, len);
@@ -216,31 +204,25 @@
}
}
- if (cinfo.image_width > kMaxWidth || cinfo.image_height > kMaxHeight) {
- // constraint on max width and max height is only due to alloc constraints
- // tune these values basing on the target device
+ mWidth = cinfo.image_width;
+ mHeight = cinfo.image_height;
+ if (mWidth > kMaxWidth || mHeight > kMaxHeight) {
status = false;
goto CleanUp;
}
- mWidth = cinfo.image_width;
- mHeight = cinfo.image_height;
-
if (decodeToRGBA) {
// The primary image is expected to be yuv420 sampling
- if (cinfo.jpeg_color_space != JCS_YCbCr) {
- status = false;
- ALOGE("%s: decodeToRGBA unexpected jpeg color space ", __func__);
- goto CleanUp;
- }
- if (cinfo.comp_info[0].h_samp_factor != 2 ||
- cinfo.comp_info[1].h_samp_factor != 1 ||
- cinfo.comp_info[2].h_samp_factor != 1 ||
- cinfo.comp_info[0].v_samp_factor != 2 ||
- cinfo.comp_info[1].v_samp_factor != 1 ||
- cinfo.comp_info[2].v_samp_factor != 1 ) {
- status = false;
- ALOGE("%s: decodeToRGBA unexpected primary image sub-sampling", __func__);
+ if (cinfo.jpeg_color_space != JCS_YCbCr) {
+ status = false;
+ ALOGE("%s: decodeToRGBA unexpected jpeg color space ", __func__);
+ goto CleanUp;
+ }
+ if (cinfo.comp_info[0].h_samp_factor != 2 || cinfo.comp_info[0].v_samp_factor != 2 ||
+ cinfo.comp_info[1].h_samp_factor != 1 || cinfo.comp_info[1].v_samp_factor != 1 ||
+ cinfo.comp_info[2].h_samp_factor != 1 || cinfo.comp_info[2].v_samp_factor != 1) {
+ status = false;
+ ALOGE("%s: decodeToRGBA unexpected primary image sub-sampling", __func__);
goto CleanUp;
}
// 4 bytes per pixel
@@ -248,12 +230,9 @@
cinfo.out_color_space = JCS_EXT_RGBA;
} else {
if (cinfo.jpeg_color_space == JCS_YCbCr) {
- if (cinfo.comp_info[0].h_samp_factor != 2 ||
- cinfo.comp_info[1].h_samp_factor != 1 ||
- cinfo.comp_info[2].h_samp_factor != 1 ||
- cinfo.comp_info[0].v_samp_factor != 2 ||
- cinfo.comp_info[1].v_samp_factor != 1 ||
- cinfo.comp_info[2].v_samp_factor != 1) {
+ if (cinfo.comp_info[0].h_samp_factor != 2 || cinfo.comp_info[0].v_samp_factor != 2 ||
+ cinfo.comp_info[1].h_samp_factor != 1 || cinfo.comp_info[1].v_samp_factor != 1 ||
+ cinfo.comp_info[2].h_samp_factor != 1 || cinfo.comp_info[2].v_samp_factor != 1) {
status = false;
ALOGE("%s: decoding to YUV only supports 4:2:0 subsampling", __func__);
goto CleanUp;
@@ -271,11 +250,9 @@
}
cinfo.dct_method = JDCT_ISLOW;
-
jpeg_start_decompress(&cinfo);
-
if (!decompress(&cinfo, static_cast<const uint8_t*>(mResultBuffer.data()),
- cinfo.jpeg_color_space == JCS_GRAYSCALE)) {
+ cinfo.jpeg_color_space == JCS_GRAYSCALE)) {
status = false;
goto CleanUp;
}
@@ -288,25 +265,20 @@
}
bool JpegDecoderHelper::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest,
- bool isSingleChannel) {
- if (isSingleChannel) {
- return decompressSingleChannel(cinfo, dest);
- }
- if (cinfo->out_color_space == JCS_EXT_RGBA)
- return decompressRGBA(cinfo, dest);
- else
- return decompressYUV(cinfo, dest);
+ bool isSingleChannel) {
+ return isSingleChannel
+ ? decompressSingleChannel(cinfo, dest)
+ : ((cinfo->out_color_space == JCS_EXT_RGBA) ? decompressRGBA(cinfo, dest)
+ : decompressYUV(cinfo, dest));
}
-bool JpegDecoderHelper::getCompressedImageParameters(const void* image, int length,
- size_t *pWidth, size_t *pHeight,
- std::vector<uint8_t> *iccData , std::vector<uint8_t> *exifData) {
+bool JpegDecoderHelper::getCompressedImageParameters(const void* image, int length, size_t* pWidth,
+ size_t* pHeight, std::vector<uint8_t>* iccData,
+ std::vector<uint8_t>* exifData) {
jpeg_decompress_struct cinfo;
- jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
jpegrerror_mgr myerr;
cinfo.err = jpeg_std_error(&myerr.pub);
myerr.pub.error_exit = jpegrerror_exit;
-
if (setjmp(myerr.setjmp_buffer)) {
jpeg_destroy_decompress(&cinfo);
return false;
@@ -316,6 +288,7 @@
jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
+ jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
cinfo.src = &mgr;
if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
jpeg_destroy_decompress(&cinfo);
@@ -330,8 +303,7 @@
}
if (iccData != nullptr) {
- for (jpeg_marker_struct* marker = cinfo.marker_list; marker;
- marker = marker->next) {
+ for (jpeg_marker_struct* marker = cinfo.marker_list; marker; marker = marker->next) {
if (marker->marker != kAPP2Marker) {
continue;
}
@@ -368,25 +340,20 @@
}
bool JpegDecoderHelper::decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
- JSAMPLE* decodeDst = (JSAMPLE*) dest;
- uint32_t lines = 0;
- // TODO: use batches for more effectiveness
- while (lines < cinfo->image_height) {
- uint32_t ret = jpeg_read_scanlines(cinfo, &decodeDst, 1);
- if (ret == 0) {
- break;
- }
- decodeDst += cinfo->image_width * 4;
- lines++;
+ JSAMPLE* out = (JSAMPLE*)dest;
+
+ while (cinfo->output_scanline < cinfo->image_height) {
+ if (1 != jpeg_read_scanlines(cinfo, &out, 1)) return false;
+ out += cinfo->image_width * 4;
}
- return lines == cinfo->image_height;
+ return true;
}
bool JpegDecoderHelper::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
JSAMPROW y[kCompressBatchSize];
JSAMPROW cb[kCompressBatchSize / 2];
JSAMPROW cr[kCompressBatchSize / 2];
- JSAMPARRAY planes[3] {y, cb, cr};
+ JSAMPARRAY planes[3]{y, cb, cr};
size_t y_plane_size = cinfo->image_width * cinfo->image_height;
size_t uv_plane_size = y_plane_size / 4;
@@ -405,7 +372,7 @@
JSAMPROW y_intrm[kCompressBatchSize];
JSAMPROW cb_intrm[kCompressBatchSize / 2];
JSAMPROW cr_intrm[kCompressBatchSize / 2];
- JSAMPARRAY planes_intrm[3] {y_intrm, cb_intrm, cr_intrm};
+ JSAMPARRAY planes_intrm[3]{y_intrm, cb_intrm, cr_intrm};
if (!is_width_aligned) {
size_t mcu_row_size = aligned_width * kCompressBatchSize * 3 / 2;
buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
@@ -462,9 +429,10 @@
return true;
}
-bool JpegDecoderHelper::decompressSingleChannel(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
+bool JpegDecoderHelper::decompressSingleChannel(jpeg_decompress_struct* cinfo,
+ const uint8_t* dest) {
JSAMPROW y[kCompressBatchSize];
- JSAMPARRAY planes[1] {y};
+ JSAMPARRAY planes[1]{y};
uint8_t* y_plane = const_cast<uint8_t*>(dest);
std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
@@ -475,7 +443,7 @@
std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
uint8_t* y_plane_intrm = nullptr;
JSAMPROW y_intrm[kCompressBatchSize];
- JSAMPARRAY planes_intrm[1] {y_intrm};
+ JSAMPARRAY planes_intrm[1]{y_intrm};
if (!is_width_aligned) {
size_t mcu_row_size = aligned_width * kCompressBatchSize;
buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
@@ -510,4 +478,4 @@
return true;
}
-} // namespace ultrahdr
+} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/tests/Android.bp b/libs/ultrahdr/tests/Android.bp
index 004a582..bda804a 100644
--- a/libs/ultrahdr/tests/Android.bp
+++ b/libs/ultrahdr/tests/Android.bp
@@ -28,6 +28,8 @@
"gainmapmath_test.cpp",
"icchelper_test.cpp",
"jpegr_test.cpp",
+ "jpegencoderhelper_test.cpp",
+ "jpegdecoderhelper_test.cpp",
],
shared_libs: [
"libimage_io",
@@ -42,38 +44,7 @@
"libultrahdr",
"libutils",
],
-}
-
-cc_test {
- name: "jpegencoderhelper_test",
- test_suites: ["device-tests"],
- srcs: [
- "jpegencoderhelper_test.cpp",
- ],
- shared_libs: [
- "libjpeg",
- "liblog",
- ],
- static_libs: [
- "libgtest",
- "libjpegencoder",
- ],
-}
-
-cc_test {
- name: "jpegdecoderhelper_test",
- test_suites: ["device-tests"],
- srcs: [
- "jpegdecoderhelper_test.cpp",
- ],
- shared_libs: [
- "libjpeg",
- "liblog",
- ],
- static_libs: [
- "libgtest",
- "libjpegdecoder",
- "libultrahdr",
- "libutils",
+ data: [
+ "./data/*.*",
],
}
diff --git a/libs/ultrahdr/tests/AndroidTest.xml b/libs/ultrahdr/tests/AndroidTest.xml
new file mode 100644
index 0000000..1754a5c
--- /dev/null
+++ b/libs/ultrahdr/tests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the"License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an"AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Unit test configuration for ultrahdr_unit_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push-file" key="ultrahdr_unit_test" value="/data/local/tmp/ultrahdr_unit_test" />
+ <option name="push" value="data/*->/data/local/tmp/" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="ultrahdr_unit_test" />
+ </test>
+</configuration>
diff --git a/libs/ultrahdr/tests/jpegdecoderhelper_test.cpp b/libs/ultrahdr/tests/jpegdecoderhelper_test.cpp
index e2da01c..af0d59e 100644
--- a/libs/ultrahdr/tests/jpegdecoderhelper_test.cpp
+++ b/libs/ultrahdr/tests/jpegdecoderhelper_test.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#include <ultrahdr/jpegdecoderhelper.h>
-#include <ultrahdr/icc.h>
#include <gtest/gtest.h>
+#include <ultrahdr/icc.h>
+#include <ultrahdr/jpegdecoderhelper.h>
#include <utils/Log.h>
#include <fcntl.h>
@@ -24,13 +24,13 @@
namespace android::ultrahdr {
// No ICC or EXIF
-#define YUV_IMAGE "/sdcard/Documents/minnie-320x240-yuv.jpg"
+#define YUV_IMAGE "/data/local/tmp/minnie-320x240-yuv.jpg"
#define YUV_IMAGE_SIZE 20193
// Has ICC and EXIF
-#define YUV_ICC_IMAGE "/sdcard/Documents/minnie-320x240-yuv-icc.jpg"
+#define YUV_ICC_IMAGE "/data/local/tmp/minnie-320x240-yuv-icc.jpg"
#define YUV_ICC_IMAGE_SIZE 34266
// No ICC or EXIF
-#define GREY_IMAGE "/sdcard/Documents/minnie-320x240-y.jpg"
+#define GREY_IMAGE "/data/local/tmp/minnie-320x240-y.jpg"
#define GREY_IMAGE_SIZE 20193
#define IMAGE_WIDTH 320
@@ -44,6 +44,7 @@
};
JpegDecoderHelperTest();
~JpegDecoderHelperTest();
+
protected:
virtual void SetUp();
virtual void TearDown();
@@ -127,8 +128,8 @@
std::vector<uint8_t> icc, exif;
JpegDecoderHelper decoder;
- EXPECT_TRUE(decoder.getCompressedImageParameters(mYuvImage.buffer.get(), mYuvImage.size,
- &width, &height, &icc, &exif));
+ EXPECT_TRUE(decoder.getCompressedImageParameters(mYuvImage.buffer.get(), mYuvImage.size, &width,
+ &height, &icc, &exif));
EXPECT_EQ(width, IMAGE_WIDTH);
EXPECT_EQ(height, IMAGE_HEIGHT);
@@ -149,8 +150,7 @@
EXPECT_GT(icc.size(), 0);
EXPECT_GT(exif.size(), 0);
- EXPECT_EQ(IccHelper::readIccColorGamut(icc.data(), icc.size()),
- ULTRAHDR_COLORGAMUT_BT709);
+ EXPECT_EQ(IccHelper::readIccColorGamut(icc.data(), icc.size()), ULTRAHDR_COLORGAMUT_BT709);
}
-} // namespace android::ultrahdr
+} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/tests/jpegencoderhelper_test.cpp b/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
index 33cb9f6..af54eb2 100644
--- a/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
+++ b/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
@@ -22,13 +22,13 @@
namespace android::ultrahdr {
-#define ALIGNED_IMAGE "/sdcard/Documents/minnie-320x240.yu12"
+#define ALIGNED_IMAGE "/data/local/tmp/minnie-320x240.yu12"
#define ALIGNED_IMAGE_WIDTH 320
#define ALIGNED_IMAGE_HEIGHT 240
-#define SINGLE_CHANNEL_IMAGE "/sdcard/Documents/minnie-320x240.y"
+#define SINGLE_CHANNEL_IMAGE "/data/local/tmp/minnie-320x240.y"
#define SINGLE_CHANNEL_IMAGE_WIDTH ALIGNED_IMAGE_WIDTH
#define SINGLE_CHANNEL_IMAGE_HEIGHT ALIGNED_IMAGE_HEIGHT
-#define UNALIGNED_IMAGE "/sdcard/Documents/minnie-318x240.yu12"
+#define UNALIGNED_IMAGE "/data/local/tmp/minnie-318x240.yu12"
#define UNALIGNED_IMAGE_WIDTH 318
#define UNALIGNED_IMAGE_HEIGHT 240
#define JPEG_QUALITY 90
diff --git a/libs/ultrahdr/tests/jpegr_test.cpp b/libs/ultrahdr/tests/jpegr_test.cpp
index a750867..5fa758e 100644
--- a/libs/ultrahdr/tests/jpegr_test.cpp
+++ b/libs/ultrahdr/tests/jpegr_test.cpp
@@ -30,9 +30,9 @@
namespace android::ultrahdr {
// resources used by unit tests
-const char* kYCbCrP010FileName = "raw_p010_image.p010";
-const char* kYCbCr420FileName = "raw_yuv420_image.yuv420";
-const char* kSdrJpgFileName = "jpeg_image.jpg";
+const char* kYCbCrP010FileName = "/data/local/tmp/raw_p010_image.p010";
+const char* kYCbCr420FileName = "/data/local/tmp/raw_yuv420_image.yuv420";
+const char* kSdrJpgFileName = "/data/local/tmp/jpeg_image.jpg";
const int kImageWidth = 1280;
const int kImageHeight = 720;
const int kQuality = 90;