// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/gfx/android/java_bitmap.h" #include #include "base/android/jni_string.h" #include "base/bits.h" #include "base/check_op.h" #include "base/notreached.h" #include "base/numerics/safe_conversions.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/gfx_jni_headers/BitmapHelper_jni.h" using base::android::AttachCurrentThread; using base::android::ConvertUTF8ToJavaString; using base::android::ScopedJavaLocalRef; using base::android::JavaRef; namespace gfx { namespace { int SkColorTypeToBitmapFormat(SkColorType color_type) { switch (color_type) { case kN32_SkColorType: return BITMAP_FORMAT_ARGB_8888; case kRGB_565_SkColorType: return BITMAP_FORMAT_RGB_565; default: // A bad format can cause out-of-bounds issues when copying pixels into or // out of the java bitmap's pixel buffer. CHECK_NE(color_type, color_type); } return BITMAP_FORMAT_NO_CONFIG; } SkColorType BitmapFormatToSkColorType(BitmapFormat bitmap_format) { switch (bitmap_format) { case BITMAP_FORMAT_ALPHA_8: return kAlpha_8_SkColorType; case BITMAP_FORMAT_ARGB_4444: return kARGB_4444_SkColorType; case BITMAP_FORMAT_ARGB_8888: return kN32_SkColorType; case BITMAP_FORMAT_RGB_565: return kRGB_565_SkColorType; case BITMAP_FORMAT_NO_CONFIG: default: CHECK_NE(bitmap_format, bitmap_format); return kUnknown_SkColorType; } } // Wraps a Java bitmap as an SkPixmap. Since the pixmap references the // underlying pixel data in the Java bitmap directly, it is only valid as long // as |bitmap| is. SkPixmap WrapJavaBitmapAsPixmap(const JavaBitmap& bitmap) { auto color_type = BitmapFormatToSkColorType(bitmap.format()); auto image_info = SkImageInfo::Make(bitmap.size().width(), bitmap.size().height(), color_type, kPremul_SkAlphaType); return SkPixmap(image_info, bitmap.pixels(), bitmap.bytes_per_row()); } } // namespace #define ASSERT_ENUM_EQ(a, b) \ static_assert(static_cast(a) == static_cast(b), "") // BitmapFormat has the same values as AndroidBitmapFormat, for simplicitly, so // that SkColorTypeToBitmapFormat() and the JavaBitmap::format() have the same // values. ASSERT_ENUM_EQ(BITMAP_FORMAT_NO_CONFIG, ANDROID_BITMAP_FORMAT_NONE); ASSERT_ENUM_EQ(BITMAP_FORMAT_ALPHA_8, ANDROID_BITMAP_FORMAT_A_8); ASSERT_ENUM_EQ(BITMAP_FORMAT_ARGB_4444, ANDROID_BITMAP_FORMAT_RGBA_4444); ASSERT_ENUM_EQ(BITMAP_FORMAT_ARGB_8888, ANDROID_BITMAP_FORMAT_RGBA_8888); ASSERT_ENUM_EQ(BITMAP_FORMAT_RGB_565, ANDROID_BITMAP_FORMAT_RGB_565); JavaBitmap::JavaBitmap(const JavaRef& bitmap) : bitmap_(bitmap), pixels_(NULL) { int err = AndroidBitmap_lockPixels(AttachCurrentThread(), bitmap_.obj(), &pixels_); DCHECK(!err); DCHECK(pixels_); AndroidBitmapInfo info; err = AndroidBitmap_getInfo(AttachCurrentThread(), bitmap_.obj(), &info); DCHECK(!err); size_ = gfx::Size(info.width, info.height); format_ = static_cast(info.format); bytes_per_row_ = info.stride; byte_count_ = Java_BitmapHelper_getByteCount(AttachCurrentThread(), bitmap_); } JavaBitmap::~JavaBitmap() { int err = AndroidBitmap_unlockPixels(AttachCurrentThread(), bitmap_.obj()); DCHECK(!err); } ScopedJavaLocalRef ConvertToJavaBitmap(const SkBitmap& skbitmap, OomBehavior reaction) { DCHECK(!skbitmap.isNull()); DCHECK_GT(skbitmap.width(), 0); DCHECK_GT(skbitmap.height(), 0); int java_bitmap_format = SkColorTypeToBitmapFormat(skbitmap.colorType()); ScopedJavaLocalRef jbitmap = Java_BitmapHelper_createBitmap( AttachCurrentThread(), skbitmap.width(), skbitmap.height(), java_bitmap_format, reaction == OomBehavior::kReturnNullOnOom); if (!jbitmap) { DCHECK_EQ(OomBehavior::kReturnNullOnOom, reaction); return jbitmap; } JavaBitmap dst_lock(jbitmap); SkPixmap dst = WrapJavaBitmapAsPixmap(dst_lock); skbitmap.readPixels(dst); return jbitmap; } SkBitmap CreateSkBitmapFromJavaBitmap(const JavaBitmap& jbitmap) { DCHECK(!jbitmap.size().IsEmpty()); DCHECK_GT(jbitmap.bytes_per_row(), 0U); DCHECK(jbitmap.pixels()); // Ensure 4 byte stride alignment since the texture upload code in the // compositor relies on this. SkPixmap src = WrapJavaBitmapAsPixmap(jbitmap); const size_t min_row_bytes = src.info().minRowBytes(); const size_t row_bytes = base::bits::AlignUp(min_row_bytes, 4u); SkBitmap skbitmap; skbitmap.allocPixels(src.info(), row_bytes); skbitmap.writePixels(src); return skbitmap; } SkColorType ConvertToSkiaColorType(const JavaRef& bitmap_config) { BitmapFormat jbitmap_format = static_cast(Java_BitmapHelper_getBitmapFormatForConfig( AttachCurrentThread(), bitmap_config)); return BitmapFormatToSkColorType(jbitmap_format); } } // namespace gfx