1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
28 #include <com/sun/star/task/XStatusIndicator.hpp>
29 #include <osl/diagnose.h>
36 #include <JpegReader.hxx>
37 #include <JpegWriter.hxx>
38 #include <boost/scoped_array.hpp>
41 #pragma warning(push, 1) /* disable to __declspec(align()) aligned warning */
42 #pragma warning (disable: 4324)
45 struct ErrorManagerStruct
48 jmp_buf setjmp_buffer
;
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
;
80 boost::scoped_array
<unsigned char> pScanLineBuffer
;
82 if ( setjmp( jerr
.setjmp_buffer
) )
84 jpeg_destroy_decompress( &cinfo
);
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
);
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 )
127 else if( nPreviewHeight
== 0 )
129 nPreviewHeight
= ( cinfo
.image_height
* nPreviewWidth
) / cinfo
.image_width
;
130 if( nPreviewHeight
<= 0 )
136 for( cinfo
.scale_denom
= 1; cinfo
.scale_denom
< 8; cinfo
.scale_denom
*= 2 )
138 if( cinfo
.image_width
< nPreviewWidth
* cinfo
.scale_denom
)
140 if( cinfo
.image_height
< nPreviewHeight
* cinfo
.scale_denom
)
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
]);
176 if( aCreateBitmapParam
.bTopDown
)
182 pTmp
= pDIB
+ ( nHeight
- 1 ) * nAlignedWidth
;
183 nAlignedWidth
= -nAlignedWidth
;
186 for ( *pLines
= 0; *pLines
< nHeight
; (*pLines
)++ )
189 { // in other words cinfo.out_color_space == JCS_CMYK
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
) ];
208 jpeg_read_scanlines( &cinfo
, (JSAMPARRAY
) &pTmp
, 1 );
212 if ( cinfo
.err
->msg_code
== 113 )
215 pTmp
+= nAlignedWidth
;
221 jpeg_finish_decompress( &cinfo
);
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
;
243 if ( setjmp( jerr
.setjmp_buffer
) )
245 jpeg_destroy_compress( &cinfo
);
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
;
260 cinfo
.input_components
= 1;
261 cinfo
.in_color_space
= JCS_GRAYSCALE
;
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
);
303 jpeg_write_scanlines( &cinfo
, reinterpret_cast<JSAMPARRAY
>(&pScanline
), 1 );
308 status
->setValue( nY
* 100L / nHeight
);
312 jpeg_finish_compress(&cinfo
);
313 jpeg_destroy_compress( &cinfo
);
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
341 aTransformOption
.transform
= JXFORM_ROT_90
;
344 aTransformOption
.transform
= JXFORM_ROT_180
;
347 aTransformOption
.transform
= JXFORM_ROT_270
;
350 aTransformOption
.transform
= JXFORM_NONE
;
354 aSourceInfo
.err
= jpeg_std_error(&aSourceError
.pub
);
355 aSourceInfo
.err
->error_exit
= errorExit
;
356 aSourceInfo
.err
->output_message
= outputMessage
;
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
);
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
);
402 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */