JpegWriter.cc 4.5 KB
Newer Older
Stefan Thomas's avatar
Stefan Thomas committed
1 2 3 4 5 6 7
//========================================================================
//
// JpegWriter.cc
//
// This file is licensed under the GPLv2 or later
//
// Copyright (C) 2009 Stefan Thomas <thomas@eload24.com>
Albert Astals Cid's avatar
Albert Astals Cid committed
8
// Copyright (C) 2010, 2012, 2017 Adrian Johnson <ajohnson@redneon.com>
9
// Copyright (C) 2010 Harry Roberts <harry.roberts@midnight-labs.org>
10
// Copyright (C) 2011 Thomas Freitag <Thomas.Freitag@alfa.de>
Peter Breitenlohner's avatar
Peter Breitenlohner committed
11
// Copyright (C) 2013 Peter Breitenlohner <peb@mppmu.mpg.de>
12
// Copyright (C) 2017, 2018 Albert Astals Cid <aacid@kde.org>
13
// Copyright (C) 2018 Martin Packman <gzlist@googlemail.com>
Stefan Thomas's avatar
Stefan Thomas committed
14 15 16 17 18 19 20
//
//========================================================================

#include "JpegWriter.h"

#ifdef ENABLE_LIBJPEG

21 22 23 24
extern "C" {
#include <jpeglib.h>
}

Stefan Thomas's avatar
Stefan Thomas committed
25 26
#include "poppler/Error.h"

27 28
struct JpegWriterPrivate {
  bool progressive;
29
  bool optimize;
30 31 32 33 34 35
  int quality;
  JpegWriter::Format format;
  struct jpeg_compress_struct cinfo;
  struct jpeg_error_mgr jerr;
};

36
static void outputMessage(j_common_ptr cinfo)
Stefan Thomas's avatar
Stefan Thomas committed
37
{
38
  char buffer[JMSG_LENGTH_MAX];
Stefan Thomas's avatar
Stefan Thomas committed
39

40 41
  // Create the message
  (*cinfo->err->format_message) (cinfo, buffer);
Stefan Thomas's avatar
Stefan Thomas committed
42

43 44
  // Send it to poppler's error handler
  error(errInternal, -1, "{0:s}", buffer);
Stefan Thomas's avatar
Stefan Thomas committed
45 46
}

47
JpegWriter::JpegWriter(int q, bool p, Format formatA)
48
{
49 50
  priv = new JpegWriterPrivate;
  priv->progressive = p;
51
  priv->optimize = false;
52 53
  priv->quality = q;
  priv->format = formatA;
54 55
}

56
JpegWriter::JpegWriter(Format formatA)
57
 : JpegWriter(-1, false, formatA)
Stefan Thomas's avatar
Stefan Thomas committed
58 59 60 61 62
{
}

JpegWriter::~JpegWriter()
{
63
  // cleanup
64 65
  jpeg_destroy_compress(&priv->cinfo);
  delete priv;
Stefan Thomas's avatar
Stefan Thomas committed
66 67
}

68 69 70 71 72 73 74 75 76 77
void JpegWriter::setQuality(int quality)
{
  priv->quality = quality;
}

void JpegWriter::setProgressive(bool progressive)
{
  priv->progressive = progressive;
}

78 79 80 81 82
void JpegWriter::setOptimize(bool optimize)
{
  priv->optimize = optimize;
}

