/* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.imagepipeline.nativecode; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import android.graphics.BitmapFactory; import android.os.Build; import android.util.Base64; import com.facebook.common.internal.DoNotStrip; import com.facebook.common.internal.Preconditions; import com.facebook.imageformat.ImageFormat; import com.facebook.imagepipeline.nativecode.ImagePipelineNativeLoader; /** * Helper methods for modifying webp static images. */ @DoNotStrip public class WebpTranscoder { static { ImagePipelineNativeLoader.load(); } /** * BASE64 encoded extended WebP image. */ private static final String VP8X_WEBP_BASE64 = "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAw" + "AAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA=="; private static final boolean mIsExtendedWebpSupported = isExtendedWebpSupported(); /** * Checks whether underlying platform supports extended WebPs */ private static boolean isExtendedWebpSupported() { // Lossless and extended formats are supported on Android 4.2.1+ // Unfortunately SDK_INT is not enough to distinguish 4.2 and 4.2.1 // (both are API level 17 (JELLY_BEAN_MR1)) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { return false; } if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR1) { // Let's test if extended webp is supported // To this end we will try to decode bounds of vp8x webp with alpha channel byte[] decodedBytes = Base64.decode(VP8X_WEBP_BASE64, Base64.DEFAULT); BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length, opts); // If Android managed to find appropriate decoder then opts.outHeight and opts.outWidth // should be set. Warning ! Unfortunately we can not assume that outMimeType is set. // Android guys forgot to update logic for mime types when they introduced support for webp. // For example, on 4.2.2 this field is not set for webp images. if (opts.outHeight != 1 || opts.outWidth != 1) { return false; } } return true; } /** * @return true if given type of WebP is supported natively by the framework */ public static boolean isWebpNativelySupported(ImageFormat webpFormat) { switch (webpFormat) { case WEBP_SIMPLE: // Simple WebPs are supported on Android 4.0+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH; case WEBP_LOSSLESS: case WEBP_EXTENDED: case WEBP_EXTENDED_WITH_ALPHA: return mIsExtendedWebpSupported; case WEBP_ANIMATED: return false; default: Preconditions.checkArgument(false); return false; } } /** * Transcodes webp image given by input stream into jpeg. */ public static void transcodeWebpToJpeg( InputStream inputStream, OutputStream outputStream, int quality) throws IOException { nativeTranscodeWebpToJpeg( Preconditions.checkNotNull(inputStream), Preconditions.checkNotNull(outputStream), quality); } /** * Transcodes Webp image given by input stream into png. */ public static void transcodeWebpToPng( InputStream inputStream, OutputStream outputStream) throws IOException { nativeTranscodeWebpToPng( Preconditions.checkNotNull(inputStream), Preconditions.checkNotNull(outputStream)); } @DoNotStrip private static native void nativeTranscodeWebpToJpeg( InputStream inputStream, OutputStream outputStream, int quality) throws IOException; @DoNotStrip private static native void nativeTranscodeWebpToPng( InputStream inputStream, OutputStream outputStream) throws IOException; }