Esempio n. 1
0
static bool jpeg_get_dimensions(FILE *f,  // or:
                                const void *buf, uint32_t buflen,
                                int32_t *w, int32_t *h,
                                GError **err) {
  bool result = false;
  struct jpeg_decompress_struct cinfo;
  struct _openslide_jpeg_error_mgr jerr;
  jmp_buf env;

  if (setjmp(env) == 0) {
    cinfo.err = _openslide_jpeg_set_error_handler(&jerr, &env);
    jpeg_create_decompress(&cinfo);

    if (f) {
      _openslide_jpeg_stdio_src(&cinfo, f);
    } else {
      _openslide_jpeg_mem_src(&cinfo, (void *) buf, buflen);
    }

    int header_result = jpeg_read_header(&cinfo, TRUE);
    if ((header_result != JPEG_HEADER_OK
	 && header_result != JPEG_HEADER_TABLES_ONLY)) {
      g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_FAILED,
                  "Couldn't read JPEG header");
      goto DONE;
    }

    jpeg_calc_output_dimensions(&cinfo);

    *w = cinfo.output_width;
    *h = cinfo.output_height;
    result = true;
  } else {
    // setjmp returned again
    g_propagate_error(err, jerr.err);
  }

DONE:
  // free buffers
  jpeg_destroy_decompress(&cinfo);

  return result;
}
Esempio n. 2
0
static bool jpeg_decode(FILE *f,  // or:
                        const void *buf, uint32_t buflen,
                        void * const _dest, bool grayscale,
                        int32_t w, int32_t h,
                        GError **err) {
  bool result = false;
  struct jpeg_decompress_struct cinfo;
  struct _openslide_jpeg_error_mgr jerr;
  jmp_buf env;
  volatile gsize row_size = 0;  // preserve across longjmp

  JSAMPARRAY buffer = g_slice_alloc0(sizeof(JSAMPROW) * MAX_SAMP_FACTOR);

  if (setjmp(env) == 0) {
    cinfo.err = _openslide_jpeg_set_error_handler(&jerr, &env);
    jpeg_create_decompress(&cinfo);

    // set up I/O
    if (f) {
      _openslide_jpeg_stdio_src(&cinfo, f);
    } else {
      _openslide_jpeg_mem_src(&cinfo, (void *) buf, buflen);
    }

    // read header
    int header_result = jpeg_read_header(&cinfo, TRUE);
    if ((header_result != JPEG_HEADER_OK
	 && header_result != JPEG_HEADER_TABLES_ONLY)) {
      g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_FAILED,
                  "Couldn't read JPEG header");
      goto DONE;
    }

    cinfo.out_color_space = grayscale ? JCS_GRAYSCALE : JCS_RGB;

    jpeg_start_decompress(&cinfo);

    // ensure buffer dimensions are correct
    int32_t width = cinfo.output_width;
    int32_t height = cinfo.output_height;
    if (w != width || h != height) {
      g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_FAILED,
                  "Dimensional mismatch reading JPEG, "
                  "expected %dx%d, got %dx%d",
                  w, h, cinfo.output_width, cinfo.output_height);
      goto DONE;
    }

    // allocate scanline buffers
    row_size = sizeof(JSAMPLE) * cinfo.output_width * cinfo.output_components;
    for (int i = 0; i < cinfo.rec_outbuf_height; i++) {
      buffer[i] = g_slice_alloc(row_size);
    }

    // decompress
    uint32_t *dest32 = _dest;
    uint8_t *dest8 = _dest;
    while (cinfo.output_scanline < cinfo.output_height) {
      JDIMENSION rows_read = jpeg_read_scanlines(&cinfo,
						 buffer,
						 cinfo.rec_outbuf_height);
      int cur_buffer = 0;
      while (rows_read > 0) {
        // copy a row
        int32_t i;
        if (cinfo.output_components == 1) {
          // grayscale
          for (i = 0; i < (int32_t) cinfo.output_width; i++) {
            dest8[i] = buffer[cur_buffer][i];
          }
          dest8 += cinfo.output_width;
        } else {
          // RGB
          for (i = 0; i < (int32_t) cinfo.output_width; i++) {
            dest32[i] = 0xFF000000 |                // A
              buffer[cur_buffer][i * 3 + 0] << 16 | // R
              buffer[cur_buffer][i * 3 + 1] << 8 |  // G
              buffer[cur_buffer][i * 3 + 2];        // B
          }
          dest32 += cinfo.output_width;
        }

	// advance 1 row
	rows_read--;
	cur_buffer++;
      }
    }
    result = true;
  } else {
    // setjmp has returned again
    g_propagate_error(err, jerr.err);
  }

