This source file includes following definitions.
- show_help
 
- loadJPEG
 
- loadJPEG
 
- user_read_fn
 
- loadPNG
 
- loadPNG
 
- list_encoder_parameters
 
- set_params
 
- main
 
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <fstream>
#include <iostream>
#include <memory>
#include <algorithm>
#include <vector>
#include <libheif/heif.h>
#if HAVE_LIBJPEG
extern "C" {
#include <jpeglib.h>
}
#endif
#if HAVE_LIBPNG
extern "C" {
#include <png.h>
}
#endif
#include <assert.h>
int master_alpha = 1;
int thumb_alpha = 1;
static struct option long_options[] = {
  {"help",       no_argument,       0, 'h' },
  {"quality",    required_argument, 0, 'q' },
  {"output",     required_argument, 0, 'o' },
  {"lossless",   no_argument,       0, 'L' },
  {"thumb",      required_argument, 0, 't' },
  {"verbose",    no_argument,       0, 'v' },
  {"params",     no_argument,       0, 'P' },
  {"no-alpha",   no_argument, &master_alpha, 0 },
  {"no-thumb-alpha",   no_argument, &thumb_alpha, 0 },
  {0,         0,                 0,  0 }
};
void show_help(const char* argv0)
{
  std::cerr << " heif-enc  libheif version: " << heif_get_version() << "\n"
            << "----------------------------------------\n"
            << "usage: heif-enc [options] image.jpeg ...\n"
            << "\n"
            << "When specifying multiple source images, they will all be saved into the same HEIF file.\n"
            << "\n"
            << "options:\n"
            << "  -h, --help      show help\n"
            << "  -q, --quality   set output quality (0-100) for lossy compression\n"
            << "  -L, --lossless  generate lossless output (-q has no effect)\n"
            << "  -t, --thumb #   generate thumbnail with maximum size # (default: off)\n"
            << "      --no-alpha  do not save alpha channel\n"
            << "      --no-thumb-alpha  do not save alpha channel in thumbnail image\n"
            << "  -o, --output    output filename (optional)\n"
            << "  -v, --verbose   enable logging output (more -v will increase logging level)\n"
            << "  -P, --params    show all encoder parameters\n"
            << "  -p              set encoder parameter (NAME=VALUE)\n";
}
#if HAVE_LIBJPEG
std::shared_ptr<heif_image> loadJPEG(const char* filename)
{
  struct heif_image* image = nullptr;
  
  struct jpeg_decompress_struct cinfo;
  struct jpeg_error_mgr jerr;
  
  FILE * infile;
  if ((infile = fopen(filename, "rb")) == NULL) {
    std::cerr << "Can't open " << filename << "\n";
    exit(1);
  }
  
  jpeg_create_decompress(&cinfo);
  cinfo.err = jpeg_std_error(&jerr);
  jpeg_stdio_src(&cinfo, infile);
  jpeg_read_header(&cinfo, TRUE);
  if (cinfo.jpeg_color_space == JCS_GRAYSCALE)
    {
      cinfo.out_color_space = JCS_GRAYSCALE;
      jpeg_start_decompress(&cinfo);
      JSAMPARRAY buffer;
      buffer = (*cinfo.mem->alloc_sarray)
        ((j_common_ptr) &cinfo, JPOOL_IMAGE, cinfo.output_width * cinfo.output_components, 1);
      
      struct heif_error err = heif_image_create(cinfo.output_width, cinfo.output_height,
                                                heif_colorspace_YCbCr,
                                                heif_chroma_monochrome,
                                                &image);
      (void)err;
      
      heif_image_add_plane(image, heif_channel_Y, cinfo.output_width, cinfo.output_height, 8);
      int y_stride;
      uint8_t* py = heif_image_get_plane(image, heif_channel_Y, &y_stride);
      
      while (cinfo.output_scanline < cinfo.output_height) {
        (void) jpeg_read_scanlines(&cinfo, buffer, 1);
        memcpy(py + (cinfo.output_scanline-1)*y_stride, buffer, cinfo.output_width);
      }
    }
  else
    {
      cinfo.out_color_space = JCS_YCbCr;
      jpeg_start_decompress(&cinfo);
      JSAMPARRAY buffer;
      buffer = (*cinfo.mem->alloc_sarray)
        ((j_common_ptr) &cinfo, JPOOL_IMAGE, cinfo.output_width * cinfo.output_components, 1);
      
      struct heif_error err = heif_image_create(cinfo.output_width, cinfo.output_height,
                                                heif_colorspace_YCbCr,
                                                heif_chroma_420,
                                                &image);
      (void)err;
      heif_image_add_plane(image, heif_channel_Y, cinfo.output_width, cinfo.output_height, 8);
      heif_image_add_plane(image, heif_channel_Cb, (cinfo.output_width+1)/2, (cinfo.output_height+1)/2, 8);
      heif_image_add_plane(image, heif_channel_Cr, (cinfo.output_width+1)/2, (cinfo.output_height+1)/2, 8);
      int y_stride;
      int cb_stride;
      int cr_stride;
      uint8_t* py  = heif_image_get_plane(image, heif_channel_Y, &y_stride);
      uint8_t* pcb = heif_image_get_plane(image, heif_channel_Cb, &cb_stride);
      uint8_t* pcr = heif_image_get_plane(image, heif_channel_Cr, &cr_stride);
      
      
      while (cinfo.output_scanline < cinfo.output_height) {
        JOCTET* bufp;
        (void) jpeg_read_scanlines(&cinfo, buffer, 1);
        bufp = buffer[0];
        int y = cinfo.output_scanline-1;
        for (unsigned int x=0;x<cinfo.output_width;x+=2) {
          py[y*y_stride + x] = *bufp++;
          pcb[y/2*cb_stride + x/2] = *bufp++;
          pcr[y/2*cr_stride + x/2] = *bufp++;
          if (x+1 < cinfo.output_width) {
            py[y*y_stride + x+1] = *bufp++;
          }
          bufp+=2;
        }
        if (cinfo.output_scanline < cinfo.output_height) {
          (void) jpeg_read_scanlines(&cinfo, buffer, 1);
          bufp = buffer[0];
          y = cinfo.output_scanline-1;
          for (unsigned int x=0;x<cinfo.output_width;x++) {
            py[y*y_stride + x] = *bufp++;
            bufp+=2;
          }
        }
      }
    }
  
  jpeg_finish_decompress(&cinfo);
  jpeg_destroy_decompress(&cinfo);
  fclose(infile);
  return std::shared_ptr<heif_image>(image,
                                    [] (heif_image* img) { heif_image_release(img); });
}
#else
std::shared_ptr<heif_image> loadJPEG(const char* filename)
{
  std::cerr << "Cannot load JPEG because libjpeg support was not compiled.\n";
  exit(1);
  return nullptr;
}
#endif
#if HAVE_LIBPNG
static void
user_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
{
  FILE* fh = (FILE*)png_get_io_ptr(png_ptr);
  size_t n = fread((char*)data,length,1,fh);
  (void)n;
} 
std::shared_ptr<heif_image> loadPNG(const char* filename)
{
  FILE* fh = fopen(filename,"rb");
  if (!fh) {
    std::cerr << "Can't open " << filename << "\n";
    exit(1);
  }
  
  struct heif_image* image = nullptr;
  png_structp png_ptr;
  png_infop info_ptr;
  png_uint_32 width, height;
  int bit_depth, color_type, interlace_type;
  
  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  assert(png_ptr != NULL);
  
  info_ptr = png_create_info_struct(png_ptr);
  if (info_ptr == NULL) {
    png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
    assert(false); 
  } 
    
  if (setjmp(png_jmpbuf(png_ptr))) {
    
    png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
    
    assert(false); 
  } 
  
  png_set_read_fn(png_ptr, (void *)fh, user_read_fn);
  
  
  png_read_info(png_ptr, info_ptr);
  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
               &interlace_type, NULL, NULL);
  assert(bit_depth < 16); 
  
  
  
  
  
  
  
  png_set_packing(png_ptr);
  
  if (color_type == PNG_COLOR_TYPE_PALETTE)
    png_set_expand(png_ptr);
  
  if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
    png_set_expand(png_ptr);
  
#if 0
  
  png_color_16 my_background = {0, 255, 255, 255, 255};
  png_color_16 *image_background;
  if (png_get_bKGD(png_ptr, info_ptr, &image_background))
    png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
  else
    png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
#endif
  
  png_read_update_info(png_ptr, info_ptr);
  
  
  uint8_t** row_pointers = new png_bytep[height];
  assert(row_pointers != NULL);
  for (uint32_t y = 0; y < height; y++) {
    row_pointers[y] = (png_bytep)malloc(png_get_rowbytes(png_ptr, info_ptr));
    assert(row_pointers[y] != NULL);
  } 
  
  png_read_image(png_ptr, row_pointers);
  
  png_read_end(png_ptr, info_ptr);
  
  png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
  
  
  int band;
  switch (color_type) {
  case PNG_COLOR_TYPE_GRAY:
  case PNG_COLOR_TYPE_GRAY_ALPHA:
    band = 1;
    break;
  case PNG_COLOR_TYPE_PALETTE:
  case PNG_COLOR_TYPE_RGB:
  case PNG_COLOR_TYPE_RGB_ALPHA:
    band = 3;
    break;
  default:
    assert(false); 
  } 
  struct heif_error err;
  bool has_alpha = (color_type & PNG_COLOR_MASK_ALPHA);
  if (band==1) {
    err = heif_image_create((int)width, (int)height,
                            heif_colorspace_YCbCr,
                            heif_chroma_monochrome,
                            &image);
    (void)err;
    heif_image_add_plane(image, heif_channel_Y, (int)width, (int)height, 8);
    int y_stride;
    int a_stride;
    uint8_t* py  = heif_image_get_plane(image, heif_channel_Y, &y_stride);
    uint8_t* pa  = nullptr;
    if (has_alpha) {
      heif_image_add_plane(image, heif_channel_Alpha, (int)width, (int)height, 8);
      pa = heif_image_get_plane(image, heif_channel_Alpha, &a_stride);
    }
    for (uint32_t y = 0; y < height; y++) {
      uint8_t* p = row_pointers[y];
      if (has_alpha)
        {
          for (uint32_t x = 0; x < width; x++) {
            py[y*y_stride + x] = *p++;
            pa[y*a_stride + x] = *p++;
          }
        }
      else
        {
          memcpy(&py[y*y_stride],p, width);
        }
    }
  }
  else {
    err = heif_image_create((int)width, (int)height,
                            heif_colorspace_RGB,
                            has_alpha ? heif_chroma_interleaved_RGBA : heif_chroma_interleaved_RGB,
                            &image);
    (void)err;
    heif_image_add_plane(image, heif_channel_interleaved, (int)width, (int)height,
                         has_alpha ? 32 : 24);
    int stride;
    uint8_t* p = heif_image_get_plane(image, heif_channel_interleaved, &stride);
    for (uint32_t y = 0; y < height; y++) {
      if (has_alpha) {
        memcpy(p + y*stride, row_pointers[y], width*4);
      }
      else {
        memcpy(p + y*stride, row_pointers[y], width*3);
      }
    }
  }
  for (uint32_t y = 0; y < height; y++) {
    free(row_pointers[y]);
  } 
  delete[] row_pointers;
  return std::shared_ptr<heif_image>(image,
                                    [] (heif_image* img) { heif_image_release(img); });
}
#else
std::shared_ptr<heif_image> loadPNG(const char* filename)
{
  std::cerr << "Cannot load PNG because libpng support was not compiled.\n";
  exit(1);
  return nullptr;
}
#endif
void list_encoder_parameters(heif_encoder* encoder)
{
  std::cerr << "Parameters for encoder `" << heif_encoder_get_name(encoder) << "`:\n";
  const struct heif_encoder_parameter*const* params = heif_encoder_list_parameters(encoder);
  for (int i=0;params[i];i++) {
    const char* name = heif_encoder_parameter_get_name(params[i]);
    switch (heif_encoder_parameter_get_type(params[i])) {
    case heif_encoder_parameter_type_integer:
      {
        int value;
        heif_error error = heif_encoder_get_parameter_integer(encoder, name, &value);
        (void)error;
        std::cerr << "  " << name << ", default=" << value;
        int have_minmax, minimum, maximum;
        error = heif_encoder_parameter_integer_valid_range(encoder, name,
                                                           &have_minmax, &minimum, &maximum);
        if (have_minmax) {
          std::cerr << ", [" << minimum << ";" << maximum << "]";
        }
        std::cerr << "\n";
      }
      break;
    case heif_encoder_parameter_type_boolean:
      {
        int value;
        heif_error error = heif_encoder_get_parameter_boolean(encoder, name, &value);
        (void)error;
        std::cerr << "  " << name << ", default=" << (value ? "true":"false") << "\n";
      }
      break;
    case heif_encoder_parameter_type_string:
      {
        const int value_size = 50;
        char value[value_size];
        heif_error error = heif_encoder_get_parameter_string(encoder, name, value, value_size);
        (void)error;
        std::cerr << "  " << name << ", default=" << value;
        const char*const* valid_options;
        error = heif_encoder_parameter_string_valid_values(encoder, name, &valid_options);
        if (valid_options) {
          std::cerr << ", { ";
          for (int i=0;valid_options[i];i++) {
            if (i>0) { std::cerr << ","; }
            std::cerr << valid_options[i];
          }
          std::cerr << " }";
        }
        std::cerr << "\n";
      }
      break;
    }
  }
}
void set_params(struct heif_encoder* encoder, std::vector<std::string> params)
{
  for (std::string p : params) {
    auto pos = p.find_first_of('=');
    if (pos == std::string::npos || pos==0 || pos==p.size()-1) {
      std::cerr << "Encoder parameter must be in the format 'name=value'\n";
      exit(5);
    }
    std::string name = p.substr(0,pos);
    std::string value = p.substr(pos+1);
    struct heif_error error = heif_encoder_set_parameter(encoder, name.c_str(), value.c_str());
    if (error.code) {
      std::cerr << "Error: " << error.message << "\n";
      exit(5);
    }
  }
}
int main(int argc, char** argv)
{
  int quality = 50;
  bool lossless = false;
  std::string output_filename;
  int logging_level = 0;
  bool option_show_parameters = false;
  int thumbnail_bbox_size = 0;
  std::vector<std::string> raw_params;
  while (true) {
    int option_index = 0;
    int c = getopt_long(argc, argv, "hq:Lo:vPp:t:", long_options, &option_index);
    if (c == -1)
      break;
    switch (c) {
    case 'h':
      show_help(argv[0]);
      return 0;
    case 'q':
      quality = atoi(optarg);
      break;
    case 'L':
      lossless = true;
      break;
    case 'o':
      output_filename = optarg;
      break;
    case 'v':
      logging_level++;
      break;
    case 'P':
      option_show_parameters = true;
      break;
    case 'p':
      raw_params.push_back(optarg);
      break;
    case 't':
      thumbnail_bbox_size = atoi(optarg);
      break;
    }
  }
  if (optind > argc-1) {
    show_help(argv[0]);
    return 0;
  }
  if (quality<0 || quality>100) {
    std::cerr << "Invalid quality factor. Must be between 0 and 100.\n";
    return 5;
  }
  if (logging_level>0) {
    logging_level += 2;
    if (logging_level > 4) {
      logging_level = 4;
    }
  }
  
  std::shared_ptr<heif_context> context(heif_context_alloc(),
                                        [] (heif_context* c) { heif_context_free(c); });
  if (!context) {
    std::cerr << "Could not create HEIF context\n";
    return 1;
  }
  struct heif_encoder* encoder = nullptr;
#define MAX_ENCODERS 5
  const heif_encoder_descriptor* encoder_descriptors[MAX_ENCODERS];
  int count = heif_context_get_encoder_descriptors(context.get(), heif_compression_HEVC, nullptr,
                                                   encoder_descriptors, MAX_ENCODERS);
  if (count>0) {
    if (logging_level>0) {
      std::cerr << "Encoder: "
                << heif_encoder_descriptor_get_id_name(encoder_descriptors[0])
                << " = "
                << heif_encoder_descriptor_get_name(encoder_descriptors[0])
                << "\n";
    }
    heif_error error = heif_context_get_encoder(context.get(), encoder_descriptors[0], &encoder);
    if (error.code) {
      std::cerr << error.message << "\n";
      return 5;
    }
  }
  else {
    std::cerr << "No HEVC encoder available.\n";
    return 5;
  }
  if (option_show_parameters) {
    list_encoder_parameters(encoder);
    return 0;
  }
  struct heif_error error;
  for ( ; optind<argc ; optind++) {
    std::string input_filename = argv[optind];
    if (output_filename.empty()) {
      std::string filename_without_suffix;
      std::string::size_type dot_position = input_filename.find_last_of('.');
      if (dot_position != std::string::npos) {
        filename_without_suffix = input_filename.substr(0 , dot_position);
      }
      else {
        filename_without_suffix = input_filename;
      }
      output_filename = filename_without_suffix + ".heic";
    }
    
    
    std::string suffix;
    auto suffix_pos = input_filename.find_last_of('.');
    if (suffix_pos != std::string::npos) {
      suffix = input_filename.substr(suffix_pos+1);
      std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::tolower);
    }
    enum { PNG, JPEG } filetype = JPEG;
    if (suffix == "png") {
      filetype = PNG;
    }
    std::shared_ptr<heif_image> image;
    if (filetype==PNG) {
      image = loadPNG(input_filename.c_str());
    }
    else {
      image = loadJPEG(input_filename.c_str());
    }
    heif_encoder_set_lossy_quality(encoder, quality);
    heif_encoder_set_lossless(encoder, lossless);
    heif_encoder_set_logging_level(encoder, logging_level);
    set_params(encoder, raw_params);
    struct heif_encoding_options* options = heif_encoding_options_alloc();
    options->save_alpha_channel = (uint8_t)master_alpha;
    struct heif_image_handle* handle;
    error = heif_context_encode_image(context.get(),
                                      image.get(),
                                      encoder,
                                      options,
                                      &handle);
    if (error.code != 0) {
      std::cerr << "Could not read HEIF file: " << error.message << "\n";
      return 1;
    }
    if (thumbnail_bbox_size > 0)
      {
        
        struct heif_image_handle* thumbnail_handle;
        options->save_alpha_channel = master_alpha && thumb_alpha;
        error = heif_context_encode_thumbnail(context.get(),
                                              image.get(),
                                              handle,
                                              encoder,
                                              options,
                                              thumbnail_bbox_size,
                                              &thumbnail_handle);
        if (error.code) {
          std::cerr << "Could not generate thumbnail: " << error.message << "\n";
          return 5;
        }
        if (thumbnail_handle) {
          heif_image_handle_release(thumbnail_handle);
        }
      }
    heif_image_handle_release(handle);
  }
  heif_encoder_release(encoder);
  error = heif_context_write_to_file(context.get(), output_filename.c_str());
  if (error.code) {
    std::cerr << error.message << "\n";
    return 5;
  }
  return 0;
}