1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
// 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"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "ui/gfx/gfx_jni_headers/BitmapHelper_jni.h"
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
using jni_zero::AttachCurrentThread;
namespace jni_zero {
// Converts |bitmap| to an SkBitmap of the same size and format.
// Note: |j_bitmap| is assumed to be non-null, non-empty and of format
// RGBA_8888.
template <>
SkBitmap FromJniType<SkBitmap>(JNIEnv* env, const JavaRef<jobject>& j_bitmap) {
return gfx::CreateSkBitmapFromJavaBitmap(gfx::JavaBitmap(j_bitmap));
}
// Converts |skbitmap| to a Java-backed bitmap (android.graphics.Bitmap).
// Note: return nullptr jobject if |skbitmap| is null or empty.
template <>
ScopedJavaLocalRef<jobject> ToJniType<SkBitmap>(JNIEnv* env,
const SkBitmap& skbitmap) {
if (skbitmap.drawsNothing()) {
return {};
}
return gfx::ConvertToJavaBitmap(skbitmap, gfx::OomBehavior::kCrashOnOom);
}
} // namespace jni_zero
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) {
int err = AndroidBitmap_lockPixels(AttachCurrentThread(), bitmap_.obj(),
&pixels_.AsEphemeralRawAddr());
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
|