Esempio n. 1
0
int
encodeWEBP(Vbitmap *vbitmap, Ychannel *channelout, YmagineFormatOptions *options)
{
  int rc = YMAGINE_ERROR;

#if HAVE_WEBP
  WebPConfig config;
  WebPPicture picture;
  WebPPreset preset = WEBP_PRESET_PHOTO; // One of DEFAULT, PICTURE, PHOTO, DRAWING, ICON or TEXT
  int width;
  int height;
  int pitch;
  int quality;
  const unsigned char *pixels;
  int colormode;

  /*
   * List of encoding options for WebP config
   *   int lossless;           // Lossless encoding (0=lossy(default), 1=lossless).
   *   float quality;          // between 0 (smallest file) and 100 (biggest)
   *   int method;             // quality/speed trade-off (0=fast, 6=slower-better)
   *
   *   WebPImageHint image_hint;  // Hint for image type (lossless only for now).
   *
   *   // Parameters related to lossy compression only:
   *   int target_size;        // if non-zero, set the desired target size in bytes.
   *                           // Takes precedence over the 'compression' parameter.
   *   float target_PSNR;      // if non-zero, specifies the minimal distortion to
   *                           // try to achieve. Takes precedence over target_size.
   *   int segments;           // maximum number of segments to use, in [1..4]
   *   int sns_strength;       // Spatial Noise Shaping. 0=off, 100=maximum.
   *   int filter_strength;    // range: [0 = off .. 100 = strongest]
   *   int filter_sharpness;   // range: [0 = off .. 7 = least sharp]
   *   int filter_type;        // filtering type: 0 = simple, 1 = strong (only used
   *                           // if filter_strength > 0 or autofilter > 0)
   *   int autofilter;         // Auto adjust filter's strength [0 = off, 1 = on]
   *   int alpha_compression;  // Algorithm for encoding the alpha plane (0 = none,
   *                           // 1 = compressed with WebP lossless). Default is 1.
   *   int alpha_filtering;    // Predictive filtering method for alpha plane.
   *                           //  0: none, 1: fast, 2: best. Default if 1.
   *   int alpha_quality;      // Between 0 (smallest size) and 100 (lossless).
   *                           // Default is 100.
   *   int pass;               // number of entropy-analysis passes (in [1..10]).
   *
   *   int show_compressed;    // if true, export the compressed picture back.
   *                           // In-loop filtering is not applied.
   *   int preprocessing;      // preprocessing filter (0=none, 1=segment-smooth)
   *   int partitions;         // log2(number of token partitions) in [0..3]
   *                           // Default is set to 0 for easier progressive decoding.
   *   int partition_limit;    // quality degradation allowed to fit the 512k limit on
   *                           // prediction modes coding (0: no degradation,
   *                           // 100: maximum possible degradation).
   */

  colormode = VbitmapColormode(vbitmap);

  if (colormode != VBITMAP_COLOR_RGBA && colormode != VBITMAP_COLOR_RGB) {
    ALOGD("currently only support RGB, RGBA webp encoding");
    return rc;
  }

  quality = YmagineFormatOptions_normalizeQuality(options);

  if (!WebPConfigPreset(&config, preset, (float) quality)) {
    return YMAGINE_ERROR;   // version error
  }

  if (options && options->accuracy >= 0) {
    int method = options->accuracy / 15;
    if (method > 6) {
      method = 6;
    }
    config.method = method;
  }

  if (!WebPValidateConfig(&config)) {
    // parameter ranges verification failed
    return YMAGINE_ERROR;
  }

  rc = VbitmapLock(vbitmap);
  if (rc < 0) {
    ALOGE("AndroidBitmap_lockPixels() failed");
    return YMAGINE_ERROR;
  }

  width = VbitmapWidth(vbitmap);
  height = VbitmapHeight(vbitmap);
  pitch = VbitmapPitch(vbitmap);
  pixels = VbitmapBuffer(vbitmap);

  if (WebPPictureInit(&picture)) {
    picture.use_argb = 1;
    picture.width = width;
    picture.height = height;
    picture.writer = WebPYchannelWrite;
    picture.custom_ptr = channelout;

    if (colormode == VBITMAP_COLOR_RGBA) {
      WebPPictureImportRGBA(&picture, pixels, pitch);
    } else {
      WebPPictureImportRGB(&picture, pixels, pitch);
    }

    WebPEncode(&config, &picture);

    WebPPictureFree(&picture);
  }

  VbitmapUnlock(vbitmap);
  rc = YMAGINE_OK;
#endif

  return rc;
}
Esempio n. 2
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;
}
Esempio n. 3
0
int
transcodeJPEG(Ychannel *channelin, Ychannel *channelout,
              YmagineFormatOptions *options)
{
  struct jpeg_decompress_struct cinfo;
  struct noop_error_mgr jerr;
  
  struct jpeg_compress_struct cinfoout;
  struct noop_error_mgr jerr2;
  int rc = YMAGINE_ERROR;
  int nlines = 0;
  int quality = YmagineFormatOptions_normalizeQuality(options);
  
  if (!YchannelReadable(channelin) || !YchannelWritable(channelout)) {
    return rc;
  }
  
  memset(&cinfo, 0, sizeof(struct jpeg_decompress_struct));
  cinfo.err = noop_jpeg_std_error(&jerr.pub);
  
  memset(&cinfoout, 0, sizeof(struct jpeg_compress_struct));
  cinfoout.err = noop_jpeg_std_error(&jerr2.pub);
  
  if (setjmp(jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error in decoder */
    noop_append_jpeg_message((j_common_ptr) &cinfo);
  } else if (setjmp(jerr2.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error in encoder */
    noop_append_jpeg_message((j_common_ptr) &cinfoout);
  } else {
    jpeg_create_decompress(&cinfo);
    
    /* Equivalent to:
	   jpeg_CreateCompress(&cinfoout, JPEG_LIB_VERSION,
	   (size_t) sizeof(struct jpeg_compress_struct));
     */
    jpeg_create_compress(&cinfoout);

    if (ymaginejpeg_input(&cinfo, channelin) >= 0 &&
        ymaginejpeg_output(&cinfoout, channelout) >= 0) {
      if (prepareDecompressor(&cinfo, options) == YMAGINE_OK) {
        /* markers copy option (NONE, COMMENTS or ALL) */
        JCOPY_OPTION copyoption;
        int metamode = YMAGINE_METAMODE_DEFAULT;

        if (options != NULL) {
          metamode = options->metamode;
        }
        if (metamode == YMAGINE_METAMODE_NONE) {
          copyoption = JCOPYOPT_NONE;
        } else if (metamode == YMAGINE_METAMODE_COMMENTS) {
          copyoption = JCOPYOPT_COMMENTS;
        } else if (metamode == YMAGINE_METAMODE_ALL) {
          copyoption = JCOPYOPT_ALL;
        } else {
          /* Default to copy all markers */
          copyoption = JCOPYOPT_ALL;
        }

        /* Enable saving of extra markers that we want to copy */
        if (copyoption != JCOPYOPT_NONE) {
          jcopy_markers_setup(&cinfo, copyoption);
        }
        
        /* Force image to be decoded without colorspace conversion if possible */
        if (jpeg_read_header(&cinfo, TRUE) == JPEG_HEADER_OK) {
          YmagineFormatOptions_invokeCallback(options, YMAGINE_IMAGEFORMAT_JPEG,
                                              cinfo.image_width, cinfo.image_height);

          /* Other compression settings */
          int optimize = 0;
          int progressive = 0;
          int grayscale = 0;
          
          if (quality >= 90) {
            optimize = 1;
          }

          if (options != NULL && options->pixelshader != NULL) {
            /* If a shader is enabled, force RGBA mode */
            cinfo.out_color_space = JCS_EXT_RGBA;
            cinfoout.in_color_space = cinfo.out_color_space;
            cinfoout.input_components = 4;
          } else if (options != NULL && options->sharpen > 0.0f) {
            cinfo.out_color_space = JCS_RGB;
            cinfoout.in_color_space = cinfo.out_color_space;
            cinfoout.input_components = 3;
          } else {
            /* Otherwise sse same colorspace than input image (typically YCbCr) */
            cinfo.out_color_space = cinfo.jpeg_color_space;
            cinfoout.in_color_space = cinfo.out_color_space;
            cinfoout.input_components = JpegPixelSize(cinfoout.in_color_space);
          }

          jpeg_set_defaults(&cinfoout);
          cinfoout.optimize_coding = FALSE;
          
          jpeg_set_quality(&cinfoout, quality, FALSE);
          if (grayscale) {
            /* Force a monochrome JPEG file to be generated. */
            jpeg_set_colorspace(&cinfoout, JCS_GRAYSCALE);
          }
          if (optimize) {
            /* Enable entropy parm optimization. */
            cinfoout.optimize_coding = TRUE;
          }

          /* If non-zero, the input image is smoothed; the value should
             be 1 for minimal smoothing to 100 for maximum smoothing. */
          cinfoout.smoothing_factor = 0;

          if (progressive) {
            /* Select simple progressive mode. */
            jpeg_simple_progression(&cinfoout);
          }
          setCompressorOptions(&cinfoout, options);

          cinfo.client_data = (void*) NULL;
          cinfoout.client_data = (void*) NULL;

          nlines = decompress_jpeg(&cinfo, &cinfoout, copyoption, NULL, options);
          if (nlines > 0) {
            rc = YMAGINE_OK;
          }
        }
      }
    }
  }
  
  jpeg_destroy_compress(&cinfoout);
  jpeg_destroy_decompress(&cinfo);
  
  return rc;
}
Esempio n. 4
0
int
encodeJPEG(Vbitmap *vbitmap, Ychannel *channelout, YmagineFormatOptions *options)
{
  struct jpeg_compress_struct cinfoout;
  struct noop_error_mgr jerr;
  int result = YMAGINE_ERROR;
  int rc;
  int nlines = 0;
  JSAMPROW row_pointer[1];
  unsigned char *pixels;
  int width;
  int height;
  int pitch;
  int colormode;
  int i;

  if (!YchannelWritable(channelout)) {
    return result;
  }

  if (vbitmap == NULL) {
    return result;
  }

  rc = VbitmapLock(vbitmap);
  if (rc != YMAGINE_OK) {
    ALOGE("AndroidBitmap_lockPixels() failed");
    return result;
  }

  memset(&cinfoout, 0, sizeof(struct jpeg_compress_struct));
  cinfoout.err = noop_jpeg_std_error(&jerr.pub);

  if (setjmp(jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error in encoder */
    noop_append_jpeg_message((j_common_ptr) &cinfoout);
  } else {
    /* Equivalent to:
	   jpeg_CreateCompress(&cinfoout, JPEG_LIB_VERSION,
	   (size_t) sizeof(struct jpeg_compress_struct));
     */
    jpeg_create_compress(&cinfoout);

    if (ymaginejpeg_output(&cinfoout, channelout) >= 0) {
      /* Other compression settings */
      int optimize = 0;
      int progressive = 0;
      int grayscale = 0;
      int quality = YmagineFormatOptions_normalizeQuality(options);

      if (quality >= 90) {
        optimize = 1;
      }

      width = VbitmapWidth(vbitmap);
      height = VbitmapHeight(vbitmap);
      pitch = VbitmapPitch(vbitmap);
      colormode = VbitmapColormode(vbitmap);

      cinfoout.image_width = width;
      cinfoout.image_height = height;

      set_colormode(&cinfoout, colormode);

      jpeg_set_defaults(&cinfoout);

      jpeg_set_quality(&cinfoout, quality, FALSE);
      if (grayscale) {
        /* Force a monochrome JPEG file to be generated. */
        jpeg_set_colorspace(&cinfoout, JCS_GRAYSCALE);
      }
      if (optimize) {
        /* Enable entropy parm optimization. */
        cinfoout.optimize_coding = TRUE;
      }
      if (progressive) {
        /* Select simple progressive mode. */
        jpeg_simple_progression(&cinfoout);
      }
      setCompressorOptions(&cinfoout, options);
      jpeg_start_compress(&cinfoout, TRUE);

      pixels = VbitmapBuffer(vbitmap);
      for (i = 0; i < height; i++) {
        row_pointer[0] = pixels + i * pitch;
        jpeg_write_scanlines(&cinfoout, row_pointer, 1);
        nlines++;
      }
      if (nlines > 0) {
        rc = YMAGINE_OK;
      }

      /* Clean up compressor */
      jpeg_finish_compress(&cinfoout);
    }
  }

  jpeg_destroy_compress(&cinfoout);
  VbitmapUnlock(vbitmap);

  return rc;
}
Esempio n. 5
0
static int
prepareDecompressor(struct jpeg_decompress_struct *cinfo, YmagineFormatOptions *options)
{
  int quality = YmagineFormatOptions_normalizeQuality(options);

  if (cinfo == NULL) {
    return YMAGINE_ERROR;
  }

  if (1) {
    cinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
  } else {
    cinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
  }
  
  if (quality < 90) {
    /* DCT method, one of JDCT_FASTEST, JDCT_IFAST, JDCT_ISLOW or JDCT_FLOAT */
    cinfo->dct_method = JDCT_IFAST;
    
    /* To perform 2-pass color quantization, the decompressor also needs a
     128K color lookup table and a full-image pixel buffer (3 bytes/pixel). */
    cinfo->two_pass_quantize = FALSE;
    
    /* No dithering with RGBA output. Use JDITHER_ORDERED only for JCS_RGB_565 */
    cinfo->dither_mode = JDITHER_NONE;
    
    /* Low visual impact but big performance benefit when turning off fancy up-sampling */
    cinfo->do_fancy_upsampling = FALSE;
    
    cinfo->do_block_smoothing = FALSE;
    
    cinfo->enable_2pass_quant = FALSE;
  } else {
    cinfo->dct_method = JDCT_ISLOW;

    if (quality >= 92) {
      cinfo->do_block_smoothing = TRUE;
    }

    /* Low visual impact but big performance benefit when turning off fancy up-sampling */
    if (quality >= 98) {
      cinfo->do_fancy_upsampling = TRUE;
    }
    if (quality >= 97) {
      cinfo->enable_2pass_quant = TRUE;
    }
  }

  /* Accuracy specified? If so, set the DCT method accordingly.
     It's toggling between JDCT_ISLOW and JDCT_IFAST;
     JDCT_FLOAT is apparently not a good option
     see: http://svn.code.sf.net/p/libjpeg-turbo/code/trunk/libjpeg.txt */
  if (options->accuracy >= 0) {
    if (options->accuracy < 50) {
      cinfo->dct_method = JDCT_IFAST;      
    } else {
      cinfo->dct_method = JDCT_ISLOW;
    }
  }
  
  return YMAGINE_OK;
}