DCTStream.cc 6.49 KB
Newer Older
1 2 3 4
//========================================================================
//
// DCTStream.cc
//
5 6
// This file is licensed under the GPLv2 or later
//
Albert Astals Cid's avatar
Albert Astals Cid committed
7
// Copyright 2005 Jeff Muizelaar <jeff@infidigm.net>
8
// Copyright 2005-2010, 2012, 2017 Albert Astals Cid <aacid@kde.org>
9
// Copyright 2009 Ryszard Trojnacki <rysiek@menel.com>
Albert Astals Cid's avatar
Albert Astals Cid committed
10
// Copyright 2010 Carlos Garcia Campos <carlosgc@gnome.org>
11
// Copyright 2011 Daiki Ueno <ueno@unixuser.org>
12
// Copyright 2011 Tomas Hoger <thoger@redhat.com>
13
// Copyright 2012, 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
14
// Copyright 2017 Adrian Johnson <ajohnson@redneon.com>
15 16 17 18 19 20 21 22 23 24 25
//
//========================================================================

#include "DCTStream.h"

static void str_init_source(j_decompress_ptr cinfo)
{
}

static boolean str_fill_input_buffer(j_decompress_ptr cinfo)
{
26
  int c;
27
  struct str_src_mgr * src = (struct str_src_mgr *)cinfo->src;
28
  if (src->index == 0) {
29
    c = 0xFF;
30 31 32
    src->index++;
  }
  else if (src->index == 1) {
33
    c = 0xD8;
34 35
    src->index++;
  }
36 37 38 39 40 41 42 43 44
  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;
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
}

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)
{
}

64
DCTStream::DCTStream(Stream *strA, int colorXformA, Dict *dict, int recursion) :
65
  FilterStream(strA) {
66
  colorXform = colorXformA;
67
  if (dict != nullptr) {
Albert Astals Cid's avatar
Albert Astals Cid committed
68
    Object obj = dict->lookup("Width", recursion);
69
    err.width = (obj.isInt() && obj.getInt() <= JPEG_MAX_DIMENSION) ? obj.getInt() : 0;
Albert Astals Cid's avatar
Albert Astals Cid committed
70
    obj = dict->lookup("Height", recursion);
71 72 73
    err.height = (obj.isInt() && obj.getInt() <= JPEG_MAX_DIMENSION) ? obj.getInt() : 0;
  } else
    err.height = err.width = 0;
74 75 76 77 78 79 80 81
  init();
}

DCTStream::~DCTStream() {
  jpeg_destroy_decompress(&cinfo);
  delete str;
}

Albert Astals Cid's avatar
Albert Astals Cid committed
82
static void exitErrorHandler(jpeg_common_struct *error) {
83
  j_decompress_ptr cinfo = (j_decompress_ptr)error;
84
  str_error_mgr * err = (struct str_error_mgr *)cinfo->err;
85 86 87 88 89 90
  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);
  }
91 92
}

93 94
void DCTStream::init()
{
95 96
  jpeg_std_error(&err.pub);
  err.pub.error_exit = &exitErrorHandler;
97 98 99 100 101 102
  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;
103
  src.pub.next_input_byte = nullptr;
104
  src.str = str;
105
  src.index = 0;
106 107
  current = nullptr;
  limit = nullptr;
108
  
109 110 111 112 113
  cinfo.err = &err.pub;
  if (!setjmp(err.setjmp_buffer)) {
    jpeg_create_decompress(&cinfo);
    cinfo.src = (jpeg_source_mgr *)&src;
  }
114
  row_buffer = nullptr;
115 116 117 118 119 120
}

void DCTStream::reset() {
  int row_stride;

  str->reset();
121 122 123 124 125 126 127

  if (row_buffer)
  {
    jpeg_destroy_decompress(&cinfo);
    init();
  }

128 129 130 131 132 133 134 135 136 137 138 139 140 141
  // 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)
      {
142
        error(errSyntaxError, -1, "Could not find start of jpeg data");
143
        return;
144
      }
145
      if (c != 0xFF) c = 0;
146 147 148 149 150 151 152 153 154 155 156 157 158
    }
    else
    {
      c2 = str->getChar();
      if (c2 != 0xD8)
      {
        c = 0;
        c2 = 0;
      }
      else startFound = true;
    }
  }

159
  if (!setjmp(err.setjmp_buffer))
160
  {
161 162 163 164 165 166 167 168 169 170 171 172 173 174
    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;
	  }
175
	} else {
176
	  colorXform = 0;
177
	}
178 179
      } else if (cinfo.saw_Adobe_marker) {
	colorXform = cinfo.Adobe_transform;
180 181
      }

182 183 184 185 186 187 188 189
      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;
      }
190

191
      jpeg_start_decompress(&cinfo);
192

193 194 195
      row_stride = cinfo.output_width * cinfo.output_components;
      row_buffer = cinfo.mem->alloc_sarray((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
    }
196
  }
197 198
}

199 200 201 202 203 204
// 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) \
    { \
205
      if (!setjmp(err.setjmp_buffer)) \
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
      { \
        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; \
  } \

223 224 225
int DCTStream::getChar() {
  int c;

226 227
  DO_GET_CHAR
  
228 229 230
  return c;
}

231
int DCTStream::getChars(int nChars, Guchar *buffer) {
Adrian Johnson's avatar
Adrian Johnson committed
232 233 234 235
  // Use volatile to prevent the compiler optimizing
  // variables into registers. See setjmp man page.
  volatile int i, c;
  for (i = 0; i < nChars; ++i) {
236 237 238 239 240 241 242
    DO_GET_CHAR
    if (likely(c != EOF)) buffer[i] = c;
    else return i;
  }
  return nChars;
}

243
int DCTStream::lookChar() {
244
  if (unlikely(current == nullptr)) {
245 246
    return EOF;
  }
Albert Astals Cid's avatar
Albert Astals Cid committed
247
  return *current;
248 249
}

250
GooString *DCTStream::getPSFilter(int psLevel, const char *indent) {
251 252 253
  GooString *s;

  if (psLevel < 2) {
254
    return nullptr;
255 256
  }
  if (!(s = str->getPSFilter(psLevel, indent))) {
257
    return nullptr;
258 259 260 261 262 263 264 265
  }
  s->append(indent)->append("<< >> /DCTDecode filter\n");
  return s;
}

GBool DCTStream::isBinary(GBool last) {
  return str->isBinary(gTrue);
}