1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "BitmapSerializeUtils" 18 19#include <jni.h> 20#include <nativehelper/JNIHelp.h> 21 22#include <android/bitmap.h> 23#include <android/log.h> 24 25namespace android { 26 27#define RGBA_8888_COLOR_DEPTH 4 28 29static bool writeAllBytes(const int fd, void* buffer, const size_t byteCount) { 30 char* writeBuffer = static_cast<char*>(buffer); 31 size_t remainingBytes = byteCount; 32 while (remainingBytes > 0) { 33 ssize_t writtenByteCount = write(fd, writeBuffer, remainingBytes); 34 if (writtenByteCount == -1) { 35 if (errno == EINTR) { 36 continue; 37 } 38 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, 39 "Error writing to buffer: %d", errno); 40 return false; 41 } 42 remainingBytes -= writtenByteCount; 43 writeBuffer += writtenByteCount; 44 } 45 return true; 46} 47 48static bool readAllBytes(const int fd, void* buffer, const size_t byteCount) { 49 char* readBuffer = static_cast<char*>(buffer); 50 size_t remainingBytes = byteCount; 51 while (remainingBytes > 0) { 52 ssize_t readByteCount = read(fd, readBuffer, remainingBytes); 53 54 remainingBytes -= readByteCount; 55 readBuffer += readByteCount; 56 57 if (readByteCount == -1) { 58 if (errno == EINTR) { 59 continue; 60 } 61 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, 62 "Error reading from buffer: %d", errno); 63 return false; 64 } else if (readByteCount == 0 && remainingBytes > 0) { 65 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, 66 "File closed before all bytes were read. %zu/%zu remaining", remainingBytes, 67 byteCount); 68 return false; 69 } 70 } 71 return true; 72} 73 74static void throwException(JNIEnv* env, const char* className, const char* message) { 75 jclass exceptionClass = env->FindClass(className); 76 env->ThrowNew(exceptionClass, message); 77} 78 79static void throwIllegalStateException(JNIEnv* env, char *message) { 80 const char* className = "java/lang/IllegalStateException"; 81 throwException(env, className, message); 82} 83 84static void throwIllegalArgumentException(JNIEnv* env, char* message) { 85 const char* className = "java/lang/IllegalArgumentException"; 86 throwException(env, className, message); 87} 88 89static void readBitmapPixels(JNIEnv* env, jclass /* clazz */, jobject jbitmap, jint fd) { 90 // Read the info. 91 AndroidBitmapInfo readInfo; 92 bool read = readAllBytes(fd, (void*) &readInfo, sizeof(AndroidBitmapInfo)); 93 if (!read) { 94 throwIllegalStateException(env, (char*) "Cannot read bitmap info"); 95 return; 96 } 97 98 // Get the info of the target bitmap. 99 AndroidBitmapInfo targetInfo; 100 int result = AndroidBitmap_getInfo(env, jbitmap, &targetInfo); 101 if (result < 0) { 102 throwIllegalStateException(env, (char*) "Cannot get bitmap info"); 103 return; 104 } 105 106 // Enforce we can reuse the bitmap. 107 if (readInfo.width != targetInfo.width || readInfo.height != targetInfo.height 108 || readInfo.stride != targetInfo.stride || readInfo.format != targetInfo.format 109 || readInfo.flags != targetInfo.flags) { 110 throwIllegalArgumentException(env, (char*) "Cannot reuse bitmap"); 111 return; 112 } 113 114 // Lock the pixels. 115 void* pixels; 116 result = AndroidBitmap_lockPixels(env, jbitmap, &pixels); 117 if (result < 0) { 118 throwIllegalStateException(env, (char*) "Cannot lock bitmap pixels"); 119 return; 120 } 121 122 // Read the pixels. 123 size_t byteCount = readInfo.stride * readInfo.height; 124 read = readAllBytes(fd, (void*) pixels, byteCount); 125 if (!read) { 126 throwIllegalStateException(env, (char*) "Cannot read bitmap pixels"); 127 return; 128 } 129 130 // Unlock the pixels. 131 result = AndroidBitmap_unlockPixels(env, jbitmap); 132 if (result < 0) { 133 throwIllegalStateException(env, (char*) "Cannot unlock bitmap pixels"); 134 } 135} 136 137static void writeBitmapPixels(JNIEnv* env, jclass /* clazz */, jobject jbitmap, jint fd) { 138 // Get the info. 139 AndroidBitmapInfo info; 140 int result = AndroidBitmap_getInfo(env, jbitmap, &info); 141 if (result < 0) { 142 throwIllegalStateException(env, (char*) "Cannot get bitmap info"); 143 return; 144 } 145 146 // Write the info. 147 bool written = writeAllBytes(fd, (void*) &info, sizeof(AndroidBitmapInfo)); 148 if (!written) { 149 throwIllegalStateException(env, (char*) "Cannot write bitmap info"); 150 return; 151 } 152 153 // Lock the pixels. 154 void* pixels; 155 result = AndroidBitmap_lockPixels(env, jbitmap, &pixels); 156 if (result < 0) { 157 throwIllegalStateException(env, (char*) "Cannot lock bitmap pixels"); 158 return; 159 } 160 161 // Write the pixels. 162 size_t byteCount = info.stride * info.height; 163 written = writeAllBytes(fd, (void*) pixels, byteCount); 164 if (!written) { 165 throwIllegalStateException(env, (char*) "Cannot write bitmap pixels"); 166 return; 167 } 168 169 // Unlock the pixels. 170 result = AndroidBitmap_unlockPixels(env, jbitmap); 171 if (result < 0) { 172 throwIllegalStateException(env, (char*) "Cannot unlock bitmap pixels"); 173 } 174} 175 176static const JNINativeMethod sMethods[] = { 177 {"nativeReadBitmapPixels", "(Landroid/graphics/Bitmap;I)V", (void *) readBitmapPixels}, 178 {"nativeWriteBitmapPixels", "(Landroid/graphics/Bitmap;I)V", (void *) writeBitmapPixels}, 179}; 180 181int register_com_android_printspooler_util_BitmapSerializeUtils(JNIEnv* env) { 182 return jniRegisterNativeMethods(env, "com/android/printspooler/util/BitmapSerializeUtils", 183 sMethods, NELEM(sMethods)); 184} 185 186} 187 188jint JNI_OnLoad(JavaVM* jvm, void*) { 189 JNIEnv *env = NULL; 190 if (jvm->GetEnv((void**) &env, JNI_VERSION_1_6)) { 191 return JNI_ERR; 192 } 193 194 if (android::register_com_android_printspooler_util_BitmapSerializeUtils(env) == -1) { 195 return JNI_ERR; 196 } 197 198 return JNI_VERSION_1_6; 199} 200