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
JpegWriter()24 JpegWriter::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
~JpegWriter()34 JpegWriter::~JpegWriter() {
35 if (reset() != J_SUCCESS) {
36 LOGE("Failed to destroy compress object, may leak memory.");
37 }
38 }
39
40 const int32_t JpegWriter::DEFAULT_X_DENSITY = 300;
41 const int32_t JpegWriter::DEFAULT_Y_DENSITY = 300;
42 const int32_t JpegWriter::DEFAULT_DENSITY_UNIT = 1;
43
setup(JNIEnv * env,jobject out,int32_t width,int32_t height,Jpeg_Config::Format format,int32_t quality)44 int32_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
write(int8_t * bytes,int32_t length)116 int32_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
formatPixels(uint8_t * buf,int32_t len)166 void 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
updateEnv(JNIEnv * env)202 void JpegWriter::updateEnv(JNIEnv *env) {
203 UpdateDstEnv(&mInfo, env);
204 }
205
reset()206 int32_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