83
bool JpegWriter::init(FILE *f, int width, int height, int hDPI, int vDPI)
Stefan Thomas's avatar
Stefan Thomas committed
84
{
85
  // Setup error handler
86 87
  priv->cinfo.err = jpeg_std_error(&priv->jerr);
  priv->jerr.output_message = &outputMessage;
88 89

  // Initialize libjpeg
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
  jpeg_create_compress(&priv->cinfo);

  // First set colorspace and call jpeg_set_defaults() since
  // jpeg_set_defaults() sets default values for all fields in
  // cinfo based on the colorspace.
  switch (priv->format) {
    case RGB:
      priv->cinfo.in_color_space = JCS_RGB;
      break;
    case GRAY:
      priv->cinfo.in_color_space = JCS_GRAYSCALE;
      break;
    case CMYK:
      priv->cinfo.in_color_space = JCS_CMYK;
      break;
    default:
      return false;
  }
  jpeg_set_defaults(&priv->cinfo);
109 110

  // Set destination file
111
  jpeg_stdio_dest(&priv->cinfo, f);
112 113

  // Set libjpeg configuration
114 115 116 117 118 119 120 121
  priv->cinfo.image_width = width;
  priv->cinfo.image_height = height;
  priv->cinfo.density_unit = 1; // dots per inch
  priv->cinfo.X_density = hDPI;
  priv->cinfo.Y_density = vDPI;
  switch (priv->format) {
    case GRAY:
      priv->cinfo.input_components = 1;
122
      break;
123 124
    case RGB:
      priv->cinfo.input_components = 3;
125
      break;
126 127 128 129
    case CMYK:
      priv->cinfo.input_components = 4;
      jpeg_set_colorspace(&priv->cinfo, JCS_YCCK);
      priv->cinfo.write_JFIF_header = TRUE;
130 131 132 133 134 135
      break;
    default:
      return false;
  }

  // Set quality
136
  if (priv->quality >= 0 && priv->quality <= 100) {
Peter Breitenlohner's avatar
Peter Breitenlohner committed
137
    jpeg_set_quality(&priv->cinfo, priv->quality, TRUE);
138 139 140
  }

  // Use progressive mode
141 142
  if (priv->progressive) {
    jpeg_simple_progression(&priv->cinfo);
143 144
  }

145 146 147
  // Set whether to compute optimal Huffman coding tables
  priv->cinfo.optimize_coding = priv->optimize;

148
  // Get ready for data
149
  jpeg_start_compress(&priv->cinfo, TRUE);
150 151

  return true;
Stefan Thomas's avatar
Stefan Thomas committed
152 153 154 155
}

bool JpegWriter::writePointers(unsigned char **rowPointers, int rowCount)
{
156
  if (priv->format == CMYK) {
157 158
    for (int y = 0; y < rowCount; y++) {
      unsigned char *row = rowPointers[y];
159
      for (unsigned int x = 0; x < priv->cinfo.image_width; x++) {
160 161 162
	for (int n = 0; n < 4; n++) {
	  *row = 0xff - *row;
	  row++;
163
	}
164 165 166 167
      }
    }
  }
  // Write all rows to the file
168
  jpeg_write_scanlines(&priv->cinfo, rowPointers, rowCount);
169 170

  return true;
Stefan Thomas's avatar
Stefan Thomas committed
171 172
}

173
bool JpegWriter::writeRow(unsigned char **rowPointer)
Stefan Thomas's avatar
Stefan Thomas committed
174
{
175
  if (priv->format == CMYK) {
176
    unsigned char *row = rowPointer[0];
177
    for (unsigned int x = 0; x < priv->cinfo.image_width; x++) {
178 179 180 181 182 183 184
      for (int n = 0; n < 4; n++) {
	*row = 0xff - *row;
	row++;
      }
    }
  }
  // Write the row to the file
185
  jpeg_write_scanlines(&priv->cinfo, rowPointer, 1);
186 187

  return true;
Stefan Thomas's avatar
Stefan Thomas committed
188 189 190 191
}

bool JpegWriter::close()
{
192
  jpeg_finish_compress(&priv->cinfo);
193 194

  return true;
Stefan Thomas's avatar
Stefan Thomas committed
195 196
}

197 198 199 200 201 202
bool JpegWriter::supportCMYK()
{
  return priv->format == CMYK;
}


Stefan Thomas's avatar
Stefan Thomas committed
203
#endif