Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
e280ae
#ifdef _MSC_VER
Toshihiro Shimizu 890ddd
#pragma warning(disable : 4996)
Toshihiro Shimizu 890ddd
#endif
Toshihiro Shimizu 890ddd
//#include "texception.h"
Toshihiro Shimizu 890ddd
//#include "tfilepath.h"
Toshihiro Shimizu 890ddd
//#include "tiio_jpg.h"
Toshihiro Shimizu 890ddd
//#include "../compatibility/tnz4.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "tiio_jpg.h"
shun-iwasawa fdbab5
#include "tiio_jpg_exif.h"
Toshihiro Shimizu 890ddd
#include "tproperty.h"
Toshihiro Shimizu 890ddd
#include "tpixel.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*
Toshihiro Shimizu 890ddd
 * Include file for users of JPEG library.
Toshihiro Shimizu 890ddd
 * You will need to have included system headers that define at least
Toshihiro Shimizu 890ddd
 * the typedefs FILE and size_t before you can include jpeglib.h.
Toshihiro Shimizu 890ddd
 * (stdio.h is sufficient on ANSI-conforming systems.)
Toshihiro Shimizu 890ddd
 * You may also wish to include "jerror.h".
Toshihiro Shimizu 890ddd
 */
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include <assert.h></assert.h>
Toshihiro Shimizu 890ddd
#include <stdio.h></stdio.h>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=========================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka 3bfa54
const std::string Tiio::JpgWriterProperties::QUALITY("Quality");
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=========================================================
Toshihiro Shimizu 890ddd
Campbell Barton 8c6c57
extern "C" {
Campbell Barton 8c6c57
static void tnz_error_exit(j_common_ptr cinfo) {
Shinya Kitaoka 120a6e
  //  throw "merda";
Toshihiro Shimizu 890ddd
}
Campbell Barton 8c6c57
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#ifdef CICCIO
Toshihiro Shimizu 890ddd
JMETHOD(void, error_exit, (j_common_ptr cinfo));
Toshihiro Shimizu 890ddd
/* Conditionally emit a trace or warning message */
Toshihiro Shimizu 890ddd
JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level));
Toshihiro Shimizu 890ddd
/* Routine that actually outputs a trace or error message */
Toshihiro Shimizu 890ddd
JMETHOD(void, output_message, (j_common_ptr cinfo));
Toshihiro Shimizu 890ddd
/* Format a message string for the most recent JPEG error or message */
Toshihiro Shimizu 890ddd
JMETHOD(void, format_message, (j_common_ptr cinfo, char *buffer));
Toshihiro Shimizu 890ddd
#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */
Toshihiro Shimizu 890ddd
/* Reset error state variables at start of a new image */
Toshihiro Shimizu 890ddd
JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo));
Toshihiro Shimizu 890ddd
#endif
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
using namespace Tiio;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
JpgReader::JpgReader() : m_chan(0), m_isOpen(false) {
Shinya Kitaoka 120a6e
  memset(&m_cinfo, 0, sizeof m_cinfo);
Shinya Kitaoka 120a6e
  memset(&m_jerr, 0, sizeof m_jerr);
Shinya Kitaoka 120a6e
  memset(&m_buffer, 0, sizeof m_buffer);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
JpgReader::~JpgReader() {
Shinya Kitaoka 120a6e
  if (m_isOpen) {
Shinya Kitaoka 120a6e
    try {
Shinya Kitaoka 120a6e
      jpeg_finish_decompress(&m_cinfo);
Shinya Kitaoka 120a6e
      jpeg_destroy_decompress(&m_cinfo);
Shinya Kitaoka 120a6e
    } catch (...) {
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  if (m_chan) {
Shinya Kitaoka 120a6e
    m_chan = 0;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Tiio::RowOrder JpgReader::getRowOrder() const { return Tiio::TOP2BOTTOM; }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void JpgReader::open(FILE *file) {
Shinya Kitaoka 120a6e
  m_cinfo.err             = jpeg_std_error(&m_jerr);
Shinya Kitaoka 120a6e
  m_cinfo.err->error_exit = tnz_error_exit;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  jpeg_create_decompress(&m_cinfo);
shun-iwasawa fdbab5
Shinya Kitaoka 120a6e
  m_chan = file;
Shinya Kitaoka 120a6e
  jpeg_stdio_src(&m_cinfo, m_chan);
shun-iwasawa fdbab5
  jpeg_save_markers(&m_cinfo, JPEG_APP0 + 1, 0xffff);  // EXIF
Shinya Kitaoka 120a6e
  bool ret = jpeg_read_header(&m_cinfo, TRUE);
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  bool resolutionFoundInExif = false;
shun-iwasawa fdbab5
  jpeg_saved_marker_ptr mark;
shun-iwasawa fdbab5
  for (mark = m_cinfo.marker_list; NULL != mark; mark = mark->next) {
shun-iwasawa fdbab5
    switch (mark->marker) {
shun-iwasawa fdbab5
    case JPEG_APP0 + 1:  // EXIF
shun-iwasawa fdbab5
      JpgExifReader exifReader;
shun-iwasawa fdbab5
      exifReader.process_EXIF(mark->data - 2, mark->data_length);
shun-iwasawa fdbab5
      if (exifReader.containsResolution()) {
shun-iwasawa fdbab5
        int resUnit           = exifReader.getResolutionUnit();
shun-iwasawa fdbab5
        resolutionFoundInExif = true;
shun-iwasawa fdbab5
        if (resUnit == 1 || resUnit == 2) {  // no unit(1) or inch(2)
shun-iwasawa fdbab5
          m_info.m_dpix = (double)exifReader.getXResolution();
shun-iwasawa fdbab5
          m_info.m_dpiy = (double)exifReader.getYResolution();
shun-iwasawa fdbab5
        } else if (resUnit == 3) {  // centimeter(3);
shun-iwasawa fdbab5
          m_info.m_dpix = (double)exifReader.getXResolution() * 2.54;
shun-iwasawa fdbab5
          m_info.m_dpiy = (double)exifReader.getYResolution() * 2.54;
shun-iwasawa fdbab5
        } else  // ignore millimeter(4) and micrometer(5) cases for now
shun-iwasawa fdbab5
          resolutionFoundInExif = false;
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  ret = ret && jpeg_start_decompress(&m_cinfo);
Shinya Kitaoka 120a6e
  if (!ret) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int row_stride = m_cinfo.output_width * m_cinfo.output_components;
Shinya Kitaoka 120a6e
  m_buffer = (*m_cinfo.mem->alloc_sarray)((j_common_ptr)&m_cinfo, JPOOL_IMAGE,
Shinya Kitaoka 120a6e
                                          row_stride, 1);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_info.m_lx             = m_cinfo.output_width;
Shinya Kitaoka 120a6e
  m_info.m_ly             = m_cinfo.output_height;
Shinya Kitaoka 120a6e
  m_info.m_samplePerPixel = 3;
Shinya Kitaoka 120a6e
  m_info.m_valid          = true;
Shinya Kitaoka 120a6e
  m_isOpen                = true;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  if (!resolutionFoundInExif && (m_cinfo.saw_JFIF_marker != 0) &&
shun-iwasawa fdbab5
      (m_cinfo.X_density != 1) && (m_cinfo.Y_density != 1)) {
shun-iwasawa fdbab5
    if (m_cinfo.density_unit == 1) {
shun-iwasawa fdbab5
      m_info.m_dpix = (double)m_cinfo.X_density;
shun-iwasawa fdbab5
      m_info.m_dpiy = (double)m_cinfo.Y_density;
shun-iwasawa fdbab5
    } else if (m_cinfo.density_unit == 2) {
shun-iwasawa fdbab5
      m_info.m_dpix = (double)m_cinfo.X_density * 2.54;
shun-iwasawa fdbab5
      m_info.m_dpiy = (double)m_cinfo.Y_density * 2.54;
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void JpgReader::readLine(char *buffer, int x0, int x1, int shrink) {
Shinya Kitaoka 120a6e
  if (m_cinfo.out_color_space == JCS_RGB && m_cinfo.out_color_components == 3) {
Shinya Kitaoka 120a6e
    int ret = jpeg_read_scanlines(&m_cinfo, m_buffer, 1);
Shinya Kitaoka 120a6e
    assert(ret == 1);
Shinya Kitaoka 120a6e
    unsigned char *src = m_buffer[0];
Shinya Kitaoka 120a6e
    TPixel32 *dst      = (TPixel32 *)buffer;
Shinya Kitaoka 120a6e
    dst += x0;
Shinya Kitaoka 120a6e
    src += 3 * x0;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    int width           = (m_cinfo.output_width - 1) / shrink + 1;
Shinya Kitaoka 120a6e
    if (x1 >= x0) width = (x1 - x0) / shrink + 1;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    while (--width >= 0) {
Shinya Kitaoka 120a6e
      dst->r = src[0];
Shinya Kitaoka 120a6e
      dst->g = src[1];
Shinya Kitaoka 120a6e
      dst->b = src[2];
Shinya Kitaoka 120a6e
      dst->m = (char)255;
Shinya Kitaoka 120a6e
      src += 3 * shrink;
Shinya Kitaoka 120a6e
      dst += shrink;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  } else if (m_cinfo.out_color_components == 1) {
Shinya Kitaoka 120a6e
    int ret = jpeg_read_scanlines(&m_cinfo, m_buffer, 1);
Shinya Kitaoka 120a6e
    assert(ret == 1);
Shinya Kitaoka 120a6e
    unsigned char *src = m_buffer[0];
Shinya Kitaoka 120a6e
    TPixel32 *dst      = (TPixel32 *)buffer;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    dst += x0;
Shinya Kitaoka 120a6e
    src += x0;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    int width           = (m_cinfo.output_width - 1) / shrink + 1;
Shinya Kitaoka 120a6e
    if (x1 >= x0) width = (x1 - x0) / shrink + 1;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    while (--width >= 0) {
Shinya Kitaoka 120a6e
      dst->r = *src;
Shinya Kitaoka 120a6e
      dst->g = *src;
Shinya Kitaoka 120a6e
      dst->b = *src;
Shinya Kitaoka 120a6e
      dst->m = (char)255;
Shinya Kitaoka 120a6e
      src += shrink;
Shinya Kitaoka 120a6e
      dst += shrink;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
int JpgReader::skipLines(int lineCount) {
Shinya Kitaoka 120a6e
  for (int i = 0; i < lineCount; i++) {
Shinya Kitaoka 120a6e
    int ret = jpeg_read_scanlines(&m_cinfo, m_buffer, 1);
Shinya Kitaoka 120a6e
    assert(ret == 1);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  return lineCount;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
class JpgWriter final : public Tiio::Writer {
Shinya Kitaoka 120a6e
  struct jpeg_compress_struct m_cinfo;
Shinya Kitaoka 120a6e
  struct jpeg_error_mgr m_jerr;
Shinya Kitaoka 120a6e
  FILE *m_chan;
Shinya Kitaoka 120a6e
  JSAMPARRAY m_buffer;
Shinya Kitaoka 120a6e
  bool m_headerWritten;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  JpgWriter() : m_chan(0), m_headerWritten(false) {}
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  void open(FILE *file, const TImageInfo &info) override {
Shinya Kitaoka 120a6e
    m_cinfo.err = jpeg_std_error(&m_jerr);
Shinya Kitaoka 120a6e
    jpeg_create_compress(&m_cinfo);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    m_cinfo.image_width      = info.m_lx;
Shinya Kitaoka 120a6e
    m_cinfo.image_height     = info.m_ly;
Shinya Kitaoka 120a6e
    m_cinfo.input_components = 3;
Shinya Kitaoka 120a6e
    m_cinfo.in_color_space   = JCS_RGB;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    jpeg_set_defaults(&m_cinfo);
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    // save dpi always in JFIF header, instead of EXIF
shun-iwasawa fdbab5
    m_cinfo.write_JFIF_header  = 1;
shun-iwasawa fdbab5
    m_cinfo.JFIF_major_version = 1;
shun-iwasawa fdbab5
    m_cinfo.JFIF_minor_version = 2;
shun-iwasawa fdbab5
    m_cinfo.X_density          = (UINT16)info.m_dpix;
shun-iwasawa fdbab5
    m_cinfo.Y_density          = (UINT16)info.m_dpiy;
shun-iwasawa fdbab5
    m_cinfo.density_unit       = 1;  // dot per inch
shun-iwasawa fdbab5
    m_cinfo.write_Adobe_marker = 0;
shun-iwasawa fdbab5
Shinya Kitaoka 120a6e
    if (!m_properties) m_properties = new Tiio::JpgWriterProperties();
Shinya Kitaoka 120a6e
shun-iwasawa cfa6ff
    int quality =
shun-iwasawa cfa6ff
        ((TIntProperty *)(m_properties->getProperty("Quality")))->getValue();
shun-iwasawa cfa6ff
shun-iwasawa cfa6ff
    jpeg_set_quality(&m_cinfo, quality, TRUE);
Shinya Kitaoka 120a6e
    m_cinfo.smoothing_factor =
Shinya Kitaoka 120a6e
        ((TIntProperty *)(m_properties->getProperty("Smoothing")))->getValue();
Shinya Kitaoka 120a6e
shun-iwasawa cfa6ff
    // set horizontal and vertical chroma subsampling factor to encoder
shun-iwasawa cfa6ff
    // according to the quality value.
shun-iwasawa cfa6ff
    if (quality >= 70) {  // none chroma-subsampling (4:4:4)
shun-iwasawa cfa6ff
      m_cinfo.comp_info[0].h_samp_factor = 1;
shun-iwasawa cfa6ff
      m_cinfo.comp_info[0].v_samp_factor = 1;
shun-iwasawa cfa6ff
    } else if (quality >= 30) {  // medium chroma-subsampling (4:2:2)
shun-iwasawa cfa6ff
      m_cinfo.comp_info[0].h_samp_factor = 2;
shun-iwasawa cfa6ff
      m_cinfo.comp_info[0].v_samp_factor = 1;
shun-iwasawa cfa6ff
    } else {  // quality < 30, high chroma-subsampling (4:1:1)
shun-iwasawa cfa6ff
      m_cinfo.comp_info[0].h_samp_factor = 2;
shun-iwasawa cfa6ff
      m_cinfo.comp_info[0].v_samp_factor = 2;
shun-iwasawa cfa6ff
    }
shun-iwasawa cfa6ff
    m_cinfo.comp_info[1].h_samp_factor = 1;
shun-iwasawa cfa6ff
    m_cinfo.comp_info[1].v_samp_factor = 1;
shun-iwasawa cfa6ff
    m_cinfo.comp_info[2].h_samp_factor = 1;
shun-iwasawa cfa6ff
    m_cinfo.comp_info[2].v_samp_factor = 1;
shun-iwasawa cfa6ff
Shinya Kitaoka 120a6e
    int row_stride = m_cinfo.image_width * m_cinfo.input_components;
Shinya Kitaoka 120a6e
    m_buffer = (*m_cinfo.mem->alloc_sarray)((j_common_ptr)&m_cinfo, JPOOL_IMAGE,
Shinya Kitaoka 120a6e
                                            row_stride, 1);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    m_chan = file;
Shinya Kitaoka 120a6e
    jpeg_stdio_dest(&m_cinfo, m_chan);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  ~JpgWriter() {
Shinya Kitaoka 120a6e
    jpeg_finish_compress(&m_cinfo);
Shinya Kitaoka 120a6e
    jpeg_destroy_compress(&m_cinfo);
Shinya Kitaoka 120a6e
    delete m_properties;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  void flush() override { fflush(m_chan); }
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  Tiio::RowOrder getRowOrder() const override { return Tiio::TOP2BOTTOM; }
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  void writeLine(char *buffer) override {
Shinya Kitaoka 120a6e
    if (!m_headerWritten) {
Shinya Kitaoka 120a6e
      m_headerWritten = true;
Shinya Kitaoka 120a6e
      jpeg_start_compress(&m_cinfo, TRUE);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    TPixel32 *src      = (TPixel32 *)buffer;
Shinya Kitaoka 120a6e
    unsigned char *dst = m_buffer[0];
Shinya Kitaoka 120a6e
    int lx             = m_cinfo.image_width;
Shinya Kitaoka 120a6e
    while (--lx >= 0) {
Shinya Kitaoka 120a6e
      dst[0] = src->r;
Shinya Kitaoka 120a6e
      dst[1] = src->g;
Shinya Kitaoka 120a6e
      dst[2] = src->b;
Shinya Kitaoka 120a6e
      dst += 3;
Shinya Kitaoka 120a6e
      ++src;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    jpeg_write_scanlines(&m_cinfo, m_buffer, 1);
Shinya Kitaoka 120a6e
  }
shun-iwasawa 93fdd4
shun-iwasawa 93fdd4
  // jpeg format does not support alpha channel
shun-iwasawa 93fdd4
  bool writeAlphaSupported() const override { return false; }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----
shun-iwasawa e87e08
shun-iwasawa e87e08
void Tiio::JpgWriterProperties::updateTranslation() {
shun-iwasawa e87e08
  m_quality.setQStringName(tr("Quality"));
shun-iwasawa e87e08
  m_smoothing.setQStringName(tr("Smoothing"));
shun-iwasawa e87e08
}
shun-iwasawa e87e08
Toshihiro Shimizu 890ddd
//----
Toshihiro Shimizu 890ddd
//----
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
Tiio::Reader *Tiio::makeJpgReader() { return new JpgReader(); }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
Tiio::Writer *Tiio::makeJpgWriter() { return new JpgWriter(); }