<commit_before>/*
 *  OpenSlide, a library for reading whole slide image files
 *
 *  Copyright (c) 2007-2015 Carnegie Mellon University
 *  Copyright (c) 2011 Google, Inc.
 *  All rights reserved.
 *
 *  OpenSlide is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation, version 2.1.
 *
 *  OpenSlide is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with OpenSlide. If not, see
 *  <http://www.gnu.org/licenses/>.
 *
 */

#include <config.h>

#include "openslide-private.h"
#include "openslide-decode-jpeg.h"

#include <glib.h>
#include <setjmp.h>
#include <stdlib.h>
#include <stdio.h>
#include <jpeglib.h>
#include <jerror.h>

struct openslide_jpeg_error_mgr {
  struct jpeg_error_mgr base;
  jmp_buf *env;
  GError *err;
};

struct _openslide_jpeg_decompress {
  struct jpeg_decompress_struct cinfo;
  struct openslide_jpeg_error_mgr jerr;
  JSAMPROW rows[MAX_SAMP_FACTOR];
  gsize allocated_row_size;
};

struct associated_image {
  struct _openslide_associated_image base;
  char *filename;
  int64_t offset;
};


static void my_error_exit(j_common_ptr cinfo) {
  struct openslide_jpeg_error_mgr *jerr =
    (struct openslide_jpeg_error_mgr *) cinfo->err;

  (jerr->base.output_message) (cinfo);

  //  g_debug("JUMP");
  longjmp(*(jerr->env), 1);
}

static void my_output_message(j_common_ptr cinfo) {
  struct openslide_jpeg_error_mgr *jerr =
    (struct openslide_jpeg_error_mgr *) cinfo->err;
  char buffer[JMSG_LENGTH_MAX];

  (*cinfo->err->format_message) (cinfo, buffer);

  g_set_error(&jerr->err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_FAILED,
              "%s", buffer);
}

static void my_emit_message(j_common_ptr cinfo, int msg_level) {
  if (msg_level < 0) {
    // Warning message.  Convert to fatal error.
    (*cinfo->err->error_exit) (cinfo);
  }
}

// the caller must assign the struct _openslide_jpeg_decompress * before
// calling setjmp() so that nothing will be clobbered by a longjmp()
struct _openslide_jpeg_decompress *_openslide_jpeg_decompress_create(struct jpeg_decompress_struct **out_cinfo) {
  struct _openslide_jpeg_decompress *dc = g_slice_new0(struct _openslide_jpeg_decompress);
  *out_cinfo = &dc->cinfo;
  return dc;
}

// after setjmp(), initialize error handler and start decompressing
void _openslide_jpeg_decompress_init(struct _openslide_jpeg_decompress *dc,
                                     jmp_buf *env) {
  struct openslide_jpeg_error_mgr *jerr = &dc->jerr;
  jpeg_std_error(&(jerr->base));
  jerr->base.error_exit = my_error_exit;
  jerr->base.output_message = my_output_message;
  jerr->base.emit_message = my_emit_message;
  jerr->env = env;
  dc->cinfo.err = (struct jpeg_error_mgr *) jerr;
  jpeg_create_decompress(&dc->cinfo);
}

bool _openslide_jpeg_decompress_run(struct _openslide_jpeg_decompress *dc,
                                    // uint8_t * if grayscale, else uint32_t *
                                    void *_dest,
                                    bool grayscale,
                                    int32_t w, int32_t h,
                                    GError **err) {
  struct jpeg_decompress_struct *cinfo = &dc->cinfo;

  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, width, height);
    return false;
  }

  // allocate scanline buffers
  g_assert(dc->rows[0] == NULL);
  dc->allocated_row_size = sizeof(JSAMPLE) * cinfo->output_width *
                           cinfo->output_components;
  for (int i = 0; i < cinfo->rec_outbuf_height; i++) {
    dc->rows[i] = g_slice_alloc(dc->allocated_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,
                                               dc->rows,
                                               cinfo->rec_outbuf_height);
    int cur_row = 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] = dc->rows[cur_row][i];
        }
        dest8 += cinfo->output_width;
      } else {
        // RGB
        for (i = 0; i < (int32_t) cinfo->output_width; i++) {
          dest32[i] = 0xFF000000 |               // A
            dc->rows[cur_row][i * 3 + 0] << 16 | // R
            dc->rows[cur_row][i * 3 + 1] << 8 |  // G
            dc->rows[cur_row][i * 3 + 2];        // B
        }
        dest32 += cinfo->output_width;
      }

      // advance 1 row
      rows_read--;
      cur_row++;
    }
  }
  return true;
}

void _openslide_jpeg_propagate_error(GError **err,
                                     struct _openslide_jpeg_decompress *dc) {
  g_propagate_error(err, dc->jerr.err);
  dc->jerr.err = NULL;
}

