aboutsummaryrefslogtreecommitdiffstats
path: root/libqpdf/Pl_DCT.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libqpdf/Pl_DCT.cc')
-rw-r--r--libqpdf/Pl_DCT.cc152
1 files changed, 130 insertions, 22 deletions
diff --git a/libqpdf/Pl_DCT.cc b/libqpdf/Pl_DCT.cc
index b341939e..0853f01d 100644
--- a/libqpdf/Pl_DCT.cc
+++ b/libqpdf/Pl_DCT.cc
@@ -1,6 +1,7 @@
#include <qpdf/Pl_DCT.hh>
#include <qpdf/QUtil.hh>
+#include <qpdf/QTC.hh>
#include <setjmp.h>
#include <string>
#include <stdexcept>
@@ -80,15 +81,28 @@ Pl_DCT::finish()
// and decompress causes a memory leak with setjmp/longjmp. Just
// use a pointer and delete it.
Buffer* b = this->buf.getBuffer();
+ // The jpeg library is a "C" library, so we use setjmp and longjmp
+ // for exceptoin handling.
if (setjmp(jerr.jmpbuf) == 0)
{
- if (this->action == a_compress)
+ try
{
- compress(reinterpret_cast<void*>(&cinfo_compress), b);
+ if (this->action == a_compress)
+ {
+ compress(reinterpret_cast<void*>(&cinfo_compress), b);
+ }
+ else
+ {
+ decompress(reinterpret_cast<void*>(&cinfo_decompress), b);
+ }
}
- else
+ catch (std::exception& e)
{
- decompress(reinterpret_cast<void*>(&cinfo_decompress), b);
+ // Convert an exception back to a longjmp so we can ensure
+ // that the right cleanup happens. This will get converted
+ // back to an exception.
+ jerr.msg = e.what();
+ longjmp(jerr.jmpbuf, 1);
}
}
else
@@ -111,24 +125,118 @@ Pl_DCT::finish()
}
}
-class Freer
+struct dct_pipeline_dest
{
- public:
- Freer(unsigned char** p) :
- p(p)
+ struct jpeg_destination_mgr pub; /* public fields */
+ unsigned char* buffer;
+ size_t size;
+ Pipeline* next;
+};
+
+static void
+init_pipeline_destination(j_compress_ptr)
+{
+}
+
+static int
+empty_pipeline_output_buffer(j_compress_ptr cinfo)
+{
+ QTC::TC("libtests", "Pl_DCT empty_pipeline_output_buffer");
+ dct_pipeline_dest* dest =
+ reinterpret_cast<dct_pipeline_dest*>(cinfo->dest);
+ dest->next->write(dest->buffer, dest->size);
+ dest->pub.next_output_byte = dest->buffer;
+ dest->pub.free_in_buffer = dest->size;
+ return TRUE;
+}
+
+static void
+term_pipeline_destination(j_compress_ptr cinfo)
+{
+ QTC::TC("libtests", "Pl_DCT term_pipeline_destination");
+ dct_pipeline_dest* dest =
+ reinterpret_cast<dct_pipeline_dest*>(cinfo->dest);
+ dest->next->write(dest->buffer, dest->size - dest->pub.free_in_buffer);
+}
+
+static void
+jpeg_pipeline_dest(j_compress_ptr cinfo,
+ unsigned char* outbuffer, size_t size,
+ Pipeline* next)
+{
+ cinfo->dest = static_cast<struct jpeg_destination_mgr *>(
+ (*cinfo->mem->alloc_small)(reinterpret_cast<j_common_ptr>(cinfo),
+ JPOOL_PERMANENT,
+ sizeof(dct_pipeline_dest)));
+ dct_pipeline_dest* dest =
+ reinterpret_cast<dct_pipeline_dest*>(cinfo->dest);
+ dest->pub.init_destination = init_pipeline_destination;
+ dest->pub.empty_output_buffer = empty_pipeline_output_buffer;
+ dest->pub.term_destination = term_pipeline_destination;
+ dest->pub.next_output_byte = dest->buffer = outbuffer;
+ dest->pub.free_in_buffer = dest->size = size;
+ dest->next = next;
+}
+
+static void
+init_buffer_source(j_decompress_ptr)
+{
+}
+
+static int
+fill_buffer_input_buffer(j_decompress_ptr)
+{
+ // The whole JPEG data is expected to reside in the supplied memory
+ // buffer, so any request for more data beyond the given buffer size
+ // is treated as an error.
+ throw std::runtime_error("invalid jpeg data reading from buffer");
+ return TRUE;
+}
+
+static void
+skip_buffer_input_data(j_decompress_ptr cinfo, long num_bytes)
+{
+ if (num_bytes < 0)
{
+ throw std::runtime_error(
+ "reading jpeg: jpeg library requested"
+ " skipping a negative number of bytes");
}
- ~Freer()
+ size_t to_skip = static_cast<size_t>(num_bytes);
+ if ((to_skip > 0) && (to_skip <= cinfo->src->bytes_in_buffer))
{
- if (*p)
- {
- free(*p);
- }
+ cinfo->src->next_input_byte += to_skip;
+ cinfo->src->bytes_in_buffer -= to_skip;
+ }
+ else if (to_skip != 0)
+ {
+ cinfo->src->next_input_byte += cinfo->src->bytes_in_buffer;
+ cinfo->src->bytes_in_buffer = 0;
}
+}
- private:
- unsigned char** p;
-};
+static void
+term_buffer_source(j_decompress_ptr)
+{
+}
+
+static void
+jpeg_buffer_src(j_decompress_ptr cinfo, Buffer* buffer)
+{
+ cinfo->src = reinterpret_cast<jpeg_source_mgr *>(
+ (*cinfo->mem->alloc_small)(reinterpret_cast<j_common_ptr>(cinfo),
+ JPOOL_PERMANENT,
+ sizeof(jpeg_source_mgr)));
+
+ jpeg_source_mgr* src = cinfo->src;
+ src->init_source = init_buffer_source;
+ src->fill_input_buffer = fill_buffer_input_buffer;
+ src->skip_input_data = skip_buffer_input_data;
+ src->resync_to_restart = jpeg_resync_to_restart; /* use default method */
+ src->term_source = term_buffer_source;
+ src->bytes_in_buffer = buffer->getSize();
+ src->next_input_byte = buffer->getBuffer();
+}
void
Pl_DCT::compress(void* cinfo_p, Buffer* b)
@@ -146,10 +254,11 @@ Pl_DCT::compress(void* cinfo_p, Buffer* b)
defined(__clang__))
# pragma GCC diagnostic pop
#endif
- unsigned char* outbuffer = 0;
- Freer freer(&outbuffer);
- unsigned long outsize = 0;
- jpeg_mem_dest(cinfo, &outbuffer, &outsize);
+ static int const BUF_SIZE = 65536;
+ PointerHolder<unsigned char> outbuffer_ph(
+ true, new unsigned char[BUF_SIZE]);
+ unsigned char* outbuffer = outbuffer_ph.getPointer();
+ jpeg_pipeline_dest(cinfo, outbuffer, BUF_SIZE, this->getNext());
cinfo->image_width = this->image_width;
cinfo->image_height = this->image_height;
@@ -182,7 +291,6 @@ Pl_DCT::compress(void* cinfo_p, Buffer* b)
(void) jpeg_write_scanlines(cinfo, row_pointer, 1);
}
jpeg_finish_compress(cinfo);
- this->getNext()->write(outbuffer, outsize);
this->getNext()->finish();
}
@@ -202,7 +310,7 @@ Pl_DCT::decompress(void* cinfo_p, Buffer* b)
defined(__clang__))
# pragma GCC diagnostic pop
#endif
- jpeg_mem_src(cinfo, b->getBuffer(), b->getSize());
+ jpeg_buffer_src(cinfo, b);
(void) jpeg_read_header(cinfo, TRUE);
(void) jpeg_calc_output_dimensions(cinfo);