Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / source / filter / jpeg / jpegc.cxx
bloba9ddddc3124b6a94586b48f6b41949837e39a135
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <setjmp.h>
25 #include <jpeglib.h>
26 #include <jerror.h>
28 #include <com/sun/star/task/XStatusIndicator.hpp>
29 #include <osl/diagnose.h>
31 extern "C" {
32 #include "transupp.h"
35 #include "jpeg.h"
36 #include <JpegReader.hxx>
37 #include <JpegWriter.hxx>
38 #include <boost/scoped_array.hpp>
40 #ifdef _MSC_VER
41 #pragma warning(push, 1) /* disable to __declspec(align()) aligned warning */
42 #pragma warning (disable: 4324)
43 #endif
45 struct ErrorManagerStruct
47 jpeg_error_mgr pub;
48 jmp_buf setjmp_buffer;
51 #ifdef _MSC_VER
52 #pragma warning(pop)
53 #endif
55 extern "C" void errorExit (j_common_ptr cinfo)
57 ErrorManagerStruct * error = reinterpret_cast<ErrorManagerStruct *>(cinfo->err);
58 (*cinfo->err->output_message) (cinfo);
59 longjmp(error->setjmp_buffer, 1);
62 extern "C" void outputMessage (j_common_ptr cinfo)
64 char buffer[JMSG_LENGTH_MAX];
65 (*cinfo->err->format_message) (cinfo, buffer);
68 void ReadJPEG( JPEGReader* pJPEGReader, void* pInputStream, long* pLines,
69 Size const & previewSize )
71 jpeg_decompress_struct cinfo;
72 ErrorManagerStruct jerr;
73 JPEGCreateBitmapParam aCreateBitmapParam;
74 unsigned char * pDIB;
75 unsigned char * pTmp;
76 long nWidth;
77 long nHeight;
78 long nAlignedWidth;
79 JSAMPLE* aRangeLimit;
80 boost::scoped_array<unsigned char> pScanLineBuffer;
82 if ( setjmp( jerr.setjmp_buffer ) )
84 jpeg_destroy_decompress( &cinfo );
85 return;
88 cinfo.err = jpeg_std_error( &jerr.pub );
89 jerr.pub.error_exit = errorExit;
90 jerr.pub.output_message = outputMessage;
92 jpeg_create_decompress( &cinfo );
93 jpeg_svstream_src( &cinfo, pInputStream );
94 jpeg_read_header( &cinfo, TRUE );
96 cinfo.scale_num = 1;
97 cinfo.scale_denom = 1;
98 cinfo.output_gamma = 1.0;
99 cinfo.raw_data_out = FALSE;
100 cinfo.quantize_colors = FALSE;
101 if ( cinfo.jpeg_color_space == JCS_YCbCr )
102 cinfo.out_color_space = JCS_RGB;
103 else if ( cinfo.jpeg_color_space == JCS_YCCK )
104 cinfo.out_color_space = JCS_CMYK;
106 if (cinfo.out_color_space != JCS_CMYK &&
107 cinfo.out_color_space != JCS_GRAYSCALE &&
108 cinfo.out_color_space != JCS_RGB)
110 SAL_WARN("vcl.filter", "jpg with unknown out color space, forcing to rgb");
111 cinfo.out_color_space = JCS_RGB;
114 /* change scale for preview import */
115 long nPreviewWidth = previewSize.Width();
116 long nPreviewHeight = previewSize.Height();
117 if( nPreviewWidth || nPreviewHeight )
119 if( nPreviewWidth == 0 )
121 nPreviewWidth = ( cinfo.image_width * nPreviewHeight ) / cinfo.image_height;
122 if( nPreviewWidth <= 0 )
124 nPreviewWidth = 1;
127 else if( nPreviewHeight == 0 )
129 nPreviewHeight = ( cinfo.image_height * nPreviewWidth ) / cinfo.image_width;
130 if( nPreviewHeight <= 0 )
132 nPreviewHeight = 1;
136 for( cinfo.scale_denom = 1; cinfo.scale_denom < 8; cinfo.scale_denom *= 2 )
138 if( cinfo.image_width < nPreviewWidth * cinfo.scale_denom )
139 break;
140 if( cinfo.image_height < nPreviewHeight * cinfo.scale_denom )
141 break;
144 if( cinfo.scale_denom > 1 )
146 cinfo.dct_method = JDCT_FASTEST;
147 cinfo.do_fancy_upsampling = FALSE;
148 cinfo.do_block_smoothing = FALSE;
152 jpeg_start_decompress( &cinfo );
154 nWidth = cinfo.output_width;
155 nHeight = cinfo.output_height;
156 aCreateBitmapParam.nWidth = nWidth;
157 aCreateBitmapParam.nHeight = nHeight;
159 aCreateBitmapParam.density_unit = cinfo.density_unit;
160 aCreateBitmapParam.X_density = cinfo.X_density;
161 aCreateBitmapParam.Y_density = cinfo.Y_density;
162 aCreateBitmapParam.bGray = long(cinfo.output_components == 1);
163 pDIB = pJPEGReader->CreateBitmap( &aCreateBitmapParam );
164 nAlignedWidth = aCreateBitmapParam.nAlignedWidth;
165 aRangeLimit = cinfo.sample_range_limit;
167 long nScanLineBufferComponents = 0;
168 if ( cinfo.out_color_space == JCS_CMYK )
170 nScanLineBufferComponents = cinfo.output_width * 4;
171 pScanLineBuffer.reset(new unsigned char[nScanLineBufferComponents]);
174 if( pDIB )
176 if( aCreateBitmapParam.bTopDown )
178 pTmp = pDIB;
180 else
182 pTmp = pDIB + ( nHeight - 1 ) * nAlignedWidth;
183 nAlignedWidth = -nAlignedWidth;
186 for ( *pLines = 0; *pLines < nHeight; (*pLines)++ )
188 if (pScanLineBuffer)
189 { // in other words cinfo.out_color_space == JCS_CMYK
190 int i;
191 int j;
192 unsigned char *pSLB = pScanLineBuffer.get();
193 jpeg_read_scanlines( &cinfo, (JSAMPARRAY) &pSLB, 1 );
194 // convert CMYK to RGB
195 for( i=0, j=0; i < nScanLineBufferComponents; i+=4, j+=3 )
197 int color_C = 255 - pScanLineBuffer[i+0];
198 int color_M = 255 - pScanLineBuffer[i+1];
199 int color_Y = 255 - pScanLineBuffer[i+2];
200 int color_K = 255 - pScanLineBuffer[i+3];
201 pTmp[j+0] = aRangeLimit[ 255L - ( color_C + color_K ) ];
202 pTmp[j+1] = aRangeLimit[ 255L - ( color_M + color_K ) ];
203 pTmp[j+2] = aRangeLimit[ 255L - ( color_Y + color_K ) ];
206 else
208 jpeg_read_scanlines( &cinfo, (JSAMPARRAY) &pTmp, 1 );
211 /* PENDING ??? */
212 if ( cinfo.err->msg_code == 113 )
213 break;
215 pTmp += nAlignedWidth;
219 if ( pDIB )
221 jpeg_finish_decompress( &cinfo );
223 else
225 jpeg_abort_decompress( &cinfo );
228 pScanLineBuffer.reset();
230 jpeg_destroy_decompress( &cinfo );
233 bool WriteJPEG( JPEGWriter* pJPEGWriter, void* pOutputStream,
234 long nWidth, long nHeight, bool bGreys,
235 long nQualityPercent, long aChromaSubsampling,
236 css::uno::Reference<css::task::XStatusIndicator> const & status )
238 jpeg_compress_struct cinfo;
239 ErrorManagerStruct jerr;
240 void* pScanline;
241 long nY;
243 if ( setjmp( jerr.setjmp_buffer ) )
245 jpeg_destroy_compress( &cinfo );
246 return false;
249 cinfo.err = jpeg_std_error( &jerr.pub );
250 jerr.pub.error_exit = errorExit;
251 jerr.pub.output_message = outputMessage;
253 jpeg_create_compress( &cinfo );
254 jpeg_svstream_dest( &cinfo, pOutputStream );
256 cinfo.image_width = (JDIMENSION) nWidth;
257 cinfo.image_height = (JDIMENSION) nHeight;
258 if ( bGreys )
260 cinfo.input_components = 1;
261 cinfo.in_color_space = JCS_GRAYSCALE;
263 else
265 cinfo.input_components = 3;
266 cinfo.in_color_space = JCS_RGB;
269 jpeg_set_defaults( &cinfo );
270 jpeg_set_quality( &cinfo, (int) nQualityPercent, FALSE );
272 cinfo.density_unit = 1;
273 cinfo.X_density = 96;
274 cinfo.Y_density = 96;
276 if ( ( nWidth > 128 ) || ( nHeight > 128 ) )
277 jpeg_simple_progression( &cinfo );
279 if (aChromaSubsampling == 1) // YUV 4:4:4
281 cinfo.comp_info[0].h_samp_factor = 1;
282 cinfo.comp_info[0].v_samp_factor = 1;
284 else if (aChromaSubsampling == 2) // YUV 4:2:2
286 cinfo.comp_info[0].h_samp_factor = 2;
287 cinfo.comp_info[0].v_samp_factor = 1;
289 else if (aChromaSubsampling == 3) // YUV 4:2:0
291 cinfo.comp_info[0].h_samp_factor = 2;
292 cinfo.comp_info[0].v_samp_factor = 2;
295 jpeg_start_compress( &cinfo, TRUE );
297 for( nY = 0; nY < nHeight; nY++ )
299 pScanline = pJPEGWriter->GetScanline( nY );
301 if( pScanline )
303 jpeg_write_scanlines( &cinfo, reinterpret_cast<JSAMPARRAY>(&pScanline), 1 );
306 if( status.is() )
308 status->setValue( nY * 100L / nHeight );
312 jpeg_finish_compress(&cinfo);
313 jpeg_destroy_compress( &cinfo );
315 return true;
318 long Transform(void* pInputStream, void* pOutputStream, long nAngle)
320 jpeg_transform_info aTransformOption;
321 JCOPY_OPTION aCopyOption = JCOPYOPT_ALL;
323 jpeg_decompress_struct aSourceInfo;
324 jpeg_compress_struct aDestinationInfo;
325 ErrorManagerStruct aSourceError;
326 ErrorManagerStruct aDestinationError;
328 jvirt_barray_ptr* aSourceCoefArrays = 0;
329 jvirt_barray_ptr* aDestinationCoefArrays = 0;
331 aTransformOption.force_grayscale = FALSE;
332 aTransformOption.trim = FALSE;
333 aTransformOption.perfect = FALSE;
334 aTransformOption.crop = FALSE;
336 // Angle to transform option
337 // 90 Clockwise = 270 Counterclockwise
338 switch (nAngle)
340 case 2700:
341 aTransformOption.transform = JXFORM_ROT_90;
342 break;
343 case 1800:
344 aTransformOption.transform = JXFORM_ROT_180;
345 break;
346 case 900:
347 aTransformOption.transform = JXFORM_ROT_270;
348 break;
349 default:
350 aTransformOption.transform = JXFORM_NONE;
353 // Decompression
354 aSourceInfo.err = jpeg_std_error(&aSourceError.pub);
355 aSourceInfo.err->error_exit = errorExit;
356 aSourceInfo.err->output_message = outputMessage;
358 // Compression
359 aDestinationInfo.err = jpeg_std_error(&aDestinationError.pub);
360 aDestinationInfo.err->error_exit = errorExit;
361 aDestinationInfo.err->output_message = outputMessage;
363 aDestinationInfo.optimize_coding = TRUE;
365 if (setjmp(aSourceError.setjmp_buffer) || setjmp(aDestinationError.setjmp_buffer))
367 jpeg_destroy_decompress(&aSourceInfo);
368 jpeg_destroy_compress(&aDestinationInfo);
369 return 0;
372 jpeg_create_decompress(&aSourceInfo);
373 jpeg_create_compress(&aDestinationInfo);
375 jpeg_svstream_src (&aSourceInfo, pInputStream);
377 jcopy_markers_setup(&aSourceInfo, aCopyOption);
378 jpeg_read_header(&aSourceInfo, TRUE);
379 jtransform_request_workspace(&aSourceInfo, &aTransformOption);
381 aSourceCoefArrays = jpeg_read_coefficients(&aSourceInfo);
382 jpeg_copy_critical_parameters(&aSourceInfo, &aDestinationInfo);
384 aDestinationCoefArrays = jtransform_adjust_parameters(&aSourceInfo, &aDestinationInfo, aSourceCoefArrays, &aTransformOption);
385 jpeg_svstream_dest (&aDestinationInfo, pOutputStream);
387 // Compute optimal Huffman coding tables instead of precomuted tables
388 aDestinationInfo.optimize_coding = TRUE;
389 jpeg_write_coefficients(&aDestinationInfo, aDestinationCoefArrays);
390 jcopy_markers_execute(&aSourceInfo, &aDestinationInfo, aCopyOption);
391 jtransform_execute_transformation(&aSourceInfo, &aDestinationInfo, aSourceCoefArrays, &aTransformOption);
393 jpeg_finish_compress(&aDestinationInfo);
394 jpeg_destroy_compress(&aDestinationInfo);
396 jpeg_finish_decompress(&aSourceInfo);
397 jpeg_destroy_decompress(&aSourceInfo);
399 return 1;
402 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */