DLLEXPORT int tj3Transform()

in turbojpeg.c [2665:2865]


DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf,
                           size_t jpegSize, int n, unsigned char **dstBufs,
                           size_t *dstSizes, const tjtransform *t)
{
  static const char FUNCTION_NAME[] = "tj3Transform";
  jpeg_transform_info *xinfo = NULL;
  jvirt_barray_ptr *srccoefs, *dstcoefs;
  int retval = 0, i, saveMarkers = 0, srcSubsamp;
  boolean alloc = TRUE;
  struct my_progress_mgr progress;

  GET_INSTANCE(handle);
  if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
    THROW("Instance has not been initialized for transformation");

  if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
      dstSizes == NULL || t == NULL)
    THROW("Invalid argument");

  if (this->scanLimit) {
    memset(&progress, 0, sizeof(struct my_progress_mgr));
    progress.pub.progress_monitor = my_progress_monitor;
    progress.this = this;
    dinfo->progress = &progress.pub;
  } else
    dinfo->progress = NULL;

  dinfo->mem->max_memory_to_use = (long)this->maxMemory * 1048576L;

  if ((xinfo =
       (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
    THROW("Memory allocation failure");
  memset(xinfo, 0, sizeof(jpeg_transform_info) * n);

  if (setjmp(this->jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error. */
    retval = -1;  goto bailout;
  }

  if (dinfo->global_state <= DSTATE_INHEADER)
    jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);

  for (i = 0; i < n; i++) {
    if (t[i].op < 0 || t[i].op >= TJ_NUMXOP)
      THROW("Invalid transform operation");
    xinfo[i].transform = xformtypes[t[i].op];
    xinfo[i].perfect = (t[i].options & TJXOPT_PERFECT) ? 1 : 0;
    xinfo[i].trim = (t[i].options & TJXOPT_TRIM) ? 1 : 0;
    xinfo[i].force_grayscale = (t[i].options & TJXOPT_GRAY) ? 1 : 0;
    xinfo[i].crop = (t[i].options & TJXOPT_CROP) ? 1 : 0;
    if (n != 1 && t[i].op == TJXOP_HFLIP) xinfo[i].slow_hflip = 1;
    else xinfo[i].slow_hflip = 0;

    if (xinfo[i].crop) {
      if (t[i].r.x < 0 || t[i].r.y < 0 || t[i].r.w < 0 || t[i].r.h < 0)
        THROW("Invalid cropping region");
      xinfo[i].crop_xoffset = t[i].r.x;  xinfo[i].crop_xoffset_set = JCROP_POS;
      xinfo[i].crop_yoffset = t[i].r.y;  xinfo[i].crop_yoffset_set = JCROP_POS;
      if (t[i].r.w != 0) {
        xinfo[i].crop_width = t[i].r.w;  xinfo[i].crop_width_set = JCROP_POS;
      } else
        xinfo[i].crop_width = JCROP_UNSET;
      if (t[i].r.h != 0) {
        xinfo[i].crop_height = t[i].r.h;  xinfo[i].crop_height_set = JCROP_POS;
      } else
        xinfo[i].crop_height = JCROP_UNSET;
    }
    if (!(t[i].options & TJXOPT_COPYNONE)) saveMarkers = 1;
  }

  jcopy_markers_setup(dinfo, saveMarkers ? JCOPYOPT_ALL : JCOPYOPT_NONE);
  if (dinfo->global_state <= DSTATE_INHEADER)
    jpeg_read_header(dinfo, TRUE);
  if (this->maxPixels &&
      (unsigned long long)dinfo->image_width * dinfo->image_height >
      (unsigned long long)this->maxPixels)
    THROW("Image is too large");
  srcSubsamp = getSubsamp(&this->dinfo);

  for (i = 0; i < n; i++) {
    if (!jtransform_request_workspace(dinfo, &xinfo[i]))
      THROW("Transform is not perfect");

    if (xinfo[i].crop) {
      int dstSubsamp = (t[i].options & TJXOPT_GRAY) ? TJSAMP_GRAY : srcSubsamp;

      if (t[i].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE ||
          t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) {
        if (dstSubsamp == TJSAMP_422) dstSubsamp = TJSAMP_440;
        else if (dstSubsamp == TJSAMP_440) dstSubsamp = TJSAMP_422;
        else if (dstSubsamp == TJSAMP_411) dstSubsamp = TJSAMP_441;
        else if (dstSubsamp == TJSAMP_441) dstSubsamp = TJSAMP_411;
      }
      if (dstSubsamp == TJSAMP_UNKNOWN)
        THROW("Could not determine subsampling level of destination image");
      if ((t[i].r.x % tjMCUWidth[dstSubsamp]) != 0 ||
          (t[i].r.y % tjMCUHeight[dstSubsamp]) != 0)
        THROWI("To crop this JPEG image, x must be a multiple of %d\n"
               "and y must be a multiple of %d.", tjMCUWidth[dstSubsamp],
               tjMCUHeight[dstSubsamp]);
    }
  }

  srccoefs = jpeg_read_coefficients(dinfo);

  for (i = 0; i < n; i++) {
    JDIMENSION dstWidth = dinfo->image_width, dstHeight = dinfo->image_height;

    if (t[i].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE ||
        t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) {
      dstWidth = dinfo->image_height;  dstHeight = dinfo->image_width;
    }

    if (xinfo[i].crop) {
      if ((JDIMENSION)t[i].r.x >= dstWidth ||
          t[i].r.x + xinfo[i].crop_width > dstWidth ||
          (JDIMENSION)t[i].r.y >= dstHeight ||
          t[i].r.y + xinfo[i].crop_height > dstHeight)
        THROW("The cropping region exceeds the destination image dimensions");
      dstWidth = xinfo[i].crop_width;  dstHeight = xinfo[i].crop_height;
    }
    if (this->noRealloc) {
      int dstSubsamp = (t[i].options & TJXOPT_GRAY) ? TJSAMP_GRAY : srcSubsamp;

      if (t[i].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE ||
          t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) {
        if (dstSubsamp == TJSAMP_422) dstSubsamp = TJSAMP_440;
        else if (dstSubsamp == TJSAMP_440) dstSubsamp = TJSAMP_422;
        else if (dstSubsamp == TJSAMP_411) dstSubsamp = TJSAMP_441;
        else if (dstSubsamp == TJSAMP_441) dstSubsamp = TJSAMP_411;
      }
      if (dstSubsamp == TJSAMP_UNKNOWN)
        THROW("Could not determine subsampling level of destination image");
      alloc = FALSE;
      dstSizes[i] = tj3JPEGBufSize(dstWidth, dstHeight, dstSubsamp);
    }
    if (!(t[i].options & TJXOPT_NOOUTPUT))
      jpeg_mem_dest_tj(cinfo, &dstBufs[i], &dstSizes[i], alloc);
    jpeg_copy_critical_parameters(dinfo, cinfo);
    dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]);
    if (this->optimize || t[i].options & TJXOPT_OPTIMIZE)
      cinfo->optimize_coding = TRUE;
#ifdef C_PROGRESSIVE_SUPPORTED
    if (this->progressive || t[i].options & TJXOPT_PROGRESSIVE)
      jpeg_simple_progression(cinfo);
#endif
    if (this->arithmetic || t[i].options & TJXOPT_ARITHMETIC) {
      cinfo->arith_code = TRUE;
      cinfo->optimize_coding = FALSE;
    }
    if (!(t[i].options & TJXOPT_NOOUTPUT)) {
      jpeg_write_coefficients(cinfo, dstcoefs);
      jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ?
                                          JCOPYOPT_NONE : JCOPYOPT_ALL);
    } else
      jinit_c_master_control(cinfo, TRUE);
    jtransform_execute_transformation(dinfo, cinfo, srccoefs, &xinfo[i]);
    if (t[i].customFilter) {
      int ci, y;
      JDIMENSION by;

      for (ci = 0; ci < cinfo->num_components; ci++) {
        jpeg_component_info *compptr = &cinfo->comp_info[ci];
        tjregion arrayRegion = { 0, 0, 0, 0 };
        tjregion planeRegion = { 0, 0, 0, 0 };

        arrayRegion.w = compptr->width_in_blocks * DCTSIZE;
        arrayRegion.h = DCTSIZE;
        planeRegion.w = compptr->width_in_blocks * DCTSIZE;
        planeRegion.h = compptr->height_in_blocks * DCTSIZE;

        for (by = 0; by < compptr->height_in_blocks;
             by += compptr->v_samp_factor) {
          JBLOCKARRAY barray = (dinfo->mem->access_virt_barray)
            ((j_common_ptr)dinfo, dstcoefs[ci], by, compptr->v_samp_factor,
             TRUE);

          for (y = 0; y < compptr->v_samp_factor; y++) {
            if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci,
                                  i, (tjtransform *)&t[i]) == -1)
              THROW("Error in custom filter");
            arrayRegion.y += DCTSIZE;
          }
        }
      }
    }
    if (!(t[i].options & TJXOPT_NOOUTPUT)) jpeg_finish_compress(cinfo);
  }

  jpeg_finish_decompress(dinfo);

bailout:
  if (cinfo->global_state > CSTATE_START) {
    if (alloc) (*cinfo->dest->term_destination) (cinfo);
    jpeg_abort_compress(cinfo);
  }
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
  free(xinfo);
  if (this->jerr.warning) retval = -1;
  return retval;
}