| // Copyright 2012 The Chromium Authors |
| // 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 <android/bitmap.h> |
| |
| #include "base/android/jni_string.h" |
| #include "base/bits.h" |
| #include "base/check_op.h" |
| #include "base/debug/crash_logging.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: |
| SCOPED_CRASH_KEY_NUMBER("gfx", "bitmap_format", |
| static_cast<int>(bitmap_format)); |
| 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<int>(a) == static_cast<int>(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<jobject>& 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<BitmapFormat>(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<jobject> 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<jobject> 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, size_t{4}); |
| |
| SkBitmap skbitmap; |
| skbitmap.allocPixels(src.info(), row_bytes); |
| skbitmap.writePixels(src); |
| return skbitmap; |
| } |
| |
| SkColorType ConvertToSkiaColorType(const JavaRef<jobject>& bitmap_config) { |
| BitmapFormat jbitmap_format = |
| static_cast<BitmapFormat>(Java_BitmapHelper_getBitmapFormatForConfig( |
| AttachCurrentThread(), bitmap_config)); |
| return BitmapFormatToSkColorType(jbitmap_format); |
| } |
| |
| } // namespace gfx |