DONE:
  // free buffers
  for (int i = 0; i < cinfo.rec_outbuf_height; i++) {
    g_slice_free1(row_size, buffer[i]);
  }
  g_slice_free1(sizeof(JSAMPROW) * MAX_SAMP_FACTOR, buffer);

  jpeg_destroy_decompress(&cinfo);

  return result;
}
// returns w and h and tw and th and comment as a convenience
static bool verify_jpeg(FILE *f, int32_t *w, int32_t *h,
			int32_t *tw, int32_t *th,
			char **comment, GError **err) {
  struct jpeg_decompress_struct cinfo;
  struct _openslide_jpeg_error_mgr jerr;
  jmp_buf env;
  bool success = false;

  *w = 0;
  *h = 0;
  *tw = 0;
  *th = 0;

  if (comment) {
    *comment = NULL;
  }

  if (setjmp(env) == 0) {
    cinfo.err = _openslide_jpeg_set_error_handler(&jerr, &env);
    jpeg_create_decompress(&cinfo);
    _openslide_jpeg_stdio_src(&cinfo, f);

    int header_result;

    if (comment) {
      // extract comment
      jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
    }

    header_result = jpeg_read_header(&cinfo, TRUE);
    if (header_result != JPEG_HEADER_OK
        && header_result != JPEG_HEADER_TABLES_ONLY) {
      g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA,
                  "Couldn't read JPEG header");
      goto DONE;
    }
    if (cinfo.num_components != 3) {
      g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA,
                  "JPEG color components != 3");
      goto DONE;
    }
    if (cinfo.restart_interval == 0) {
      g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA,
                  "No restart markers");
      goto DONE;
    }

    jpeg_start_decompress(&cinfo);

    if (comment) {
      if (cinfo.marker_list) {
	// copy everything out
	char *com = g_strndup((const gchar *) cinfo.marker_list->data,
			      cinfo.marker_list->data_length);
	// but only really save everything up to the first '\0'
	*comment = g_strdup(com);
	g_free(com);
      }
      jpeg_save_markers(&cinfo, JPEG_COM, 0);  // stop saving
    }

    *w = cinfo.output_width;
    *h = cinfo.output_height;

    if (cinfo.restart_interval > cinfo.MCUs_per_row) {
      g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA,
                  "Restart interval greater than MCUs per row");
      goto DONE;
    }

    *tw = *w / (cinfo.MCUs_per_row / cinfo.restart_interval);
    *th = *h / cinfo.MCU_rows_in_scan;
    int leftover_mcus = cinfo.MCUs_per_row % cinfo.restart_interval;
    if (leftover_mcus != 0) {
      g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA,
                  "Inconsistent restart marker spacing within row");
      goto DONE;
    }


    //  g_debug("w: %d, h: %d, restart_interval: %d\n"
    //	 "mcus_per_row: %d, mcu_rows_in_scan: %d\n"
    //	 "leftover mcus: %d",
    //	 cinfo.output_width, cinfo.output_height,
    //	 cinfo.restart_interval,
    //	 cinfo.MCUs_per_row, cinfo.MCU_rows_in_scan,
    //	 leftover_mcus);
  } else {
    // setjmp has returned again
    g_propagate_error(err, jerr.err);
    goto DONE;
  }

  success = true;

DONE:
  jpeg_destroy_decompress(&cinfo);
  return success;
}