1/* 2 * Copyright (C) 2013 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#include "jpeg_hook.h" 18#include "jpeg_writer.h" 19#include "error_codes.h" 20 21#include <setjmp.h> 22#include <assert.h> 23 24JpegWriter::JpegWriter() : mInfo(), 25 mErrorManager(), 26 mScanlineBuf(NULL), 27 mScanlineIter(NULL), 28 mScanlineBuflen(0), 29 mScanlineBytesRemaining(0), 30 mFormat(), 31 mFinished(false), 32 mSetup(false) {} 33 34JpegWriter::~JpegWriter() { 35 if (reset() != J_SUCCESS) { 36 LOGE("Failed to destroy compress object, may leak memory."); 37 } 38} 39 40const int32_t JpegWriter::DEFAULT_X_DENSITY = 300; 41const int32_t JpegWriter::DEFAULT_Y_DENSITY = 300; 42const int32_t JpegWriter::DEFAULT_DENSITY_UNIT = 1; 43 44int32_t JpegWriter::setup(JNIEnv *env, jobject out, int32_t width, int32_t height, 45 Jpeg_Config::Format format, int32_t quality) { 46 if (mFinished || mSetup) { 47 return J_ERROR_FATAL; 48 } 49 if (env->ExceptionCheck()) { 50 return J_EXCEPTION; 51 } 52 if (height <= 0 || width <= 0 || quality <= 0 || quality > 100) { 53 return J_ERROR_BAD_ARGS; 54 } 55 // Setup error handler 56 SetupErrMgr(reinterpret_cast<j_common_ptr>(&mInfo), &mErrorManager); 57 58 // Set jump address for error handling 59 if (setjmp(mErrorManager.setjmp_buf)) { 60 return J_ERROR_FATAL; 61 } 62 63 // Setup cinfo struct 64 jpeg_create_compress(&mInfo); 65 66 // Setup global java refs 67 int32_t flags = MakeDst(&mInfo, env, out); 68 if (flags != J_SUCCESS) { 69 return flags; 70 } 71 72 // Initialize width, height, and color space 73 mInfo.image_width = width; 74 mInfo.image_height = height; 75 const int components = (static_cast<int>(format) & 0xff); 76 switch (components) { 77 case 1: 78 mInfo.input_components = 1; 79 mInfo.in_color_space = JCS_GRAYSCALE; 80 break; 81 case 3: 82 case 4: 83 mInfo.input_components = 3; 84 mInfo.in_color_space = JCS_RGB; 85 break; 86 default: 87 return J_ERROR_BAD_ARGS; 88 } 89 90 // Set defaults 91 jpeg_set_defaults(&mInfo); 92 mInfo.density_unit = DEFAULT_DENSITY_UNIT; // JFIF code for pixel size units: 93 // 1 = in, 2 = cm 94 mInfo.X_density = DEFAULT_X_DENSITY; // Horizontal pixel density 95 mInfo.Y_density = DEFAULT_Y_DENSITY; // Vertical pixel density 96 97 // Set compress quality 98 jpeg_set_quality(&mInfo, quality, TRUE); 99 100 mFormat = format; 101 102 // Setup scanline buffer 103 mScanlineBuflen = width * components; 104 mScanlineBytesRemaining = mScanlineBuflen; 105 mScanlineBuf = (JSAMPLE *) (mInfo.mem->alloc_small)( 106 reinterpret_cast<j_common_ptr>(&mInfo), JPOOL_PERMANENT, 107 mScanlineBuflen * sizeof(JSAMPLE)); 108 mScanlineIter = mScanlineBuf; 109 110 // Start compression 111 jpeg_start_compress(&mInfo, TRUE); 112 mSetup = true; 113 return J_SUCCESS; 114} 115 116int32_t JpegWriter::write(int8_t* bytes, int32_t length) { 117 if (!mSetup) { 118 return J_ERROR_FATAL; 119 } 120 if (mFinished) { 121 return 0; 122 } 123 // Set jump address for error handling 124 if (setjmp(mErrorManager.setjmp_buf)) { 125 return J_ERROR_FATAL; 126 } 127 if (length < 0 || bytes == NULL) { 128 return J_ERROR_BAD_ARGS; 129 } 130 131 int32_t total_length = length; 132 JSAMPROW row_pointer[1]; 133 while (mInfo.next_scanline < mInfo.image_height) { 134 if (length < mScanlineBytesRemaining) { 135 // read partial scanline and return 136 memcpy((void*) mScanlineIter, (void*) bytes, 137 length * sizeof(int8_t)); 138 mScanlineBytesRemaining -= length; 139 mScanlineIter += length; 140 return total_length; 141 } else if (length > 0) { 142 // read full scanline 143 memcpy((void*) mScanlineIter, (void*) bytes, 144 mScanlineBytesRemaining * sizeof(int8_t)); 145 bytes += mScanlineBytesRemaining; 146 length -= mScanlineBytesRemaining; 147 mScanlineBytesRemaining = 0; 148 } 149 // Do in-place pixel formatting 150 formatPixels(static_cast<uint8_t*>(mScanlineBuf), mScanlineBuflen); 151 row_pointer[0] = mScanlineBuf; 152 // Do compression 153 if (jpeg_write_scanlines(&mInfo, row_pointer, 1) != 1) { 154 return J_ERROR_FATAL; 155 } 156 // Reset scanline buffer 157 mScanlineBytesRemaining = mScanlineBuflen; 158 mScanlineIter = mScanlineBuf; 159 } 160 jpeg_finish_compress(&mInfo); 161 mFinished = true; 162 return total_length - length; 163} 164 165// Does in-place pixel formatting 166void JpegWriter::formatPixels(uint8_t* buf, int32_t len) { 167 // Assumes len is a multiple of 4 for RGBA and ABGR pixels. 168 assert((len % 4) == 0); 169 uint8_t* d = buf; 170 switch (mFormat) { 171 case Jpeg_Config::FORMAT_RGBA: { 172 // Strips alphas 173 for (int i = 0; i < len / 4; ++i, buf += 4) { 174 *d++ = buf[0]; 175 *d++ = buf[1]; 176 *d++ = buf[2]; 177 } 178 break; 179 } 180 case Jpeg_Config::FORMAT_ABGR: { 181 // Strips alphas and flips endianness 182 if (len / 4 >= 1) { 183 *d++ = buf[3]; 184 uint8_t tmp = *d; 185 *d++ = buf[2]; 186 *d++ = tmp; 187 } 188 for (int i = 1; i < len / 4; ++i, buf += 4) { 189 *d++ = buf[3]; 190 *d++ = buf[2]; 191 *d++ = buf[1]; 192 } 193 break; 194 } 195 default: { 196 // Do nothing 197 break; 198 } 199 } 200} 201 202void JpegWriter::updateEnv(JNIEnv *env) { 203 UpdateDstEnv(&mInfo, env); 204} 205 206int32_t JpegWriter::reset() { 207 // Set jump address for error handling 208 if (setjmp(mErrorManager.setjmp_buf)) { 209 return J_ERROR_FATAL; 210 } 211 // Clean up global java references 212 CleanDst(&mInfo); 213 // Wipe compress struct, free memory pools 214 jpeg_destroy_compress(&mInfo); 215 mFinished = false; 216 mSetup = false; 217 return J_SUCCESS; 218} 219