| /* |
| * Copyright (C) 2009 Torch Mobile, Inc. All rights reserved. |
| * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| |
| #include "config.h" |
| #include "JPEGImageEncoder.h" |
| |
| #include "IntSize.h" |
| // FIXME: jpeglib.h requires stdio.h to be included first for FILE |
| #include <stdio.h> |
| #include <jpeglib.h> |
| #include <setjmp.h> |
| |
| namespace WebCore { |
| |
| class JPEGDestinationManager : public jpeg_destination_mgr { |
| public: |
| explicit JPEGDestinationManager(Vector<char>& toDump) |
| : m_dump(toDump) |
| { |
| // Zero base class memory. |
| jpeg_destination_mgr* base = this; |
| memset(base, 0, sizeof(jpeg_destination_mgr)); |
| } |
| Vector<char> m_buffer; |
| Vector<char>& m_dump; |
| }; |
| |
| class JPEGCompressErrorMgr : public jpeg_error_mgr { |
| public: |
| JPEGCompressErrorMgr() |
| { |
| // Zero memory |
| memset(this, 0, sizeof(JPEGCompressErrorMgr)); |
| } |
| |
| jmp_buf m_setjmpBuffer; |
| }; |
| |
| static void jpegInitializeDestination(j_compress_ptr compressData) |
| { |
| JPEGDestinationManager* dest = static_cast<JPEGDestinationManager*>(compressData->dest); |
| dest->m_buffer.resize(4096); |
| dest->next_output_byte = reinterpret_cast<JOCTET*>(dest->m_buffer.data()); |
| dest->free_in_buffer = dest->m_buffer.size(); |
| } |
| |
| static boolean jpegEmptyOutputBuffer(j_compress_ptr compressData) |
| { |
| JPEGDestinationManager* dest = static_cast<JPEGDestinationManager*>(compressData->dest); |
| dest->m_dump.append(dest->m_buffer.data(), dest->m_buffer.size()); |
| dest->next_output_byte = reinterpret_cast<JOCTET*>(dest->m_buffer.data()); |
| dest->free_in_buffer = dest->m_buffer.size(); |
| return TRUE; |
| } |
| |
| static void jpegTerminateDestination(j_compress_ptr compressData) |
| { |
| JPEGDestinationManager* dest = static_cast<JPEGDestinationManager*>(compressData->dest); |
| dest->m_dump.append(dest->m_buffer.data(), dest->m_buffer.size() - dest->free_in_buffer); |
| } |
| |
| static void jpegErrorExit(j_common_ptr compressData) |
| { |
| JPEGCompressErrorMgr* err = static_cast<JPEGCompressErrorMgr*>(compressData->err); |
| longjmp(err->m_setjmpBuffer, -1); |
| } |
| |
| bool compressRGBABigEndianToJPEG(unsigned char* rgbaBigEndianData, const IntSize& size, Vector<char>& jpegData, const double* quality) |
| { |
| struct jpeg_compress_struct compressData; |
| JPEGCompressErrorMgr err; |
| compressData.err = jpeg_std_error(&err); |
| err.error_exit = jpegErrorExit; |
| |
| jpeg_create_compress(&compressData); |
| |
| JPEGDestinationManager dest(jpegData); |
| compressData.dest = &dest; |
| dest.init_destination = jpegInitializeDestination; |
| dest.empty_output_buffer = jpegEmptyOutputBuffer; |
| dest.term_destination = jpegTerminateDestination; |
| |
| compressData.image_width = size.width(); |
| compressData.image_height = size.height(); |
| compressData.input_components = 3; |
| compressData.in_color_space = JCS_RGB; |
| jpeg_set_defaults(&compressData); |
| int compressionQuality = 65; |
| |
| if (quality && *quality >= 0.0 && *quality <= 1.0) |
| compressionQuality = static_cast<int>(*quality * 100 + 0.5); |
| |
| jpeg_set_quality(&compressData, compressionQuality, TRUE); |
| |
| // rowBuffer must be defined here so that its destructor is always called even when "setjmp" catches an error. |
| Vector<JSAMPLE, 600 * 3> rowBuffer; |
| |
| if (setjmp(err.m_setjmpBuffer)) { |
| jpeg_destroy_compress(&compressData); |
| return false; |
| } |
| |
| jpeg_start_compress(&compressData, TRUE); |
| rowBuffer.resize(compressData.image_width * 3); |
| |
| // add offset to prevent clobbered error caused by setjmp and longjmp. |
| unsigned offset = 0; |
| const unsigned char* pixel = rgbaBigEndianData; |
| const unsigned char* pixelEnd = pixel + compressData.image_width * compressData.image_height * 4; |
| while (pixel + offset < pixelEnd) { |
| JSAMPLE* output = rowBuffer.data(); |
| for (const unsigned char* rowEnd = pixel + offset + compressData.image_width * 4; pixel + offset < rowEnd;) { |
| *output++ = static_cast<JSAMPLE>(*(pixel + offset++) & 0xFF); // red |
| *output++ = static_cast<JSAMPLE>(*(pixel + offset++) & 0xFF); // green |
| *output++ = static_cast<JSAMPLE>(*(pixel + offset++) & 0xFF); // blue |
| ++offset; // skip alpha |
| } |
| output = rowBuffer.data(); |
| jpeg_write_scanlines(&compressData, &output, 1); |
| } |
| |
| jpeg_finish_compress(&compressData); |
| jpeg_destroy_compress(&compressData); |
| return true; |
| } |
| |
| } // namespace WebCore |