summaryrefslogtreecommitdiffstats
path: root/jni_jpegstream/src/jpeg_reader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'jni_jpegstream/src/jpeg_reader.cpp')
-rw-r--r--jni_jpegstream/src/jpeg_reader.cpp254
1 files changed, 254 insertions, 0 deletions
diff --git a/jni_jpegstream/src/jpeg_reader.cpp b/jni_jpegstream/src/jpeg_reader.cpp
new file mode 100644
index 000000000..4726b6426
--- /dev/null
+++ b/jni_jpegstream/src/jpeg_reader.cpp
@@ -0,0 +1,254 @@
+/*
+ * 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_reader.h"
+#include "error_codes.h"
+#include "jpeg_hook.h"
+
+#include <setjmp.h>
+
+JpegReader::JpegReader() :
+ mInfo(),
+ mErrorManager(),
+ mScanlineBuf(NULL),
+ mScanlineIter(NULL),
+ mScanlineBuflen(0),
+ mScanlineUnformattedBuflen(0),
+ mScanlineBytesRemaining(0),
+ mFormat(),
+ mFinished(false),
+ mSetup(false) {}
+
+JpegReader::~JpegReader() {
+ if (reset() != J_SUCCESS) {
+ LOGE("Failed to destroy compress object, JpegReader may leak memory.");
+ }
+}
+
+int32_t JpegReader::setup(JNIEnv *env, jobject in, int32_t* width, int32_t* height,
+ Jpeg_Config::Format format) {
+ if (mFinished || mSetup) {
+ return J_ERROR_FATAL;
+ }
+ if (env->ExceptionCheck()) {
+ return J_EXCEPTION;
+ }
+
+ // 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;
+ }
+
+ // Call libjpeg setup
+ jpeg_create_decompress(&mInfo);
+
+ // Setup our data source object, this allocates java global references
+ int32_t flags = MakeSrc(&mInfo, env, in);
+ if (flags != J_SUCCESS) {
+ LOGE("Failed to make source with error code: %d ", flags);
+ return flags;
+ }
+
+ // Reads jpeg file header
+ jpeg_read_header(&mInfo, TRUE);
+ jpeg_calc_output_dimensions(&mInfo);
+
+ const int components = (static_cast<int>(format) & 0xff);
+
+ // Do setup for input format
+ switch (components) {
+ case 1:
+ mInfo.out_color_space = JCS_GRAYSCALE;
+ mScanlineUnformattedBuflen = mInfo.output_width;
+ break;
+ case 3:
+ case 4:
+ mScanlineUnformattedBuflen = mInfo.output_width * components;
+ if (mInfo.jpeg_color_space == JCS_CMYK
+ || mInfo.jpeg_color_space == JCS_YCCK) {
+ // Always use cmyk for output in a 4 channel jpeg.
+ // libjpeg has a builtin cmyk->rgb decoder.
+ mScanlineUnformattedBuflen = mInfo.output_width * 4;
+ mInfo.out_color_space = JCS_CMYK;
+ } else {
+ mInfo.out_color_space = JCS_RGB;
+ }
+ break;
+ default:
+ return J_ERROR_BAD_ARGS;
+ }
+
+ mScanlineBuflen = mInfo.output_width * components;
+ mScanlineBytesRemaining = mScanlineBuflen;
+ mScanlineBuf = (JSAMPLE *) (mInfo.mem->alloc_small)(
+ reinterpret_cast<j_common_ptr>(&mInfo), JPOOL_PERMANENT,
+ mScanlineUnformattedBuflen * sizeof(JSAMPLE));
+ mScanlineIter = mScanlineBuf;
+ jpeg_start_decompress(&mInfo);
+
+ // Output image dimensions
+ if (width != NULL) {
+ *width = mInfo.output_width;
+ }
+ if (height != NULL) {
+ *height = mInfo.output_height;
+ }
+
+ mFormat = format;
+ mSetup = true;
+ return J_SUCCESS;
+}
+
+int32_t JpegReader::read(int8_t* bytes, int32_t offset, int32_t count) {
+ if (!mSetup) {
+ return J_ERROR_FATAL;
+ }
+ if (mFinished) {
+ return J_DONE;
+ }
+ // Set jump address for error handling
+ if (setjmp(mErrorManager.setjmp_buf)) {
+ return J_ERROR_FATAL;
+ }
+ if (count <= 0) {
+ return J_ERROR_BAD_ARGS;
+ }
+ int32_t total_length = count;
+ while (mInfo.output_scanline < mInfo.output_height) {
+ if (count < mScanlineBytesRemaining) {
+ // read partial scanline and return
+ if (bytes != NULL) {
+ // Treat NULL bytes as a skip
+ memcpy((void*) (bytes + offset), (void*) mScanlineIter,
+ count * sizeof(int8_t));
+ }
+ mScanlineBytesRemaining -= count;
+ mScanlineIter += count;
+ return total_length;
+ } else if (count > 0) {
+ // read full scanline
+ if (bytes != NULL) {
+ // Treat NULL bytes as a skip
+ memcpy((void*) (bytes + offset), (void*) mScanlineIter,
+ mScanlineBytesRemaining * sizeof(int8_t));
+ bytes += mScanlineBytesRemaining;
+ }
+ count -= mScanlineBytesRemaining;
+ mScanlineBytesRemaining = 0;
+ }
+ // Scanline buffer exhausted, read next scanline
+ if (jpeg_read_scanlines(&mInfo, &mScanlineBuf, 1) != 1) {
+ // Always read full scanline, no IO suspension
+ return J_ERROR_FATAL;
+ }
+ // Do in-place pixel formatting
+ formatPixels(static_cast<uint8_t*>(mScanlineBuf),
+ mScanlineUnformattedBuflen);
+
+ // Reset iterators
+ mScanlineIter = mScanlineBuf;
+ mScanlineBytesRemaining = mScanlineBuflen;
+ }
+
+ // Read all of the scanlines
+ jpeg_finish_decompress(&mInfo);
+ mFinished = true;
+ return total_length - count;
+}
+
+void JpegReader::updateEnv(JNIEnv *env) {
+ UpdateSrcEnv(&mInfo, env);
+}
+
+// Does in-place pixel formatting
+void JpegReader::formatPixels(uint8_t* buf, int32_t len) {
+ uint8_t *iter = buf;
+
+ // Do cmyk->rgb conversion if necessary
+ switch (mInfo.out_color_space) {
+ case JCS_CMYK:
+ // Convert CMYK to RGB
+ int r, g, b, c, m, y, k;
+ for (int i = 0; i < len; i += 4) {
+ c = buf[i + 0];
+ m = buf[i + 1];
+ y = buf[i + 2];
+ k = buf[i + 3];
+ // Handle fmt for weird photoshop markers
+ if (mInfo.saw_Adobe_marker) {
+ r = (k * c) / 255;
+ g = (k * m) / 255;
+ b = (k * y) / 255;
+ } else {
+ r = (255 - k) * (255 - c) / 255;
+ g = (255 - k) * (255 - m) / 255;
+ b = (255 - k) * (255 - y) / 255;
+ }
+ *iter++ = r;
+ *iter++ = g;
+ *iter++ = b;
+ }
+ break;
+ case JCS_RGB:
+ iter += (len * 3 / 4);
+ break;
+ case JCS_GRAYSCALE:
+ default:
+ return;
+ }
+
+ // Do endianness and alpha for output format
+ if (mFormat == Jpeg_Config::FORMAT_RGBA) {
+ // Set alphas to 255
+ uint8_t* end = buf + len - 1;
+ for (int i = len - 1; i >= 0; i -= 4) {
+ buf[i] = 255;
+ buf[i - 1] = *--iter;
+ buf[i - 2] = *--iter;
+ buf[i - 3] = *--iter;
+ }
+ } else if (mFormat == Jpeg_Config::FORMAT_ABGR) {
+ // Reverse endianness and set alphas to 255
+ uint8_t* end = buf + len - 1;
+ int r, g, b;
+ for (int i = len - 1; i >= 0; i -= 4) {
+ b = *--iter;
+ g = *--iter;
+ r = *--iter;
+ buf[i] = r;
+ buf[i - 1] = g;
+ buf[i - 2] = b;
+ buf[i - 3] = 255;
+ }
+ }
+}
+
+int32_t JpegReader::reset() {
+ // Set jump address for error handling
+ if (setjmp(mErrorManager.setjmp_buf)) {
+ return J_ERROR_FATAL;
+ }
+ // Clean up global java references
+ CleanSrc(&mInfo);
+ // Wipe decompress struct, free memory pools
+ jpeg_destroy_decompress(&mInfo);
+ mFinished = false;
+ mSetup = false;
+ return J_SUCCESS;
+}
+