void _openslide_jpeg_decompress_destroy(struct _openslide_jpeg_decompress *dc) {
  jpeg_destroy_decompress(&dc->cinfo);
  g_assert(dc->jerr.err == NULL);
  if (dc->allocated_row_size) {<commit_msg>jpeg: Avoid unnecessary copy when decoding grayscale JPEGs<commit_after>/*
 *  OpenSlide, a library for reading whole slide image files
 *
 *  Copyright (c) 2007-2015 Carnegie Mellon University
 *  Copyright (c) 2011 Google, Inc.
 *  All rights reserved.
 *
 *  OpenSlide is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation, version 2.1.
 *
 *  OpenSlide is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with OpenSlide. If not, see
 *  <http://www.gnu.org/licenses/>.
 *
 */

#include <config.h>

#include "openslide-private.h"
#include "openslide-decode-jpeg.h"

#include <glib.h>
#include <setjmp.h>
#include <stdlib.h>
#include <stdio.h>
#include <jpeglib.h>
#include <jerror.h>

struct openslide_jpeg_error_mgr {
  struct jpeg_error_mgr base;
  jmp_buf *env;
  GError *err;
};

struct _openslide_jpeg_decompress {
  struct jpeg_decompress_struct cinfo;
  struct openslide_jpeg_error_mgr jerr;
  JSAMPROW rows[MAX_SAMP_FACTOR];
  gsize allocated_row_size;
};

struct associated_image {
  struct _openslide_associated_image base;
  char *filename;
  int64_t offset;
};


static void my_error_exit(j_common_ptr cinfo) {
  struct openslide_jpeg_error_mgr *jerr =
    (struct openslide_jpeg_error_mgr *) cinfo->err;

  (jerr->base.output_message) (cinfo);

  //  g_debug("JUMP");
  longjmp(*(jerr->env), 1);
}

static void my_output_message(j_common_ptr cinfo) {
  struct openslide_jpeg_error_mgr *jerr =
    (struct openslide_jpeg_error_mgr *) cinfo->err;
  char buffer[JMSG_LENGTH_MAX];

  (*cinfo->err->format_message) (cinfo, buffer);

  g_set_error(&jerr->err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_FAILED,
              "%s", buffer);
}

static void my_emit_message(j_common_ptr cinfo, int msg_level) {
  if (msg_level < 0) {
    // Warning message.  Convert to fatal error.
    (*cinfo->err->error_exit) (cinfo);
  }
}

// the caller must assign the struct _openslide_jpeg_decompress * before
// calling setjmp() so that nothing will be clobbered by a longjmp()
struct _openslide_jpeg_decompress *_openslide_jpeg_decompress_create(struct jpeg_decompress_struct **out_cinfo) {
  struct _openslide_jpeg_decompress *dc = g_slice_new0(struct _openslide_jpeg_decompress);
  *out_cinfo = &dc->cinfo;
  return dc;
}

// after setjmp(), initialize error handler and start decompressing
void _openslide_jpeg_decompress_init(struct _openslide_jpeg_decompress *dc,
                                     jmp_buf *env) {
  struct openslide_jpeg_error_mgr *jerr = &dc->jerr;
  jpeg_std_error(&(jerr->base));
  jerr->base.error_exit = my_error_exit;
  jerr->base.output_message = my_output_message;
  jerr->base.emit_message = my_emit_message;
  jerr->env = env;
  dc->cinfo.err = (struct jpeg_error_mgr *) jerr;
  jpeg_create_decompress(&dc->cinfo);
}

bool _openslide_jpeg_decompress_run(struct _openslide_jpeg_decompress *dc,
                                    // uint8_t * if grayscale, else uint32_t *
                                    void *_dest,
                                    bool grayscale,
                                    int32_t w, int32_t h,
                                    GError **err) {
  struct jpeg_decompress_struct *cinfo = &dc->cinfo;

  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, width, height);
    return false;
  }

  // verify we haven't run already
  g_assert(dc->rows[0] == NULL);

  if (cinfo->output_components == 1) {
    // grayscale; decode directly to output

    uint8_t *dest = _dest;
    while (cinfo->output_scanline < cinfo->output_height) {
      // set row pointers
      for (int32_t i = 0; i < cinfo->rec_outbuf_height; i++) {
        dc->rows[i] = cinfo->output_scanline + i < cinfo->output_height ?
                      dest + i * cinfo->output_width : NULL;
      }

      // decompress
      JDIMENSION rows_read = jpeg_read_scanlines(cinfo,
                                                 dc->rows,
                                                 cinfo->rec_outbuf_height);
      dest += rows_read * cinfo->output_width;
    }

  } else {
    // decode into temporary buffer, then reformat

    // allocate scanline buffers
    dc->allocated_row_size = sizeof(JSAMPLE) * cinfo->output_width *
                             cinfo->output_components;
    for (int i = 0; i < cinfo->rec_outbuf_height; i++) {
      dc->rows[i] = g_slice_alloc(dc->allocated_row_size);
    }

    // decompress
    uint32_t *dest = _dest;
    while (cinfo->output_scanline < cinfo->output_height) {
      JDIMENSION rows_read = jpeg_read_scanlines(cinfo,
                                                 dc->rows,
                                                 cinfo->rec_outbuf_height);
      int cur_row = 0;
      while (rows_read > 0) {
        // copy a row
        for (int32_t i = 0; i < (int32_t) cinfo->output_width; i++) {
          dest[i] = 0xFF000000 |                 // A
            dc->rows[cur_row][i * 3 + 0] << 16 | // R
            dc->rows[cur_row][i * 3 + 1] << 8 |  // G
            dc->rows[cur_row][i * 3 + 2];        // B
        }
        dest += cinfo->output_width;

        // advance 1 row
        rows_read--;
        cur_row++;
      }
    }
  }
  return true;
}

void _openslide_jpeg_propagate_error(GError **err,
                                     struct _openslide_jpeg_decompress *dc) {
  g_propagate_error(err, dc->jerr.err);
  dc->jerr.err = NULL;
}

void _openslide_jpeg_decompress_destroy(struct _openslide_jpeg_decompress *dc) {
  jpeg_destroy_decompress(&dc->cinfo);
  g_assert(dc->jerr.err == NULL);
  if (dc->allocated_row_size) {
{'fraction_non_alphanumeric': 0.10559109412644346, 'fraction_numerical': 0.011215419124366537, 'mean_word_length': 2.6063511084481727, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 122, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 30, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1048032', 'n_tokens_mistral': 4242, 'n_tokens_neox': 4017, 'n_words': 941}