Spaces:
Running
Running
//======================================================================== | |
// | |
// DCTStream.cc | |
// | |
// This file is licensed under the GPLv2 or later | |
// | |
// Copyright 2005 Jeff Muizelaar <[email protected]> | |
// Copyright 2005-2010, 2012, 2017, 2020-2023 Albert Astals Cid <[email protected]> | |
// Copyright 2009 Ryszard Trojnacki <[email protected]> | |
// Copyright 2010 Carlos Garcia Campos <[email protected]> | |
// Copyright 2011 Daiki Ueno <[email protected]> | |
// Copyright 2011 Tomas Hoger <[email protected]> | |
// Copyright 2012, 2013 Thomas Freitag <[email protected]> | |
// Copyright 2017 Adrian Johnson <[email protected]> | |
// Copyright 2020 Lluís Batlle i Rossell <[email protected]> | |
// | |
//======================================================================== | |
static void str_init_source(j_decompress_ptr cinfo) { } | |
static boolean str_fill_input_buffer(j_decompress_ptr cinfo) | |
{ | |
int c; | |
struct str_src_mgr *src = (struct str_src_mgr *)cinfo->src; | |
if (src->index == 0) { | |
c = 0xFF; | |
src->index++; | |
} else if (src->index == 1) { | |
c = 0xD8; | |
src->index++; | |
} else { | |
c = src->str->getChar(); | |
} | |
src->buffer = c; | |
src->pub.next_input_byte = &src->buffer; | |
src->pub.bytes_in_buffer = 1; | |
if (c != EOF) { | |
return TRUE; | |
} else { | |
return FALSE; | |
} | |
} | |
static void str_skip_input_data(j_decompress_ptr cinfo, long num_bytes) | |
{ | |
struct str_src_mgr *src = (struct str_src_mgr *)cinfo->src; | |
if (num_bytes > 0) { | |
while (num_bytes > (long)src->pub.bytes_in_buffer) { | |
num_bytes -= (long)src->pub.bytes_in_buffer; | |
str_fill_input_buffer(cinfo); | |
} | |
src->pub.next_input_byte += (size_t)num_bytes; | |
src->pub.bytes_in_buffer -= (size_t)num_bytes; | |
} | |
} | |
static void str_term_source(j_decompress_ptr cinfo) { } | |
DCTStream::DCTStream(Stream *strA, int colorXformA, Dict *dict, int recursion) : FilterStream(strA) | |
{ | |
colorXform = colorXformA; | |
if (dict != nullptr) { | |
Object obj = dict->lookup("Width", recursion); | |
err.width = (obj.isInt() && obj.getInt() <= JPEG_MAX_DIMENSION) ? obj.getInt() : 0; | |
obj = dict->lookup("Height", recursion); | |
err.height = (obj.isInt() && obj.getInt() <= JPEG_MAX_DIMENSION) ? obj.getInt() : 0; | |
} else { | |
err.height = err.width = 0; | |
} | |
init(); | |
} | |
DCTStream::~DCTStream() | |
{ | |
jpeg_destroy_decompress(&cinfo); | |
delete str; | |
} | |
static void exitErrorHandler(jpeg_common_struct *error) | |
{ | |
j_decompress_ptr cinfo = (j_decompress_ptr)error; | |
str_error_mgr *err = (struct str_error_mgr *)cinfo->err; | |
if (cinfo->err->msg_code == JERR_IMAGE_TOO_BIG && err->width != 0 && err->height != 0) { | |
cinfo->image_height = err->height; | |
cinfo->image_width = err->width; | |
} else { | |
longjmp(err->setjmp_buffer, 1); | |
} | |
} | |
void DCTStream::init() | |
{ | |
jpeg_std_error(&err.pub); | |
err.pub.error_exit = &exitErrorHandler; | |
src.pub.init_source = str_init_source; | |
src.pub.fill_input_buffer = str_fill_input_buffer; | |
src.pub.skip_input_data = str_skip_input_data; | |
src.pub.resync_to_restart = jpeg_resync_to_restart; | |
src.pub.term_source = str_term_source; | |
src.pub.bytes_in_buffer = 0; | |
src.pub.next_input_byte = nullptr; | |
src.str = str; | |
src.index = 0; | |
current = nullptr; | |
limit = nullptr; | |
cinfo.err = &err.pub; | |
if (!setjmp(err.setjmp_buffer)) { | |
jpeg_create_decompress(&cinfo); | |
cinfo.src = (jpeg_source_mgr *)&src; | |
} | |
row_buffer = nullptr; | |
} | |
void DCTStream::reset() | |
{ | |
int row_stride; | |
str->reset(); | |
if (row_buffer) { | |
jpeg_destroy_decompress(&cinfo); | |
init(); | |
} | |
// JPEG data has to start with 0xFF 0xD8 | |
// but some pdf like the one on | |
// https://bugs.freedesktop.org/show_bug.cgi?id=3299 | |
// does have some garbage before that this seeks for | |
// the start marker... | |
bool startFound = false; | |
int c = 0, c2 = 0; | |
while (!startFound) { | |
if (!c) { | |
c = str->getChar(); | |
if (c == -1) { | |
error(errSyntaxError, -1, "Could not find start of jpeg data"); | |
return; | |
} | |
if (c != 0xFF) { | |
c = 0; | |
} | |
} else { | |
c2 = str->getChar(); | |
if (c2 != 0xD8) { | |
c = 0; | |
c2 = 0; | |
} else { | |
startFound = true; | |
} | |
} | |
} | |
if (!setjmp(err.setjmp_buffer)) { | |
if (jpeg_read_header(&cinfo, TRUE) != JPEG_SUSPENDED) { | |
// figure out color transform | |
if (colorXform == -1 && !cinfo.saw_Adobe_marker) { | |
if (cinfo.num_components == 3) { | |
if (cinfo.saw_JFIF_marker) { | |
colorXform = 1; | |
} else if (cinfo.cur_comp_info[0]->component_id == 82 && cinfo.cur_comp_info[1]->component_id == 71 && cinfo.cur_comp_info[2]->component_id == 66) { // ASCII "RGB" | |
colorXform = 0; | |
} else { | |
colorXform = 1; | |
} | |
} else { | |
colorXform = 0; | |
} | |
} else if (cinfo.saw_Adobe_marker) { | |
colorXform = cinfo.Adobe_transform; | |
} | |
switch (cinfo.num_components) { | |
case 3: | |
cinfo.jpeg_color_space = colorXform ? JCS_YCbCr : JCS_RGB; | |
break; | |
case 4: | |
cinfo.jpeg_color_space = colorXform ? JCS_YCCK : JCS_CMYK; | |
break; | |
} | |
jpeg_start_decompress(&cinfo); | |
row_stride = cinfo.output_width * cinfo.output_components; | |
row_buffer = cinfo.mem->alloc_sarray((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1); | |
} | |
} | |
} | |
bool DCTStream::readLine() | |
{ | |
if (cinfo.output_scanline < cinfo.output_height) { | |
if (!setjmp(err.setjmp_buffer)) { | |
if (!jpeg_read_scanlines(&cinfo, row_buffer, 1)) { | |
return false; | |
} else { | |
current = &row_buffer[0][0]; | |
limit = &row_buffer[0][(cinfo.output_width - 1) * cinfo.output_components] + cinfo.output_components; | |
return true; | |
} | |
} else { | |
return false; | |
} | |
} else { | |
return false; | |
} | |
} | |
int DCTStream::getChar() | |
{ | |
if (current == limit) { | |
if (!readLine()) { | |
return EOF; | |
} | |
} | |
return *current++; | |
} | |
int DCTStream::getChars(int nChars, unsigned char *buffer) | |
{ | |
for (int i = 0; i < nChars;) { | |
if (current == limit) { | |
if (!readLine()) { | |
return i; | |
} | |
} | |
intptr_t left = limit - current; | |
if (left + i > nChars) { | |
left = nChars - i; | |
} | |
memcpy(buffer + i, current, left); | |
current += left; | |
i += static_cast<int>(left); | |
} | |
return nChars; | |
} | |
int DCTStream::lookChar() | |
{ | |
if (unlikely(current == nullptr)) { | |
return EOF; | |
} | |
return *current; | |
} | |
GooString *DCTStream::getPSFilter(int psLevel, const char *indent) | |
{ | |
GooString *s; | |
if (psLevel < 2) { | |
return nullptr; | |
} | |
if (!(s = str->getPSFilter(psLevel, indent))) { | |
return nullptr; | |
} | |
s->append(indent)->append("<< >> /DCTDecode filter\n"); | |
return s; | |
} | |
bool DCTStream::isBinary(bool last) const | |
{ | |
return str->isBinary(true); | |
} | |