1 /* 2 * Copyright (C) 2009 Torch Mobile, Inc. All rights reserved. 3 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 */ 20 21 22 #include "config.h" 23 #include "JPEGImageEncoder.h" 24 25 #include "IntSize.h" 26 // FIXME: jpeglib.h requires stdio.h to be included first for FILE 27 #include <stdio.h> 28 #include "jpeglib.h" 29 #include <setjmp.h> 30 31 namespace WebCore { 32 33 class JPEGDestinationManager : public jpeg_destination_mgr { 34 public: 35 explicit JPEGDestinationManager(Vector<char>& toDump) 36 : m_dump(toDump) 37 { 38 // Zero base class memory. 39 jpeg_destination_mgr* base = this; 40 memset(base, 0, sizeof(jpeg_destination_mgr)); 41 } 42 Vector<char> m_buffer; 43 Vector<char>& m_dump; 44 }; 45 46 class JPEGCompressErrorMgr : public jpeg_error_mgr { 47 public: 48 JPEGCompressErrorMgr() 49 { 50 // Zero memory 51 memset(this, 0, sizeof(JPEGCompressErrorMgr)); 52 } 53 54 jmp_buf m_setjmpBuffer; 55 }; 56 57 static void jpegInitializeDestination(j_compress_ptr compressData) 58 { 59 JPEGDestinationManager* dest = static_cast<JPEGDestinationManager*>(compressData->dest); 60 dest->m_buffer.resize(4096); 61 dest->next_output_byte = reinterpret_cast<JOCTET*>(dest->m_buffer.data()); 62 dest->free_in_buffer = dest->m_buffer.size(); 63 } 64 65 static boolean jpegEmptyOutputBuffer(j_compress_ptr compressData) 66 { 67 JPEGDestinationManager* dest = static_cast<JPEGDestinationManager*>(compressData->dest); 68 dest->m_dump.append(dest->m_buffer.data(), dest->m_buffer.size()); 69 dest->next_output_byte = reinterpret_cast<JOCTET*>(dest->m_buffer.data()); 70 dest->free_in_buffer = dest->m_buffer.size(); 71 return TRUE; 72 } 73 74 static void jpegTerminateDestination(j_compress_ptr compressData) 75 { 76 JPEGDestinationManager* dest = static_cast<JPEGDestinationManager*>(compressData->dest); 77 dest->m_dump.append(dest->m_buffer.data(), dest->m_buffer.size() - dest->free_in_buffer); 78 } 79 80 static void jpegErrorExit(j_common_ptr compressData) 81 { 82 JPEGCompressErrorMgr* err = static_cast<JPEGCompressErrorMgr*>(compressData->err); 83 longjmp(err->m_setjmpBuffer, -1); 84 } 85 86 bool compressRGBABigEndianToJPEG(unsigned char* rgbaBigEndianData, const IntSize& size, Vector<char>& jpegData) 87 { 88 struct jpeg_compress_struct compressData = { 0 }; 89 JPEGCompressErrorMgr err; 90 compressData.err = jpeg_std_error(&err); 91 err.error_exit = jpegErrorExit; 92 93 jpeg_create_compress(&compressData); 94 95 JPEGDestinationManager dest(jpegData); 96 compressData.dest = &dest; 97 dest.init_destination = jpegInitializeDestination; 98 dest.empty_output_buffer = jpegEmptyOutputBuffer; 99 dest.term_destination = jpegTerminateDestination; 100 101 compressData.image_width = size.width(); 102 compressData.image_height = size.height(); 103 compressData.input_components = 3; 104 compressData.in_color_space = JCS_RGB; 105 jpeg_set_defaults(&compressData); 106 jpeg_set_quality(&compressData, 65, FALSE); 107 108 // rowBuffer must be defined here so that its destructor is always called even when "setjmp" catches an error. 109 Vector<JSAMPLE, 600 * 3> rowBuffer; 110 111 if (setjmp(err.m_setjmpBuffer)) 112 return false; 113 114 jpeg_start_compress(&compressData, TRUE); 115 rowBuffer.resize(compressData.image_width * 3); 116 117 const unsigned char* pixel = rgbaBigEndianData; 118 const unsigned char* pixelEnd = pixel + compressData.image_width * compressData.image_height * 4; 119 while (pixel < pixelEnd) { 120 JSAMPLE* output = rowBuffer.data(); 121 for (const unsigned char* rowEnd = pixel + compressData.image_width * 4; pixel < rowEnd;) { 122 *output++ = static_cast<JSAMPLE>(*pixel++ & 0xFF); // red 123 *output++ = static_cast<JSAMPLE>(*pixel++ & 0xFF); // green 124 *output++ = static_cast<JSAMPLE>(*pixel++ & 0xFF); // blue 125 ++pixel; // skip alpha 126 } 127 output = rowBuffer.data(); 128 jpeg_write_scanlines(&compressData, &output, 1); 129 } 130 131 jpeg_finish_compress(&compressData); 132 return true; 133 } 134 135 } // namespace WebCore 136