/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "BitmapSerializeUtils" #include #include #include #include namespace android { #define RGBA_8888_COLOR_DEPTH 4 static bool writeAllBytes(const int fd, void* buffer, const size_t byteCount) { char* writeBuffer = static_cast(buffer); size_t remainingBytes = byteCount; while (remainingBytes > 0) { ssize_t writtenByteCount = write(fd, writeBuffer, remainingBytes); if (writtenByteCount == -1) { if (errno == EINTR) { continue; } __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Error writing to buffer: %d", errno); return false; } remainingBytes -= writtenByteCount; writeBuffer += writtenByteCount; } return true; } static bool readAllBytes(const int fd, void* buffer, const size_t byteCount) { char* readBuffer = static_cast(buffer); size_t remainingBytes = byteCount; while (remainingBytes > 0) { ssize_t readByteCount = read(fd, readBuffer, remainingBytes); remainingBytes -= readByteCount; readBuffer += readByteCount; if (readByteCount == -1) { if (errno == EINTR) { continue; } __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Error reading from buffer: %d", errno); return false; } else if (readByteCount == 0 && remainingBytes > 0) { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "File closed before all bytes were read. %zu/%zu remaining", remainingBytes, byteCount); return false; } } return true; } static void throwException(JNIEnv* env, const char* className, const char* message) { jclass exceptionClass = env->FindClass(className); env->ThrowNew(exceptionClass, message); } static void throwIllegalStateException(JNIEnv* env, char *message) { const char* className = "java/lang/IllegalStateException"; throwException(env, className, message); } static void throwIllegalArgumentException(JNIEnv* env, char* message) { const char* className = "java/lang/IllegalArgumentException"; throwException(env, className, message); } static void readBitmapPixels(JNIEnv* env, jclass /* clazz */, jobject jbitmap, jint fd) { // Read the info. AndroidBitmapInfo readInfo; bool read = readAllBytes(fd, (void*) &readInfo, sizeof(AndroidBitmapInfo)); if (!read) { throwIllegalStateException(env, (char*) "Cannot read bitmap info"); return; } // Get the info of the target bitmap. AndroidBitmapInfo targetInfo; int result = AndroidBitmap_getInfo(env, jbitmap, &targetInfo); if (result < 0) { throwIllegalStateException(env, (char*) "Cannot get bitmap info"); return; } // Enforce we can reuse the bitmap. if (readInfo.width != targetInfo.width || readInfo.height != targetInfo.height || readInfo.stride != targetInfo.stride || readInfo.format != targetInfo.format || readInfo.flags != targetInfo.flags) { throwIllegalArgumentException(env, (char*) "Cannot reuse bitmap"); return; } // Lock the pixels. void* pixels; result = AndroidBitmap_lockPixels(env, jbitmap, &pixels); if (result < 0) { throwIllegalStateException(env, (char*) "Cannot lock bitmap pixels"); return; } // Read the pixels. size_t byteCount = readInfo.stride * readInfo.height; read = readAllBytes(fd, (void*) pixels, byteCount); if (!read) { throwIllegalStateException(env, (char*) "Cannot read bitmap pixels"); return; } // Unlock the pixels. result = AndroidBitmap_unlockPixels(env, jbitmap); if (result < 0) { throwIllegalStateException(env, (char*) "Cannot unlock bitmap pixels"); } } static void writeBitmapPixels(JNIEnv* env, jclass /* clazz */, jobject jbitmap, jint fd) { // Get the info. AndroidBitmapInfo info; int result = AndroidBitmap_getInfo(env, jbitmap, &info); if (result < 0) { throwIllegalStateException(env, (char*) "Cannot get bitmap info"); return; } // Write the info. bool written = writeAllBytes(fd, (void*) &info, sizeof(AndroidBitmapInfo)); if (!written) { throwIllegalStateException(env, (char*) "Cannot write bitmap info"); return; } // Lock the pixels. void* pixels; result = AndroidBitmap_lockPixels(env, jbitmap, &pixels); if (result < 0) { throwIllegalStateException(env, (char*) "Cannot lock bitmap pixels"); return; } // Write the pixels. size_t byteCount = info.stride * info.height; written = writeAllBytes(fd, (void*) pixels, byteCount); if (!written) { throwIllegalStateException(env, (char*) "Cannot write bitmap pixels"); return; } // Unlock the pixels. result = AndroidBitmap_unlockPixels(env, jbitmap); if (result < 0) { throwIllegalStateException(env, (char*) "Cannot unlock bitmap pixels"); } } static const JNINativeMethod sMethods[] = { {"nativeReadBitmapPixels", "(Landroid/graphics/Bitmap;I)V", (void *) readBitmapPixels}, {"nativeWriteBitmapPixels", "(Landroid/graphics/Bitmap;I)V", (void *) writeBitmapPixels}, }; int register_com_android_printspooler_util_BitmapSerializeUtils(JNIEnv* env) { return jniRegisterNativeMethods(env, "com/android/printspooler/util/BitmapSerializeUtils", sMethods, NELEM(sMethods)); } } jint JNI_OnLoad(JavaVM* jvm, void*) { JNIEnv *env = NULL; if (jvm->GetEnv((void**) &env, JNI_VERSION_1_6)) { return JNI_ERR; } if (android::register_com_android_printspooler_util_BitmapSerializeUtils(env) == -1) { return JNI_ERR; } return JNI_VERSION_1_6; }