2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-12-23 03:18:59 +00:00
qpdf/libqpdf/Pl_DCT.cc
2017-08-28 22:16:45 -04:00

224 lines
5.8 KiB
C++

#include <qpdf/Pl_DCT.hh>
#include <qpdf/QUtil.hh>
#include <setjmp.h>
#include <string>
#include <stdexcept>
#include <cstdlib>
#if BITS_IN_JSAMPLE != 8
# error "qpdf does not support libjpeg built with BITS_IN_JSAMPLE != 8"
#endif
struct qpdf_jpeg_error_mgr
{
struct jpeg_error_mgr pub;
jmp_buf jmpbuf;
std::string msg;
};
static void
error_handler(j_common_ptr cinfo)
{
qpdf_jpeg_error_mgr* jerr =
reinterpret_cast<qpdf_jpeg_error_mgr*>(cinfo->err);
char buf[JMSG_LENGTH_MAX];
(*cinfo->err->format_message)(cinfo, buf);
jerr->msg = buf;
longjmp(jerr->jmpbuf, 1);
}
Pl_DCT::Pl_DCT(char const* identifier, Pipeline* next) :
Pipeline(identifier, next),
action(a_decompress),
buf("DCT compressed image")
{
}
Pl_DCT::Pl_DCT(char const* identifier, Pipeline* next,
JDIMENSION image_width,
JDIMENSION image_height,
int components,
J_COLOR_SPACE color_space,
CompressConfig* config_callback) :
Pipeline(identifier, next),
action(a_compress),
buf("DCT uncompressed image"),
image_width(image_width),
image_height(image_height),
components(components),
color_space(color_space),
config_callback(config_callback)
{
}
Pl_DCT::~Pl_DCT()
{
}
void
Pl_DCT::write(unsigned char* data, size_t len)
{
this->buf.write(data, len);
}
void
Pl_DCT::finish()
{
this->buf.finish();
struct jpeg_compress_struct cinfo_compress;
struct jpeg_decompress_struct cinfo_decompress;
struct qpdf_jpeg_error_mgr jerr;
cinfo_compress.err = jpeg_std_error(&(jerr.pub));
cinfo_decompress.err = jpeg_std_error(&(jerr.pub));
jerr.pub.error_exit = error_handler;
bool error = false;
// Using a PointerHolder<Buffer> here and passing it into compress
// and decompress causes a memory leak with setjmp/longjmp. Just
// use a pointer and delete it.
Buffer* b = this->buf.getBuffer();
if (setjmp(jerr.jmpbuf) == 0)
{
if (this->action == a_compress)
{
compress(reinterpret_cast<void*>(&cinfo_compress), b);
}
else
{
decompress(reinterpret_cast<void*>(&cinfo_decompress), b);
}
}
else
{
error = true;
}
delete b;
if (this->action == a_compress)
{
jpeg_destroy_compress(&cinfo_compress);
}
if (this->action == a_decompress)
{
jpeg_destroy_decompress(&cinfo_decompress);
}
if (error)
{
throw std::runtime_error(jerr.msg);
}
}
class Freer
{
public:
Freer(unsigned char** p) :
p(p)
{
}
~Freer()
{
if (*p)
{
free(*p);
}
}
private:
unsigned char** p;
};
void
Pl_DCT::compress(void* cinfo_p, Buffer* b)
{
struct jpeg_compress_struct* cinfo =
reinterpret_cast<jpeg_compress_struct*>(cinfo_p);
#if ((defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) || \
defined(__clang__))
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
jpeg_create_compress(cinfo);
#if ((defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) || \
defined(__clang__))
# pragma GCC diagnostic pop
#endif
unsigned char* outbuffer = 0;
Freer freer(&outbuffer);
unsigned long outsize = 0;
jpeg_mem_dest(cinfo, &outbuffer, &outsize);
cinfo->image_width = this->image_width;
cinfo->image_height = this->image_height;
cinfo->input_components = this->components;
cinfo->in_color_space = this->color_space;
jpeg_set_defaults(cinfo);
if (this->config_callback)
{
this->config_callback->apply(cinfo);
}
jpeg_start_compress(cinfo, TRUE);
int width = cinfo->image_width * cinfo->input_components;
size_t expected_size =
cinfo->image_height * cinfo->image_width * cinfo->input_components;
if (b->getSize() != expected_size)
{
throw std::runtime_error(
"Pl_DCT: image buffer size = " +
QUtil::int_to_string(b->getSize()) + "; expected size = " +
QUtil::int_to_string(expected_size));
}
JSAMPROW row_pointer[1];
unsigned char* buffer = b->getBuffer();
while (cinfo->next_scanline < cinfo->image_height)
{
// We already verified that the buffer is big enough.
row_pointer[0] = &buffer[cinfo->next_scanline * width];
(void) jpeg_write_scanlines(cinfo, row_pointer, 1);
}
jpeg_finish_compress(cinfo);
this->getNext()->write(outbuffer, outsize);
this->getNext()->finish();
}
void
Pl_DCT::decompress(void* cinfo_p, Buffer* b)
{
struct jpeg_decompress_struct* cinfo =
reinterpret_cast<jpeg_decompress_struct*>(cinfo_p);
#if ((defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) || \
defined(__clang__))
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
jpeg_create_decompress(cinfo);
#if ((defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) || \
defined(__clang__))
# pragma GCC diagnostic pop
#endif
jpeg_mem_src(cinfo, b->getBuffer(), b->getSize());
(void) jpeg_read_header(cinfo, TRUE);
(void) jpeg_calc_output_dimensions(cinfo);
int width = cinfo->output_width * cinfo->output_components;
JSAMPARRAY buffer = (*cinfo->mem->alloc_sarray)
(reinterpret_cast<j_common_ptr>(cinfo), JPOOL_IMAGE, width, 1);
(void) jpeg_start_decompress(cinfo);
while (cinfo->output_scanline < cinfo->output_height)
{
(void) jpeg_read_scanlines(cinfo, buffer, 1);
this->getNext()->write(reinterpret_cast<unsigned char*>(buffer[0]),
width * sizeof(buffer[0][0]));
}
(void) jpeg_finish_decompress(cinfo);
this->getNext()->finish();
}