Home | History | Annotate | Download | only in jpeg
      1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
      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     http://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 
     16 // This file defines functions to compress and uncompress JPEG data
     17 // to and from memory, as well as some direct manipulations of JPEG string
     18 
     19 #include "tensorflow/core/lib/jpeg/jpeg_mem.h"
     20 
     21 #include <setjmp.h>
     22 #include <string.h>
     23 #include <algorithm>
     24 #include <memory>
     25 #include <string>
     26 #include <utility>
     27 
     28 #include "tensorflow/core/lib/jpeg/jpeg_handle.h"
     29 #include "tensorflow/core/platform/dynamic_annotations.h"
     30 #include "tensorflow/core/platform/logging.h"
     31 #include "tensorflow/core/platform/mem.h"
     32 #include "tensorflow/core/platform/types.h"
     33 
     34 namespace tensorflow {
     35 namespace jpeg {
     36 
     37 // -----------------------------------------------------------------------------
     38 // Decompression
     39 
     40 namespace {
     41 
     42 enum JPEGErrors {
     43   JPEGERRORS_OK,
     44   JPEGERRORS_UNEXPECTED_END_OF_DATA,
     45   JPEGERRORS_BAD_PARAM
     46 };
     47 
     48 // Prevent bad compiler behavior in ASAN mode by wrapping most of the
     49 // arguments in a struct struct.
     50 class FewerArgsForCompiler {
     51  public:
     52   FewerArgsForCompiler(int datasize, const UncompressFlags& flags, int64* nwarn,
     53                        std::function<uint8*(int, int, int)> allocate_output)
     54       : datasize_(datasize),
     55         flags_(flags),
     56         pnwarn_(nwarn),
     57         allocate_output_(std::move(allocate_output)),
     58         height_read_(0),
     59         height_(0),
     60         stride_(0) {
     61     if (pnwarn_ != nullptr) *pnwarn_ = 0;
     62   }
     63 
     64   const int datasize_;
     65   const UncompressFlags flags_;
     66   int64* const pnwarn_;
     67   std::function<uint8*(int, int, int)> allocate_output_;
     68   int height_read_;  // number of scanline lines successfully read
     69   int height_;
     70   int stride_;
     71 };
     72 
     73 // Check whether the crop window is valid, assuming crop is true.
     74 bool IsCropWindowValid(const UncompressFlags& flags, int input_image_width,
     75                        int input_image_height) {
     76   // Crop window is valid only if it is non zero and all the window region is
     77   // within the original image.
     78   return flags.crop_width > 0 && flags.crop_height > 0 && flags.crop_x >= 0 &&
     79          flags.crop_y >= 0 &&
     80          flags.crop_y + flags.crop_height <= input_image_height &&
     81          flags.crop_x + flags.crop_width <= input_image_width;
     82 }
     83 
     84 uint8* UncompressLow(const void* srcdata, FewerArgsForCompiler* argball) {
     85   // unpack the argball
     86   const int datasize = argball->datasize_;
     87   const auto& flags = argball->flags_;
     88   const int ratio = flags.ratio;
     89   int components = flags.components;
     90   int stride = flags.stride;              // may be 0
     91   int64* const nwarn = argball->pnwarn_;  // may be NULL
     92 
     93   // Can't decode if the ratio is not recognized by libjpeg
     94   if ((ratio != 1) && (ratio != 2) && (ratio != 4) && (ratio != 8)) {
     95     return nullptr;
     96   }
     97 
     98   // Channels must be autodetect, grayscale, or rgb.
     99   if (!(components == 0 || components == 1 || components == 3)) {
    100     return nullptr;
    101   }
    102 
    103   // if empty image, return
    104   if (datasize == 0 || srcdata == nullptr) return nullptr;
    105 
    106   // Declare temporary buffer pointer here so that we can free on error paths
    107   JSAMPLE* tempdata = nullptr;
    108 
    109   // Initialize libjpeg structures to have a memory source
    110   // Modify the usual jpeg error manager to catch fatal errors.
    111   JPEGErrors error = JPEGERRORS_OK;
    112   struct jpeg_decompress_struct cinfo;
    113   struct jpeg_error_mgr jerr;
    114   cinfo.err = jpeg_std_error(&jerr);
    115   jmp_buf jpeg_jmpbuf;
    116   cinfo.client_data = &jpeg_jmpbuf;
    117   jerr.error_exit = CatchError;
    118   if (setjmp(jpeg_jmpbuf)) {
    119     delete[] tempdata;
    120     return nullptr;
    121   }
    122 
    123   jpeg_create_decompress(&cinfo);
    124   SetSrc(&cinfo, srcdata, datasize, flags.try_recover_truncated_jpeg);
    125   jpeg_read_header(&cinfo, TRUE);
    126 
    127   // Set components automatically if desired, autoconverting cmyk to rgb.
    128   if (components == 0) components = std::min(cinfo.num_components, 3);
    129 
    130   // set grayscale and ratio parameters
    131   switch (components) {
    132     case 1:
    133       cinfo.out_color_space = JCS_GRAYSCALE;
    134       break;
    135     case 3:
    136       if (cinfo.jpeg_color_space == JCS_CMYK ||
    137           cinfo.jpeg_color_space == JCS_YCCK) {
    138         // Always use cmyk for output in a 4 channel jpeg. libjpeg has a builtin
    139         // decoder.  We will further convert to rgb below.
    140         cinfo.out_color_space = JCS_CMYK;
    141       } else {
    142         cinfo.out_color_space = JCS_RGB;
    143       }
    144       break;
    145     default:
    146       LOG(ERROR) << " Invalid components value " << components << std::endl;
    147       jpeg_destroy_decompress(&cinfo);
    148       return nullptr;
    149   }
    150   cinfo.do_fancy_upsampling = boolean(flags.fancy_upscaling);
    151   cinfo.scale_num = 1;
    152   cinfo.scale_denom = ratio;
    153   cinfo.dct_method = flags.dct_method;
    154 
    155   jpeg_start_decompress(&cinfo);
    156 
    157   int64 total_size = static_cast<int64>(cinfo.output_height) *
    158                      static_cast<int64>(cinfo.output_width);
    159   // Some of the internal routines do not gracefully handle ridiculously
    160   // large images, so fail fast.
    161   if (cinfo.output_width <= 0 || cinfo.output_height <= 0) {
    162     LOG(ERROR) << "Invalid image size: " << cinfo.output_width << " x "
    163                << cinfo.output_height;
    164     jpeg_destroy_decompress(&cinfo);
    165     return nullptr;
    166   }
    167   if (total_size >= (1LL << 29)) {
    168     LOG(ERROR) << "Image too large: " << total_size;
    169     jpeg_destroy_decompress(&cinfo);
    170     return nullptr;
    171   }
    172 
    173   JDIMENSION target_output_width = cinfo.output_width;
    174   JDIMENSION target_output_height = cinfo.output_height;
    175   JDIMENSION skipped_scanlines = 0;
    176 #if defined(LIBJPEG_TURBO_VERSION)
    177   if (flags.crop) {
    178     // Update target output height and width based on crop window.
    179     target_output_height = flags.crop_height;
    180     target_output_width = flags.crop_width;
    181 
    182     // So far, cinfo holds the original input image information.
    183     if (!IsCropWindowValid(flags, cinfo.output_width, cinfo.output_height)) {
    184       LOG(ERROR) << "Invalid crop window: x=" << flags.crop_x
    185                  << ", y=" << flags.crop_y << ", w=" << target_output_width
    186                  << ", h=" << target_output_height
    187                  << " for image_width: " << cinfo.output_width
    188                  << " and image_height: " << cinfo.output_height;
    189       jpeg_destroy_decompress(&cinfo);
    190       return nullptr;
    191     }
    192 
    193     // Update cinfo.output_width. It is tricky that cinfo.output_width must
    194     // fall on an Minimum Coded Unit (MCU) boundary; if it doesn't, then it will
    195     // be moved left to the nearest MCU boundary, and width will be increased
    196     // accordingly. Therefore, the final cinfo.crop_width might differ from the
    197     // given flags.crop_width. Please see libjpeg library for details.
    198     JDIMENSION crop_width = flags.crop_width;
    199     JDIMENSION crop_x = flags.crop_x;
    200     jpeg_crop_scanline(&cinfo, &crop_x, &crop_width);
    201 
    202     // Update cinfo.output_scanline.
    203     skipped_scanlines = jpeg_skip_scanlines(&cinfo, flags.crop_y);
    204     CHECK_EQ(skipped_scanlines, flags.crop_y);
    205   }
    206 #endif
    207 
    208   // check for compatible stride
    209   const int min_stride = target_output_width * components * sizeof(JSAMPLE);
    210   if (stride == 0) {
    211     stride = min_stride;
    212   } else if (stride < min_stride) {
    213     LOG(ERROR) << "Incompatible stride: " << stride << " < " << min_stride;
    214     jpeg_destroy_decompress(&cinfo);
    215     return nullptr;
    216   }
    217 
    218   // Remember stride and height for use in Uncompress
    219   argball->height_ = target_output_height;
    220   argball->stride_ = stride;
    221 
    222 #if !defined(LIBJPEG_TURBO_VERSION)
    223   uint8* dstdata = nullptr;
    224   if (flags.crop) {
    225     dstdata = new JSAMPLE[stride * target_output_height];
    226   } else {
    227     dstdata = argball->allocate_output_(target_output_width,
    228                                         target_output_height, components);
    229   }
    230 #else
    231   uint8* dstdata = argball->allocate_output_(target_output_width,
    232                                              target_output_height, components);
    233 #endif
    234   if (dstdata == nullptr) {
    235     jpeg_destroy_decompress(&cinfo);
    236     return nullptr;
    237   }
    238   JSAMPLE* output_line = static_cast<JSAMPLE*>(dstdata);
    239 
    240   // jpeg_read_scanlines requires the buffers to be allocated based on
    241   // cinfo.output_width, but the target image width might be different if crop
    242   // is enabled and crop_width is not MCU aligned. In this case, we need to
    243   // realign the scanline output to achieve the exact cropping.  Notably, only
    244   // cinfo.output_width needs to fall on MCU boundary, while cinfo.output_height
    245   // has no such constraint.
    246   const bool need_realign_cropped_scanline =
    247       (target_output_width != cinfo.output_width);
    248   const bool use_cmyk = (cinfo.out_color_space == JCS_CMYK);
    249 
    250   if (use_cmyk) {
    251     // Temporary buffer used for CMYK -> RGB conversion.
    252     tempdata = new JSAMPLE[cinfo.output_width * 4];
    253   } else if (need_realign_cropped_scanline) {
    254     // Temporary buffer used for MCU-aligned scanline data.
    255     tempdata = new JSAMPLE[cinfo.output_width * components];
    256   }
    257 
    258   // If there is an error reading a line, this aborts the reading.
    259   // Save the fraction of the image that has been read.
    260   argball->height_read_ = target_output_height;
    261 
    262   // These variables are just to avoid repeated computation in the loop.
    263   const int max_scanlines_to_read = skipped_scanlines + target_output_height;
    264   const int mcu_align_offset =
    265       (cinfo.output_width - target_output_width) * (use_cmyk ? 4 : components);
    266   while (cinfo.output_scanline < max_scanlines_to_read) {
    267     int num_lines_read = 0;
    268     if (use_cmyk) {
    269       num_lines_read = jpeg_read_scanlines(&cinfo, &tempdata, 1);
    270       if (num_lines_read > 0) {
    271         // Convert CMYK to RGB if scanline read succeeded.
    272         for (size_t i = 0; i < target_output_width; ++i) {
    273           int offset = 4 * i;
    274           if (need_realign_cropped_scanline) {
    275             // Align the offset for MCU boundary.
    276             offset += mcu_align_offset;
    277           }
    278           const int c = tempdata[offset + 0];
    279           const int m = tempdata[offset + 1];
    280           const int y = tempdata[offset + 2];
    281           const int k = tempdata[offset + 3];
    282           int r, g, b;
    283           if (cinfo.saw_Adobe_marker) {
    284             r = (k * c) / 255;
    285             g = (k * m) / 255;
    286             b = (k * y) / 255;
    287           } else {
    288             r = (255 - k) * (255 - c) / 255;
    289             g = (255 - k) * (255 - m) / 255;
    290             b = (255 - k) * (255 - y) / 255;
    291           }
    292           output_line[3 * i + 0] = r;
    293           output_line[3 * i + 1] = g;
    294           output_line[3 * i + 2] = b;
    295         }
    296       }
    297     } else if (need_realign_cropped_scanline) {
    298       num_lines_read = jpeg_read_scanlines(&cinfo, &tempdata, 1);
    299       if (num_lines_read > 0) {
    300         memcpy(output_line, tempdata + mcu_align_offset, min_stride);
    301       }
    302     } else {
    303       num_lines_read = jpeg_read_scanlines(&cinfo, &output_line, 1);
    304     }
    305     // Handle error cases
    306     if (num_lines_read == 0) {
    307       LOG(ERROR) << "Premature end of JPEG data. Stopped at line "
    308                  << cinfo.output_scanline - skipped_scanlines << "/"
    309                  << target_output_height;
    310       if (!flags.try_recover_truncated_jpeg) {
    311         argball->height_read_ = cinfo.output_scanline - skipped_scanlines;
    312         error = JPEGERRORS_UNEXPECTED_END_OF_DATA;
    313       } else {
    314         for (size_t line = cinfo.output_scanline; line < max_scanlines_to_read;
    315              ++line) {
    316           if (line == 0) {
    317             // If even the first line is missing, fill with black color
    318             memset(output_line, 0, min_stride);
    319           } else {
    320             // else, just replicate the line above.
    321             memcpy(output_line, output_line - stride, min_stride);
    322           }
    323           output_line += stride;
    324         }
    325         argball->height_read_ =
    326             target_output_height;  // consider all lines as read
    327         // prevent error-on-exit in libjpeg:
    328         cinfo.output_scanline = max_scanlines_to_read;
    329       }
    330       break;
    331     }
    332     DCHECK_EQ(num_lines_read, 1);
    333     TF_ANNOTATE_MEMORY_IS_INITIALIZED(output_line, min_stride);
    334     output_line += stride;
    335   }
    336   delete[] tempdata;
    337   tempdata = nullptr;
    338 
    339 #if defined(LIBJPEG_TURBO_VERSION)
    340   if (flags.crop && cinfo.output_scanline < cinfo.output_height) {
    341     // Skip the rest of scanlines, required by jpeg_destroy_decompress.
    342     jpeg_skip_scanlines(&cinfo,
    343                         cinfo.output_height - flags.crop_y - flags.crop_height);
    344     // After this, cinfo.output_height must be equal to cinfo.output_height;
    345     // otherwise, jpeg_destroy_decompress would fail.
    346   }
    347 #endif
    348 
    349   // Convert the RGB data to RGBA, with alpha set to 0xFF to indicate
    350   // opacity.
    351   // RGBRGBRGB... --> RGBARGBARGBA...
    352   if (components == 4) {
    353     // Start on the last line.
    354     JSAMPLE* scanlineptr = static_cast<JSAMPLE*>(
    355         dstdata + static_cast<int64>(target_output_height - 1) * stride);
    356     const JSAMPLE kOpaque = -1;  // All ones appropriate for JSAMPLE.
    357     const int right_rgb = (target_output_width - 1) * 3;
    358     const int right_rgba = (target_output_width - 1) * 4;
    359 
    360     for (int y = target_output_height; y-- > 0;) {
    361       // We do all the transformations in place, going backwards for each row.
    362       const JSAMPLE* rgb_pixel = scanlineptr + right_rgb;
    363       JSAMPLE* rgba_pixel = scanlineptr + right_rgba;
    364       scanlineptr -= stride;
    365       for (int x = target_output_width; x-- > 0;
    366            rgba_pixel -= 4, rgb_pixel -= 3) {
    367         // We copy the 3 bytes at rgb_pixel into the 4 bytes at rgba_pixel
    368         // The "a" channel is set to be opaque.
    369         rgba_pixel[3] = kOpaque;
    370         rgba_pixel[2] = rgb_pixel[2];
    371         rgba_pixel[1] = rgb_pixel[1];
    372         rgba_pixel[0] = rgb_pixel[0];
    373       }
    374     }
    375   }
    376 
    377   switch (components) {
    378     case 1:
    379       if (cinfo.output_components != 1) {
    380         error = JPEGERRORS_BAD_PARAM;
    381       }
    382       break;
    383     case 3:
    384     case 4:
    385       if (cinfo.out_color_space == JCS_CMYK) {
    386         if (cinfo.output_components != 4) {
    387           error = JPEGERRORS_BAD_PARAM;
    388         }
    389       } else {
    390         if (cinfo.output_components != 3) {
    391           error = JPEGERRORS_BAD_PARAM;
    392         }
    393       }
    394       break;
    395     default:
    396       // will never happen, should be catched by the previous switch
    397       LOG(ERROR) << "Invalid components value " << components << std::endl;
    398       jpeg_destroy_decompress(&cinfo);
    399       return nullptr;
    400   }
    401 
    402   // save number of warnings if requested
    403   if (nwarn != nullptr) {
    404     *nwarn = cinfo.err->num_warnings;
    405   }
    406 
    407   // Handle errors in JPEG
    408   switch (error) {
    409     case JPEGERRORS_OK:
    410       jpeg_finish_decompress(&cinfo);
    411       break;
    412     case JPEGERRORS_UNEXPECTED_END_OF_DATA:
    413     case JPEGERRORS_BAD_PARAM:
    414       jpeg_abort(reinterpret_cast<j_common_ptr>(&cinfo));
    415       break;
    416     default:
    417       LOG(ERROR) << "Unhandled case " << error;
    418       break;
    419   }
    420 
    421 #if !defined(LIBJPEG_TURBO_VERSION)
    422   // TODO(tanmingxing): delete all these code after migrating to libjpeg_turbo
    423   // for Windows.
    424   if (flags.crop) {
    425     // Update target output height and width based on crop window.
    426     target_output_height = flags.crop_height;
    427     target_output_width = flags.crop_width;
    428 
    429     // cinfo holds the original input image information.
    430     if (!IsCropWindowValid(flags, cinfo.output_width, cinfo.output_height)) {
    431       LOG(ERROR) << "Invalid crop window: x=" << flags.crop_x
    432                  << ", y=" << flags.crop_y << ", w=" << target_output_width
    433                  << ", h=" << target_output_height
    434                  << " for image_width: " << cinfo.output_width
    435                  << " and image_height: " << cinfo.output_height;
    436       delete[] dstdata;
    437       jpeg_destroy_decompress(&cinfo);
    438       return nullptr;
    439     }
    440 
    441     const uint8* full_image = dstdata;
    442     dstdata = argball->allocate_output_(target_output_width,
    443                                         target_output_height, components);
    444     if (dstdata == nullptr) {
    445       delete[] full_image;
    446       jpeg_destroy_decompress(&cinfo);
    447       return nullptr;
    448     }
    449 
    450     const int full_image_stride = stride;
    451     // Update stride and hight for crop window.
    452     const int min_stride = target_output_width * components * sizeof(JSAMPLE);
    453     if (flags.stride == 0) {
    454       stride = min_stride;
    455     }
    456     argball->height_ = target_output_height;
    457     argball->stride_ = stride;
    458 
    459     if (argball->height_read_ > target_output_height) {
    460       argball->height_read_ = target_output_height;
    461     }
    462     const int crop_offset = flags.crop_x * components * sizeof(JSAMPLE);
    463     const uint8* full_image_ptr = full_image + flags.crop_y * full_image_stride;
    464     uint8* crop_image_ptr = dstdata;
    465     for (int i = 0; i < argball->height_read_; i++) {
    466       memcpy(crop_image_ptr, full_image_ptr + crop_offset, min_stride);
    467       crop_image_ptr += stride;
    468       full_image_ptr += full_image_stride;
    469     }
    470     delete[] full_image;
    471   }
    472 #endif
    473 
    474   jpeg_destroy_decompress(&cinfo);
    475   return dstdata;
    476 }
    477 
    478 }  // anonymous namespace
    479 
    480 // -----------------------------------------------------------------------------
    481 //  We do the apparently silly thing of packing 5 of the arguments
    482 //  into a structure that is then passed to another routine
    483 //  that does all the work.  The reason is that we want to catch
    484 //  fatal JPEG library errors with setjmp/longjmp, and g++ and
    485 //  associated libraries aren't good enough to guarantee that 7
    486 //  parameters won't get clobbered by the longjmp.  So we help
    487 //  it out a little.
    488 uint8* Uncompress(const void* srcdata, int datasize,
    489                   const UncompressFlags& flags, int64* nwarn,
    490                   std::function<uint8*(int, int, int)> allocate_output) {
    491   FewerArgsForCompiler argball(datasize, flags, nwarn,
    492                                std::move(allocate_output));
    493   uint8* const dstdata = UncompressLow(srcdata, &argball);
    494 
    495   const float fraction_read =
    496       argball.height_ == 0
    497           ? 1.0
    498           : (static_cast<float>(argball.height_read_) / argball.height_);
    499   if (dstdata == nullptr ||
    500       fraction_read < std::min(1.0f, flags.min_acceptable_fraction)) {
    501     // Major failure, none or too-partial read returned; get out
    502     return nullptr;
    503   }
    504 
    505   // If there was an error in reading the jpeg data,
    506   // set the unread pixels to black
    507   if (argball.height_read_ != argball.height_) {
    508     const int first_bad_line = argball.height_read_;
    509     uint8* start = dstdata + first_bad_line * argball.stride_;
    510     const int nbytes = (argball.height_ - first_bad_line) * argball.stride_;
    511     memset(static_cast<void*>(start), 0, nbytes);
    512   }
    513 
    514   return dstdata;
    515 }
    516 
    517 uint8* Uncompress(const void* srcdata, int datasize,
    518                   const UncompressFlags& flags, int* pwidth, int* pheight,
    519                   int* pcomponents, int64* nwarn) {
    520   uint8* buffer = nullptr;
    521   uint8* result =
    522       Uncompress(srcdata, datasize, flags, nwarn,
    523                  [=, &buffer](int width, int height, int components) {
    524                    if (pwidth != nullptr) *pwidth = width;
    525                    if (pheight != nullptr) *pheight = height;
    526                    if (pcomponents != nullptr) *pcomponents = components;
    527                    buffer = new uint8[height * width * components];
    528                    return buffer;
    529                  });
    530   if (!result) delete[] buffer;
    531   return result;
    532 }
    533 
    534 // ----------------------------------------------------------------------------
    535 // Computes image information from jpeg header.
    536 // Returns true on success; false on failure.
    537 bool GetImageInfo(const void* srcdata, int datasize, int* width, int* height,
    538                   int* components) {
    539   // Init in case of failure
    540   if (width) *width = 0;
    541   if (height) *height = 0;
    542   if (components) *components = 0;
    543 
    544   // If empty image, return
    545   if (datasize == 0 || srcdata == nullptr) return false;
    546 
    547   // Initialize libjpeg structures to have a memory source
    548   // Modify the usual jpeg error manager to catch fatal errors.
    549   struct jpeg_decompress_struct cinfo;
    550   struct jpeg_error_mgr jerr;
    551   jmp_buf jpeg_jmpbuf;
    552   cinfo.err = jpeg_std_error(&jerr);
    553   cinfo.client_data = &jpeg_jmpbuf;
    554   jerr.error_exit = CatchError;
    555   if (setjmp(jpeg_jmpbuf)) {
    556     return false;
    557   }
    558 
    559   // set up, read header, set image parameters, save size
    560   jpeg_create_decompress(&cinfo);
    561   SetSrc(&cinfo, srcdata, datasize, false);
    562 
    563   jpeg_read_header(&cinfo, TRUE);
    564   jpeg_start_decompress(&cinfo);  // required to transfer image size to cinfo
    565   if (width) *width = cinfo.output_width;
    566   if (height) *height = cinfo.output_height;
    567   if (components) *components = cinfo.output_components;
    568 
    569   jpeg_destroy_decompress(&cinfo);
    570 
    571   return true;
    572 }
    573 
    574 // -----------------------------------------------------------------------------
    575 // Compression
    576 
    577 namespace {
    578 bool CompressInternal(const uint8* srcdata, int width, int height,
    579                       const CompressFlags& flags, string* output) {
    580   output->clear();
    581   const int components = (static_cast<int>(flags.format) & 0xff);
    582 
    583   int64 total_size = static_cast<int64>(width) * static_cast<int64>(height);
    584   // Some of the internal routines do not gracefully handle ridiculously
    585   // large images, so fail fast.
    586   if (width <= 0 || height <= 0) {
    587     LOG(ERROR) << "Invalid image size: " << width << " x " << height;
    588     return false;
    589   }
    590   if (total_size >= (1LL << 29)) {
    591     LOG(ERROR) << "Image too large: " << total_size;
    592     return false;
    593   }
    594 
    595   int in_stride = flags.stride;
    596   if (in_stride == 0) {
    597     in_stride = width * (static_cast<int>(flags.format) & 0xff);
    598   } else if (in_stride < width * components) {
    599     LOG(ERROR) << "Incompatible input stride";
    600     return false;
    601   }
    602 
    603   JOCTET* buffer = nullptr;
    604 
    605   // NOTE: for broader use xmp_metadata should be made a unicode string
    606   CHECK(srcdata != nullptr);
    607   CHECK(output != nullptr);
    608   // This struct contains the JPEG compression parameters and pointers to
    609   // working space
    610   struct jpeg_compress_struct cinfo;
    611   // This struct represents a JPEG error handler.
    612   struct jpeg_error_mgr jerr;
    613   jmp_buf jpeg_jmpbuf;  // recovery point in case of error
    614 
    615   // Step 1: allocate and initialize JPEG compression object
    616   // Use the usual jpeg error manager.
    617   cinfo.err = jpeg_std_error(&jerr);
    618   cinfo.client_data = &jpeg_jmpbuf;
    619   jerr.error_exit = CatchError;
    620   if (setjmp(jpeg_jmpbuf)) {
    621     output->clear();
    622     delete[] buffer;
    623     return false;
    624   }
    625 
    626   jpeg_create_compress(&cinfo);
    627 
    628   // Step 2: specify data destination
    629   // We allocate a buffer of reasonable size. If we have a small image, just
    630   // estimate the size of the output using the number of bytes of the input.
    631   // If this is getting too big, we will append to the string by chunks of 1MB.
    632   // This seems like a reasonable compromise between performance and memory.
    633   int bufsize = std::min(width * height * components, 1 << 20);
    634   buffer = new JOCTET[bufsize];
    635   SetDest(&cinfo, buffer, bufsize, output);
    636 
    637   // Step 3: set parameters for compression
    638   cinfo.image_width = width;
    639   cinfo.image_height = height;
    640   switch (components) {
    641     case 1:
    642       cinfo.input_components = 1;
    643       cinfo.in_color_space = JCS_GRAYSCALE;
    644       break;
    645     case 3:
    646     case 4:
    647       cinfo.input_components = 3;
    648       cinfo.in_color_space = JCS_RGB;
    649       break;
    650     default:
    651       LOG(ERROR) << " Invalid components value " << components << std::endl;
    652       output->clear();
    653       delete[] buffer;
    654       return false;
    655   }
    656   jpeg_set_defaults(&cinfo);
    657   if (flags.optimize_jpeg_size) cinfo.optimize_coding = TRUE;
    658 
    659   cinfo.density_unit = flags.density_unit;  // JFIF code for pixel size units:
    660                                             // 1 = in, 2 = cm
    661   cinfo.X_density = flags.x_density;        // Horizontal pixel density
    662   cinfo.Y_density = flags.y_density;        // Vertical pixel density
    663   jpeg_set_quality(&cinfo, flags.quality, TRUE);
    664 
    665   if (flags.progressive) {
    666     jpeg_simple_progression(&cinfo);
    667   }
    668 
    669   if (!flags.chroma_downsampling) {
    670     // Turn off chroma subsampling (it is on by default).  For more details on
    671     // chroma subsampling, see http://en.wikipedia.org/wiki/Chroma_subsampling.
    672     for (int i = 0; i < cinfo.num_components; ++i) {
    673       cinfo.comp_info[i].h_samp_factor = 1;
    674       cinfo.comp_info[i].v_samp_factor = 1;
    675     }
    676   }
    677 
    678   jpeg_start_compress(&cinfo, TRUE);
    679 
    680   // Embed XMP metadata if any
    681   if (!flags.xmp_metadata.empty()) {
    682     // XMP metadata is embedded in the APP1 tag of JPEG and requires this
    683     // namespace header string (null-terminated)
    684     const string name_space = "http://ns.adobe.com/xap/1.0/";
    685     const int name_space_length = name_space.size();
    686     const int metadata_length = flags.xmp_metadata.size();
    687     const int packet_length = metadata_length + name_space_length + 1;
    688     std::unique_ptr<JOCTET[]> joctet_packet(new JOCTET[packet_length]);
    689 
    690     for (int i = 0; i < name_space_length; i++) {
    691       // Conversion char --> JOCTET
    692       joctet_packet[i] = name_space[i];
    693     }
    694     joctet_packet[name_space_length] = 0;  // null-terminate namespace string
    695 
    696     for (int i = 0; i < metadata_length; i++) {
    697       // Conversion char --> JOCTET
    698       joctet_packet[i + name_space_length + 1] = flags.xmp_metadata[i];
    699     }
    700     jpeg_write_marker(&cinfo, JPEG_APP0 + 1, joctet_packet.get(),
    701                       packet_length);
    702   }
    703 
    704   // JSAMPLEs per row in image_buffer
    705   std::unique_ptr<JSAMPLE[]> row_temp(
    706       new JSAMPLE[width * cinfo.input_components]);
    707   while (cinfo.next_scanline < cinfo.image_height) {
    708     JSAMPROW row_pointer[1];  // pointer to JSAMPLE row[s]
    709     const uint8* r = &srcdata[cinfo.next_scanline * in_stride];
    710     uint8* p = static_cast<uint8*>(row_temp.get());
    711     switch (flags.format) {
    712       case FORMAT_RGBA: {
    713         for (int i = 0; i < width; ++i, p += 3, r += 4) {
    714           p[0] = r[0];
    715           p[1] = r[1];
    716           p[2] = r[2];
    717         }
    718         row_pointer[0] = row_temp.get();
    719         break;
    720       }
    721       case FORMAT_ABGR: {
    722         for (int i = 0; i < width; ++i, p += 3, r += 4) {
    723           p[0] = r[3];
    724           p[1] = r[2];
    725           p[2] = r[1];
    726         }
    727         row_pointer[0] = row_temp.get();
    728         break;
    729       }
    730       default: {
    731         row_pointer[0] = reinterpret_cast<JSAMPLE*>(const_cast<JSAMPLE*>(r));
    732       }
    733     }
    734     CHECK_EQ(jpeg_write_scanlines(&cinfo, row_pointer, 1), 1u);
    735   }
    736   jpeg_finish_compress(&cinfo);
    737 
    738   // release JPEG compression object
    739   jpeg_destroy_compress(&cinfo);
    740   delete[] buffer;
    741   return true;
    742 }
    743 
    744 }  // anonymous namespace
    745 
    746 // -----------------------------------------------------------------------------
    747 
    748 bool Compress(const void* srcdata, int width, int height,
    749               const CompressFlags& flags, string* output) {
    750   return CompressInternal(static_cast<const uint8*>(srcdata), width, height,
    751                           flags, output);
    752 }
    753 
    754 string Compress(const void* srcdata, int width, int height,
    755                 const CompressFlags& flags) {
    756   string temp;
    757   CompressInternal(static_cast<const uint8*>(srcdata), width, height, flags,
    758                    &temp);
    759   // If CompressInternal fails, temp will be empty.
    760   return temp;
    761 }
    762 
    763 }  // namespace jpeg
    764 }  // namespace tensorflow
    765