Ejemplo n.º 1
0
static int
WEBPDecode(WEBPDec* pSrc, Vbitmap *vbitmap,
           YmagineFormatOptions *options)
{
  int contentSize;
  int origWidth = 0;
  int origHeight = 0;
  int quality;
  unsigned char header[WEBP_HEADER_SIZE + 32];
  int headerlen;
  int toRead;
  unsigned char *input = NULL;
  int inputlen;
  int oformat;
  int opitch;
  unsigned char *odata;
  int rc;
  Vrect srcrect;
  Vrect destrect;
  WebPIDecoder* idec;

  if (options == NULL) {
    /* Options argument is mandatory */
    return 0;
  }

  headerlen = YchannelRead(pSrc->channel, (char *) header, sizeof(header));
  if (headerlen < WEBP_HEADER_SIZE) {
    return 0;
  }

  /* Check WEBP header */
  contentSize = WebpCheckHeader((const char*) header, headerlen);
  if (contentSize <= 0) {
    return 0;
  }

  if (WebPGetInfo(header, headerlen, &origWidth, &origHeight) == 0) {
    ALOGD("invalid VP8 header");
    return 0;
  }

  if (origWidth <= 0 || origHeight <= 0) {
    return 0;
  }

  if (YmagineFormatOptions_invokeCallback(options, YMAGINE_IMAGEFORMAT_WEBP,
                                          origWidth, origHeight) != YMAGINE_OK) {
    return 0;
  }

  if (YmaginePrepareTransform(vbitmap, options,
                              origWidth, origHeight,
                              &srcrect, &destrect) != YMAGINE_OK) {
    return 0;
  }

#if YMAGINE_DEBUG_WEBP
  ALOGD("size: %dx%d req: %dx%d %s -> output: %dx%d",
        origWidth, origHeight,
        destrect.width, destrect.height,
        (options->scalemode == YMAGINE_SCALE_CROP) ? "crop" :
        (options->scalemode == YMAGINE_SCALE_FIT ? "fit" : "letterbox"),
        destrect.width, destrect.height);
#endif

  if (vbitmap != NULL) {
    if (options->resizable) {
      destrect.x = 0;
      destrect.y = 0;
      if (VbitmapResize(vbitmap, destrect.width, destrect.height) != YMAGINE_OK) {
        return 0;
      }
    }
    if (VbitmapType(vbitmap) == VBITMAP_NONE) {
      /* Decode bounds only, return positive number (number of lines) on success */
      return VbitmapHeight(vbitmap);
    }
  }

  pSrc->bitmap = vbitmap;

  inputlen = contentSize;
  toRead = inputlen - headerlen;

  rc = VbitmapLock(vbitmap);
  if (rc != YMAGINE_OK) {
    ALOGE("VbitmapLock() failed (code %d)", rc);
    rc = YMAGINE_ERROR;
  } else {
    odata = VbitmapBuffer(vbitmap);
    opitch = VbitmapPitch(vbitmap);
    oformat = VbitmapColormode(vbitmap);

    pSrc->inwidth = origWidth;
    pSrc->inheight = origHeight;
    pSrc->outwidth = destrect.width;
    pSrc->outheight = destrect.height;

    if (odata == NULL) {
      ALOGD("failed to get reference to pixel buffer");
      rc = YMAGINE_ERROR;
   } else {
      WebPDecoderConfig config;
      int supported = 1;
      int webpcolorspace;

      switch(oformat) {
      case VBITMAP_COLOR_RGBA:
        webpcolorspace = MODE_RGBA;
        break;
      case VBITMAP_COLOR_RGB:
        webpcolorspace = MODE_RGB;
        break;
      case VBITMAP_COLOR_rgbA:
        webpcolorspace = MODE_rgbA;
        break;
      case VBITMAP_COLOR_ARGB:
        webpcolorspace = MODE_ARGB;
        break;
      case VBITMAP_COLOR_Argb:
        webpcolorspace = MODE_Argb;
        break;
      case VBITMAP_COLOR_GRAYSCALE:
      case VBITMAP_COLOR_YUV:
      case VBITMAP_COLOR_CMYK:
      case VBITMAP_COLOR_YCbCr:
      default:
        supported = 0;
        break;
      }

      if (!supported) {
        ALOGD("currently only support RGB, RGBA webp decoding");
        rc = YMAGINE_ERROR;
      } else {
        pSrc->isdirect = 1;
        pSrc->outformat = oformat;
        pSrc->outbpp = VbitmapBpp(vbitmap);
        pSrc->outstride = opitch;
        pSrc->outbuffer = odata + destrect.x * pSrc->outbpp + destrect.y * pSrc->outstride;

        WebPInitDecoderConfig(&config);

        quality = YmagineFormatOptions_normalizeQuality(options);
        if (quality < 90) {
          config.options.no_fancy_upsampling = 1;
        }
        if (quality < 60) {
          config.options.bypass_filtering = 1;
        }
        config.options.use_threads = 1;

        if (srcrect.x != 0 || srcrect.y != 0 || srcrect.width != origWidth || srcrect.height != origHeight) {
          /* Crop on source */
          config.options.use_cropping = 1;
          config.options.crop_left = srcrect.x;
          config.options.crop_top = srcrect.y;
          config.options.crop_width = srcrect.width;
          config.options.crop_height = srcrect.height;
        }
        if (pSrc->outwidth != pSrc->inwidth || pSrc->outheight != pSrc->inheight) {
          config.options.use_scaling = 1;
          config.options.scaled_width = pSrc->outwidth;
          config.options.scaled_height = pSrc->outheight;
        }

        rc = YMAGINE_ERROR;

        // Specify the desired output colorspace:
        config.output.colorspace = webpcolorspace;

        // Have config.output point to an external buffer:
        config.output.u.RGBA.rgba = (uint8_t*) pSrc->outbuffer;
        config.output.u.RGBA.stride = pSrc->outstride;
        config.output.u.RGBA.size = pSrc->outstride * pSrc->outheight;
        config.output.is_external_memory = 1;

        idec = WebPIDecode(NULL, 0, &config);
        if (idec != NULL) {
          VP8StatusCode status;

          status = WebPIAppend(idec, header, headerlen);
          if (status == VP8_STATUS_OK || status == VP8_STATUS_SUSPENDED) {
            int bytes_remaining = toRead;
            int bytes_read;
            int bytes_req;
            unsigned char rbuf[8192];

            // See WebPIUpdate(idec, buffer, size_of_transmitted_buffer);
            bytes_req = sizeof(rbuf);
            while (bytes_remaining > 0) {
              if (bytes_req > bytes_remaining) {
                bytes_req = bytes_remaining;
              }
              bytes_read = YchannelRead(pSrc->channel, rbuf, bytes_req);
              if (bytes_read <= 0) {
                break;
              }
              status = WebPIAppend(idec, (uint8_t*) rbuf, bytes_read);
              if (status == VP8_STATUS_OK) {
                rc = YMAGINE_OK;
                break;
              } else if (status == VP8_STATUS_SUSPENDED) {
                if (bytes_remaining > 0) {
                  bytes_remaining -= bytes_read;
                }
              } else {
                /* error */
                break;
              }
              // The above call decodes the current available buffer.
              // Part of the image can now be refreshed by calling
              // WebPIDecGetRGB()/WebPIDecGetYUVA() etc.
            }
          }
        }

        // the object doesn't own the image memory, so it can now be deleted.
        WebPIDelete(idec);
        WebPFreeDecBuffer(&config.output);
      }
    }

    VbitmapUnlock(vbitmap);
  }

  if (input) {
    Ymem_free((char*) input);
  }
  if (!pSrc->isdirect) {
    Ymem_free(pSrc->outbuffer);
  }

  if (rc == YMAGINE_OK) {
    return origHeight;
  }

  return 0;
}
Ejemplo n.º 2
0
static YOPTIMIZE_SPEED int
decompress_jpeg(struct jpeg_decompress_struct *cinfo,
                struct jpeg_compress_struct *cinfoout, JCOPY_OPTION copyoption,
                Vbitmap *vbitmap, YmagineFormatOptions *options)
{
  int scanlines;
  int nlines;
  int totallines;
  int j;
  int scalenum = -1;
  JSAMPARRAY buffer;
  Vrect srcrect;
  Vrect destrect;
  size_t row_stride;
  Transformer *transformer;
  PixelShader *shader = NULL;
  int iwidth, iheight;
  float sharpen = 0.0f;

  if (vbitmap == NULL && cinfoout == NULL) {
    /* No output specified */
    return 0;
  }
  if (options == NULL) {
    /* Options argument is mandatory */
    return 0;
  }

  iwidth = cinfo->image_width;
  iheight = cinfo->image_height;

  if (YmaginePrepareTransform(vbitmap, options,
                              iwidth, iheight,
                              &srcrect, &destrect) != YMAGINE_OK) {
    return 0;
  }

  shader = options->pixelshader;
  sharpen = options->sharpen;

  /* Define if image can be pre-subsampled by a ratio n/8 (n=1..7) */
  scalenum = GetScaleNum(destrect.width, destrect.height,
                         srcrect.width, srcrect.height,
                         options->scalemode);
  if (scalenum > 0 && scalenum < 8) {
    cinfo->scale_num = scalenum;
    cinfo->scale_denom = 8;
  }

  /* Compute actual output dimension for image returned by decoder */
  jpeg_calc_output_dimensions(cinfo);

#if YMAGINE_DEBUG_JPEG
  ALOGD("src=%dx%d@%d,%d dst=%dx%d@%d,%d",
        srcrect.width, srcrect.height, srcrect.x, srcrect.y,
        destrect.width, destrect.height, destrect.x, destrect.y);

  ALOGD("size: %dx%d req: %dx%d %s -> scale: %d/%d output: %dx%d components: %d",
        iwidth, iheight,
        destrect.width, destrect.height,
        Ymagine_scaleModeStr(options->scalemode),
        cinfo->scale_num, cinfo->scale_denom,
        cinfo->output_width, cinfo->output_height,
        cinfo->output_components);
#endif

  /* Scale the crop region to reflect scaling ratio applied by JPEG decoder */
  if (cinfo->image_width != cinfo->output_width) {
    srcrect.x = (srcrect.x * cinfo->output_width) / cinfo->image_width;
    srcrect.width = (srcrect.width * cinfo->output_width) / cinfo->image_width;
  }
  if (cinfo->image_height != cinfo->output_height) {
    srcrect.y = (srcrect.y * cinfo->output_height) / cinfo->image_height;
    srcrect.height = (srcrect.height * cinfo->output_height) / cinfo->image_height;
  }

  /* Number of scan lines to handle per pass. Making it larger actually doesn't help much */
  row_stride = cinfo->output_width * cinfo->output_components;
  scanlines = (32 * 1024) / row_stride;
  if (scanlines < 1) {
    scanlines = 1;
  }
  if (scanlines > cinfo->output_height) {
    scanlines = cinfo->output_height;
  }

#if YMAGINE_DEBUG_JPEG
  ALOGD("BITMAP @(%d,%d) %dx%d bpp=%d -> @(%dx%d) %dx%d (%d lines)",
        srcrect.x, srcrect.y, srcrect.width, srcrect.height, JpegPixelSize(cinfo->out_color_space),
        destrect.x, destrect.y, destrect.width, destrect.height,
        scanlines);
#endif

  /* Resize encoder */
  if (cinfoout != NULL) {
    cinfoout->image_width = destrect.width;
    cinfoout->image_height = destrect.height;
    jpeg_start_compress(cinfoout, TRUE);
    if (copyoption != JCOPYOPT_NONE) {
      /* Copy to the output file any extra markers that we want to preserve */
      jcopy_markers_execute(cinfo, cinfoout, copyoption);
    }
  }

  /* Resize target bitmap */
  if (vbitmap != NULL) {
    if (options->resizable) {
      destrect.x = 0;
      destrect.y = 0;
      if (VbitmapResize(vbitmap, destrect.width, destrect.height) != YMAGINE_OK) {
        return 0;
      }
    }
    if (VbitmapType(vbitmap) == VBITMAP_NONE) {
      /* Decode bounds only, return positive number (number of lines) on success */
      return VbitmapHeight(vbitmap);
    }
  }

  if (!jpeg_start_decompress(cinfo)) {
    if (cinfoout != NULL) {
      jpeg_abort_compress(cinfoout);
    }
    return 0;
  }
  
  buffer = (JSAMPARRAY) (*cinfo->mem->alloc_sarray)((j_common_ptr) cinfo, JPOOL_IMAGE,
                                                    row_stride, scanlines);
  if (buffer == NULL) {
    if (cinfoout != NULL) {
      jpeg_abort_compress(cinfoout);
    }
    jpeg_abort_decompress(cinfo);
    return 0;
  }
  
  totallines = 0;
  
  transformer = TransformerCreate();
  if (transformer != NULL) {
    TransformerSetScale(transformer,
                        cinfo->output_width, cinfo->output_height,
                        destrect.width, destrect.height);
    TransformerSetRegion(transformer,
                         srcrect.x, srcrect.y, srcrect.width, srcrect.height);

    if (vbitmap != NULL) {
      TransformerSetMode(transformer, JpegPixelMode(cinfo->out_color_space), VbitmapColormode(vbitmap));
      TransformerSetBitmap(transformer, vbitmap, destrect.x, destrect.y);
    } else {
      TransformerSetMode(transformer, JpegPixelMode(cinfo->out_color_space),
                         JpegPixelMode(cinfoout->in_color_space));
      TransformerSetWriter(transformer, JpegWriter, cinfoout);
    }
    TransformerSetShader(transformer, shader);
    TransformerSetSharpen(transformer, sharpen);
  }

  while (transformer != NULL && cinfo->output_scanline < cinfo->output_height) {
    nlines = jpeg_read_scanlines(cinfo, buffer, scanlines);
    if (nlines <= 0) {
      /* Decoding error */
      ALOGD("decoding error (nlines=%d)", nlines);
      break;
    }

    for (j = 0; j < nlines; j++) {
      if (TransformerPush(transformer, (const char*) buffer[j]) != YMAGINE_OK) {
        TransformerRelease(transformer);
        transformer = NULL;
        break;
      }
      totallines++;
    }
  }

  /* Clean up */
  if (transformer != NULL) {
    TransformerRelease(transformer);
  }
  if (cinfo->output_scanline > 0 && cinfo->output_scanline == cinfo->output_height) {
    /* Do normal cleanup if whole image has been read and decoded */
    jpeg_finish_decompress(cinfo);
    if (cinfoout != NULL) {
      jpeg_finish_compress(cinfoout);
    }
  }
  else {
    /* else early abort */
    jpeg_abort_decompress(cinfo);
    if (cinfoout != NULL) {
      jpeg_abort_compress(cinfoout);
    }
    totallines = 0;
  }
  
  return totallines;
}