//======================================================================== // // DCTStream.cc // // This file is licensed under the GPLv2 or later // // Copyright 2005 Jeff Muizelaar // Copyright 2005-2010, 2012, 2017 Albert Astals Cid // Copyright 2009 Ryszard Trojnacki // Copyright 2010 Carlos Garcia Campos // Copyright 2011 Daiki Ueno // Copyright 2011 Tomas Hoger // Copyright 2012, 2013 Thomas Freitag // Copyright 2017 Adrian Johnson // //======================================================================== #include "DCTStream.h" 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(); if (c != EOF) { src->buffer = c; src->pub.next_input_byte = &src->buffer; src->pub.bytes_in_buffer = 1; 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); } } } // we can not go with inline since gcc // refuses to inline because of setjmp #define DO_GET_CHAR \ if (current == limit) { \ if (cinfo.output_scanline < cinfo.output_height) { \ if (!setjmp(err.setjmp_buffer)) { \ if (!jpeg_read_scanlines(&cinfo, row_buffer, 1)) \ c = EOF; \ else { \ current = &row_buffer[0][0]; \ limit = &row_buffer[0][(cinfo.output_width - 1) * cinfo.output_components] + cinfo.output_components; \ c = *current; \ ++current; \ } \ } else \ c = EOF; \ } else \ c = EOF; \ } else { \ c = *current; \ ++current; \ } int DCTStream::getChar() { int c; DO_GET_CHAR return c; } int DCTStream::getChars(int nChars, unsigned char *buffer) { // Use volatile to prevent the compiler optimizing // variables into registers. See setjmp man page. volatile int i, c; for (i = 0; i < nChars; ++i) { DO_GET_CHAR if (likely(c != EOF)) buffer[i] = c; else return i; } 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) { return str->isBinary(true); }