summaryrefslogtreecommitdiffstats
path: root/jni_jpegstream/src/jpeg_writer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'jni_jpegstream/src/jpeg_writer.cpp')
-rw-r--r--jni_jpegstream/src/jpeg_writer.cpp218
1 files changed, 218 insertions, 0 deletions
diff --git a/jni_jpegstream/src/jpeg_writer.cpp b/jni_jpegstream/src/jpeg_writer.cpp
new file mode 100644
index 0000000..4f78917
--- /dev/null
+++ b/jni_jpegstream/src/jpeg_writer.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "jpeg_hook.h"
+#include "jpeg_writer.h"
+#include "error_codes.h"
+
+#include <setjmp.h>
+#include <assert.h>
+
+JpegWriter::JpegWriter() : mInfo(),
+ mErrorManager(),
+ mScanlineBuf(NULL),
+ mScanlineIter(NULL),
+ mScanlineBuflen(0),
+ mScanlineBytesRemaining(0),
+ mFormat(),
+ mFinished(false),
+ mSetup(false) {}
+
+JpegWriter::~JpegWriter() {
+ if (reset() != J_SUCCESS) {
+ LOGE("Failed to destroy compress object, may leak memory.");
+ }
+}
+
+const int32_t JpegWriter::DEFAULT_X_DENSITY = 300;
+const int32_t JpegWriter::DEFAULT_Y_DENSITY = 300;
+const int32_t JpegWriter::DEFAULT_DENSITY_UNIT = 1;
+
+int32_t JpegWriter::setup(JNIEnv *env, jobject out, int32_t width, int32_t height,
+ Jpeg_Config::Format format, int32_t quality) {
+ if (mFinished || mSetup) {
+ return J_ERROR_FATAL;
+ }
+ if (env->ExceptionCheck()) {
+ return J_EXCEPTION;
+ }
+ if (height <= 0 || width <= 0 || quality <= 0 || quality > 100) {
+ return J_ERROR_BAD_ARGS;
+ }
+ // Setup error handler
+ SetupErrMgr(reinterpret_cast<j_common_ptr>(&mInfo), &mErrorManager);
+
+ // Set jump address for error handling
+ if (setjmp(mErrorManager.setjmp_buf)) {
+ return J_ERROR_FATAL;
+ }
+
+ // Setup cinfo struct
+ jpeg_create_compress(&mInfo);
+
+ // Setup global java refs
+ int32_t flags = MakeDst(&mInfo, env, out);
+ if (flags != J_SUCCESS) {
+ return flags;
+ }
+
+ // Initialize width, height, and color space
+ mInfo.image_width = width;
+ mInfo.image_height = height;
+ const int components = (static_cast<int>(format) & 0xff);
+ switch (components) {
+ case 1:
+ mInfo.input_components = 1;
+ mInfo.in_color_space = JCS_GRAYSCALE;
+ break;
+ case 3:
+ case 4:
+ mInfo.input_components = 3;
+ mInfo.in_color_space = JCS_RGB;
+ break;
+ default:
+ return J_ERROR_BAD_ARGS;
+ }
+
+ // Set defaults
+ jpeg_set_defaults(&mInfo);
+ mInfo.density_unit = DEFAULT_DENSITY_UNIT; // JFIF code for pixel size units:
+ // 1 = in, 2 = cm
+ mInfo.X_density = DEFAULT_X_DENSITY; // Horizontal pixel density
+ mInfo.Y_density = DEFAULT_Y_DENSITY; // Vertical pixel density
+
+ // Set compress quality
+ jpeg_set_quality(&mInfo, quality, TRUE);
+
+ mFormat = format;
+
+ // Setup scanline buffer
+ mScanlineBuflen = width * components;
+ mScanlineBytesRemaining = mScanlineBuflen;
+ mScanlineBuf = (JSAMPLE *) (mInfo.mem->alloc_small)(
+ reinterpret_cast<j_common_ptr>(&mInfo), JPOOL_PERMANENT,
+ mScanlineBuflen * sizeof(JSAMPLE));
+ mScanlineIter = mScanlineBuf;
+
+ // Start compression
+ jpeg_start_compress(&mInfo, TRUE);
+ mSetup = true;
+ return J_SUCCESS;
+}
+
+int32_t JpegWriter::write(int8_t* bytes, int32_t length) {
+ if (!mSetup) {
+ return J_ERROR_FATAL;
+ }
+ if (mFinished) {
+ return 0;
+ }
+ // Set jump address for error handling
+ if (setjmp(mErrorManager.setjmp_buf)) {
+ return J_ERROR_FATAL;
+ }
+ if (length < 0 || bytes == NULL) {
+ return J_ERROR_BAD_ARGS;
+ }
+
+ int32_t total_length = length;
+ JSAMPROW row_pointer[1];
+ while (mInfo.next_scanline < mInfo.image_height) {
+ if (length < mScanlineBytesRemaining) {
+ // read partial scanline and return
+ memcpy((void*) mScanlineIter, (void*) bytes,
+ length * sizeof(int8_t));
+ mScanlineBytesRemaining -= length;
+ mScanlineIter += length;
+ return total_length;
+ } else if (length > 0) {
+ // read full scanline
+ memcpy((void*) mScanlineIter, (void*) bytes,
+ mScanlineBytesRemaining * sizeof(int8_t));
+ bytes += mScanlineBytesRemaining;
+ length -= mScanlineBytesRemaining;
+ mScanlineBytesRemaining = 0;
+ }
+ // Do in-place pixel formatting
+ formatPixels(static_cast<uint8_t*>(mScanlineBuf), mScanlineBuflen);
+ row_pointer[0] = mScanlineBuf;
+ // Do compression
+ if (jpeg_write_scanlines(&mInfo, row_pointer, 1) != 1) {
+ return J_ERROR_FATAL;
+ }
+ // Reset scanline buffer
+ mScanlineBytesRemaining = mScanlineBuflen;
+ mScanlineIter = mScanlineBuf;
+ }
+ jpeg_finish_compress(&mInfo);
+ mFinished = true;
+ return total_length - length;
+}
+
+// Does in-place pixel formatting
+void JpegWriter::formatPixels(uint8_t* buf, int32_t len) {
+ // Assumes len is a multiple of 4 for RGBA and ABGR pixels.
+ assert((len % 4) == 0);
+ uint8_t* d = buf;
+ switch (mFormat) {
+ case Jpeg_Config::FORMAT_RGBA: {
+ // Strips alphas
+ for (int i = 0; i < len / 4; ++i, buf += 4) {
+ *d++ = buf[0];
+ *d++ = buf[1];
+ *d++ = buf[2];
+ }
+ break;
+ }
+ case Jpeg_Config::FORMAT_ABGR: {
+ // Strips alphas and flips endianness
+ if (len / 4 >= 1) {
+ *d++ = buf[3];
+ uint8_t tmp = *d;
+ *d++ = buf[2];
+ *d++ = tmp;
+ }
+ for (int i = 1; i < len / 4; ++i, buf += 4) {
+ *d++ = buf[3];
+ *d++ = buf[2];
+ *d++ = buf[1];
+ }
+ break;
+ }
+ default: {
+ // Do nothing
+ break;
+ }
+ }
+}
+
+void JpegWriter::updateEnv(JNIEnv *env) {
+ UpdateDstEnv(&mInfo, env);
+}
+
+int32_t JpegWriter::reset() {
+ // Set jump address for error handling
+ if (setjmp(mErrorManager.setjmp_buf)) {
+ return J_ERROR_FATAL;
+ }
+ // Clean up global java references
+ CleanDst(&mInfo);
+ // Wipe compress struct, free memory pools
+ jpeg_destroy_compress(&mInfo);
+ mFinished = false;
+ mSetup = false;
+ return J_SUCCESS;
+}