Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <sal/config.h>
21 :
22 : #include <stdio.h>
23 : #include <stdlib.h>
24 : #include <setjmp.h>
25 : #include <jpeglib.h>
26 : #include <jerror.h>
27 :
28 : #include <com/sun/star/task/XStatusIndicator.hpp>
29 : #include <osl/diagnose.h>
30 :
31 : extern "C" {
32 : #include "transupp.h"
33 : }
34 :
35 : #include "jpeg.h"
36 : #include <JpegReader.hxx>
37 : #include <JpegWriter.hxx>
38 : #include <boost/scoped_array.hpp>
39 :
40 : #ifdef _MSC_VER
41 : #pragma warning(push, 1) /* disable to __declspec(align()) aligned warning */
42 : #pragma warning (disable: 4324)
43 : #endif
44 :
45 : struct ErrorManagerStruct
46 : {
47 : jpeg_error_mgr pub;
48 : jmp_buf setjmp_buffer;
49 : };
50 :
51 : #ifdef _MSC_VER
52 : #pragma warning(pop)
53 : #endif
54 :
55 6 : extern "C" void errorExit (j_common_ptr cinfo)
56 : {
57 6 : ErrorManagerStruct * error = reinterpret_cast<ErrorManagerStruct *>(cinfo->err);
58 6 : (*cinfo->err->output_message) (cinfo);
59 6 : longjmp(error->setjmp_buffer, 1);
60 : }
61 :
62 13 : extern "C" void outputMessage (j_common_ptr cinfo)
63 : {
64 : char buffer[JMSG_LENGTH_MAX];
65 13 : (*cinfo->err->format_message) (cinfo, buffer);
66 13 : }
67 :
68 439 : void ReadJPEG( JPEGReader* pJPEGReader, void* pInputStream, long* pLines,
69 : Size const & previewSize )
70 : {
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 439 : boost::scoped_array<unsigned char> pScanLineBuffer;
81 :
82 439 : if ( setjmp( jerr.setjmp_buffer ) )
83 : {
84 6 : jpeg_destroy_decompress( &cinfo );
85 445 : return;
86 : }
87 :
88 439 : cinfo.err = jpeg_std_error( &jerr.pub );
89 439 : jerr.pub.error_exit = errorExit;
90 439 : jerr.pub.output_message = outputMessage;
91 :
92 439 : jpeg_create_decompress( &cinfo );
93 439 : jpeg_svstream_src( &cinfo, pInputStream );
94 439 : jpeg_read_header( &cinfo, TRUE );
95 :
96 438 : cinfo.scale_num = 1;
97 438 : cinfo.scale_denom = 1;
98 438 : cinfo.output_gamma = 1.0;
99 438 : cinfo.raw_data_out = FALSE;
100 438 : cinfo.quantize_colors = FALSE;
101 438 : if ( cinfo.jpeg_color_space == JCS_YCbCr )
102 436 : cinfo.out_color_space = JCS_RGB;
103 2 : else if ( cinfo.jpeg_color_space == JCS_YCCK )
104 1 : cinfo.out_color_space = JCS_CMYK;
105 :
106 : OSL_ASSERT(cinfo.out_color_space == JCS_CMYK || cinfo.out_color_space == JCS_GRAYSCALE || cinfo.out_color_space == JCS_RGB);
107 :
108 : /* change scale for preview import */
109 438 : long nPreviewWidth = previewSize.Width();
110 438 : long nPreviewHeight = previewSize.Height();
111 438 : if( nPreviewWidth || nPreviewHeight )
112 : {
113 0 : if( nPreviewWidth == 0 )
114 : {
115 0 : nPreviewWidth = ( cinfo.image_width * nPreviewHeight ) / cinfo.image_height;
116 0 : if( nPreviewWidth <= 0 )
117 : {
118 0 : nPreviewWidth = 1;
119 : }
120 : }
121 0 : else if( nPreviewHeight == 0 )
122 : {
123 0 : nPreviewHeight = ( cinfo.image_height * nPreviewWidth ) / cinfo.image_width;
124 0 : if( nPreviewHeight <= 0 )
125 : {
126 0 : nPreviewHeight = 1;
127 : }
128 : }
129 :
130 0 : for( cinfo.scale_denom = 1; cinfo.scale_denom < 8; cinfo.scale_denom *= 2 )
131 : {
132 0 : if( cinfo.image_width < nPreviewWidth * cinfo.scale_denom )
133 0 : break;
134 0 : if( cinfo.image_height < nPreviewHeight * cinfo.scale_denom )
135 0 : break;
136 : }
137 :
138 0 : if( cinfo.scale_denom > 1 )
139 : {
140 0 : cinfo.dct_method = JDCT_FASTEST;
141 0 : cinfo.do_fancy_upsampling = FALSE;
142 0 : cinfo.do_block_smoothing = FALSE;
143 : }
144 : }
145 :
146 438 : jpeg_start_decompress( &cinfo );
147 :
148 434 : nWidth = cinfo.output_width;
149 434 : nHeight = cinfo.output_height;
150 434 : aCreateBitmapParam.nWidth = nWidth;
151 434 : aCreateBitmapParam.nHeight = nHeight;
152 :
153 434 : aCreateBitmapParam.density_unit = cinfo.density_unit;
154 434 : aCreateBitmapParam.X_density = cinfo.X_density;
155 434 : aCreateBitmapParam.Y_density = cinfo.Y_density;
156 434 : aCreateBitmapParam.bGray = long(cinfo.output_components == 1);
157 434 : pDIB = pJPEGReader->CreateBitmap( &aCreateBitmapParam );
158 434 : nAlignedWidth = aCreateBitmapParam.nAlignedWidth;
159 434 : aRangeLimit = cinfo.sample_range_limit;
160 :
161 434 : long nScanLineBufferComponents = 0;
162 434 : if ( cinfo.out_color_space == JCS_CMYK )
163 : {
164 1 : nScanLineBufferComponents = cinfo.output_width * 4;
165 1 : pScanLineBuffer.reset(new unsigned char[nScanLineBufferComponents]);
166 : }
167 :
168 434 : if( pDIB )
169 : {
170 434 : if( aCreateBitmapParam.bTopDown )
171 : {
172 433 : pTmp = pDIB;
173 : }
174 : else
175 : {
176 1 : pTmp = pDIB + ( nHeight - 1 ) * nAlignedWidth;
177 1 : nAlignedWidth = -nAlignedWidth;
178 : }
179 :
180 179104 : for ( *pLines = 0; *pLines < nHeight; (*pLines)++ )
181 : {
182 178670 : if (pScanLineBuffer)
183 : { // in other words cinfo.out_color_space == JCS_CMYK
184 : int i;
185 : int j;
186 353 : unsigned char *pSLB = pScanLineBuffer.get();
187 353 : jpeg_read_scanlines( &cinfo, reinterpret_cast<JSAMPARRAY>(&pSLB), 1 );
188 : // convert CMYK to RGB
189 414069 : for( i=0, j=0; i < nScanLineBufferComponents; i+=4, j+=3 )
190 : {
191 413716 : int color_C = 255 - pScanLineBuffer[i+0];
192 413716 : int color_M = 255 - pScanLineBuffer[i+1];
193 413716 : int color_Y = 255 - pScanLineBuffer[i+2];
194 413716 : int color_K = 255 - pScanLineBuffer[i+3];
195 413716 : pTmp[j+0] = aRangeLimit[ 255L - ( color_C + color_K ) ];
196 413716 : pTmp[j+1] = aRangeLimit[ 255L - ( color_M + color_K ) ];
197 413716 : pTmp[j+2] = aRangeLimit[ 255L - ( color_Y + color_K ) ];
198 : }
199 : }
200 : else
201 : {
202 178317 : jpeg_read_scanlines( &cinfo, reinterpret_cast<JSAMPARRAY>(&pTmp), 1 );
203 : }
204 :
205 : /* PENDING ??? */
206 178670 : if ( cinfo.err->msg_code == 113 )
207 0 : break;
208 :
209 178670 : pTmp += nAlignedWidth;
210 : }
211 : }
212 :
213 434 : if ( pDIB )
214 : {
215 434 : jpeg_finish_decompress( &cinfo );
216 : }
217 : else
218 : {
219 0 : jpeg_abort_decompress( &cinfo );
220 : }
221 :
222 433 : pScanLineBuffer.reset();
223 :
224 433 : jpeg_destroy_decompress( &cinfo );
225 : }
226 :
227 1 : bool WriteJPEG( JPEGWriter* pJPEGWriter, void* pOutputStream,
228 : long nWidth, long nHeight, bool bGreys,
229 : long nQualityPercent, long aChromaSubsampling,
230 : css::uno::Reference<css::task::XStatusIndicator> const & status )
231 : {
232 : jpeg_compress_struct cinfo;
233 : ErrorManagerStruct jerr;
234 : void* pScanline;
235 : long nY;
236 :
237 1 : if ( setjmp( jerr.setjmp_buffer ) )
238 : {
239 0 : jpeg_destroy_compress( &cinfo );
240 0 : return false;
241 : }
242 :
243 1 : cinfo.err = jpeg_std_error( &jerr.pub );
244 1 : jerr.pub.error_exit = errorExit;
245 1 : jerr.pub.output_message = outputMessage;
246 :
247 1 : jpeg_create_compress( &cinfo );
248 1 : jpeg_svstream_dest( &cinfo, pOutputStream );
249 :
250 1 : cinfo.image_width = (JDIMENSION) nWidth;
251 1 : cinfo.image_height = (JDIMENSION) nHeight;
252 1 : if ( bGreys )
253 : {
254 1 : cinfo.input_components = 1;
255 1 : cinfo.in_color_space = JCS_GRAYSCALE;
256 : }
257 : else
258 : {
259 0 : cinfo.input_components = 3;
260 0 : cinfo.in_color_space = JCS_RGB;
261 : }
262 :
263 1 : jpeg_set_defaults( &cinfo );
264 1 : jpeg_set_quality( &cinfo, (int) nQualityPercent, FALSE );
265 :
266 1 : cinfo.density_unit = 1;
267 1 : cinfo.X_density = 96;
268 1 : cinfo.Y_density = 96;
269 :
270 1 : if ( ( nWidth > 128 ) || ( nHeight > 128 ) )
271 0 : jpeg_simple_progression( &cinfo );
272 :
273 1 : if (aChromaSubsampling == 1) // YUV 4:4:4
274 : {
275 0 : cinfo.comp_info[0].h_samp_factor = 1;
276 0 : cinfo.comp_info[0].v_samp_factor = 1;
277 : }
278 1 : else if (aChromaSubsampling == 2) // YUV 4:2:2
279 : {
280 0 : cinfo.comp_info[0].h_samp_factor = 2;
281 0 : cinfo.comp_info[0].v_samp_factor = 1;
282 : }
283 1 : else if (aChromaSubsampling == 3) // YUV 4:2:0
284 : {
285 0 : cinfo.comp_info[0].h_samp_factor = 2;
286 0 : cinfo.comp_info[0].v_samp_factor = 2;
287 : }
288 :
289 1 : jpeg_start_compress( &cinfo, TRUE );
290 :
291 101 : for( nY = 0; nY < nHeight; nY++ )
292 : {
293 100 : pScanline = pJPEGWriter->GetScanline( nY );
294 :
295 100 : if( pScanline )
296 : {
297 100 : jpeg_write_scanlines( &cinfo, reinterpret_cast<JSAMPARRAY>(&pScanline), 1 );
298 : }
299 :
300 100 : if( status.is() )
301 : {
302 0 : status->setValue( nY * 100L / nHeight );
303 : }
304 : }
305 :
306 1 : jpeg_finish_compress(&cinfo);
307 1 : jpeg_destroy_compress( &cinfo );
308 :
309 1 : return true;
310 : }
311 :
312 0 : long Transform(void* pInputStream, void* pOutputStream, long nAngle)
313 : {
314 : jpeg_transform_info aTransformOption;
315 0 : JCOPY_OPTION aCopyOption = JCOPYOPT_ALL;
316 :
317 : jpeg_decompress_struct aSourceInfo;
318 : jpeg_compress_struct aDestinationInfo;
319 : ErrorManagerStruct aSourceError;
320 : ErrorManagerStruct aDestinationError;
321 :
322 0 : jvirt_barray_ptr* aSourceCoefArrays = 0;
323 0 : jvirt_barray_ptr* aDestinationCoefArrays = 0;
324 :
325 0 : aTransformOption.force_grayscale = FALSE;
326 0 : aTransformOption.trim = FALSE;
327 0 : aTransformOption.perfect = FALSE;
328 0 : aTransformOption.crop = FALSE;
329 :
330 : // Angle to transform option
331 : // 90 Clockwise = 270 Counterclockwise
332 0 : switch (nAngle)
333 : {
334 : case 2700:
335 0 : aTransformOption.transform = JXFORM_ROT_90;
336 0 : break;
337 : case 1800:
338 0 : aTransformOption.transform = JXFORM_ROT_180;
339 0 : break;
340 : case 900:
341 0 : aTransformOption.transform = JXFORM_ROT_270;
342 0 : break;
343 : default:
344 0 : aTransformOption.transform = JXFORM_NONE;
345 : }
346 :
347 : // Decompression
348 0 : aSourceInfo.err = jpeg_std_error(&aSourceError.pub);
349 0 : aSourceInfo.err->error_exit = errorExit;
350 0 : aSourceInfo.err->output_message = outputMessage;
351 :
352 : // Compression
353 0 : aDestinationInfo.err = jpeg_std_error(&aDestinationError.pub);
354 0 : aDestinationInfo.err->error_exit = errorExit;
355 0 : aDestinationInfo.err->output_message = outputMessage;
356 :
357 0 : aDestinationInfo.optimize_coding = TRUE;
358 :
359 0 : if (setjmp(aSourceError.setjmp_buffer) || setjmp(aDestinationError.setjmp_buffer))
360 : {
361 0 : jpeg_destroy_decompress(&aSourceInfo);
362 0 : jpeg_destroy_compress(&aDestinationInfo);
363 0 : return 0;
364 : }
365 :
366 0 : jpeg_create_decompress(&aSourceInfo);
367 0 : jpeg_create_compress(&aDestinationInfo);
368 :
369 0 : jpeg_svstream_src (&aSourceInfo, pInputStream);
370 :
371 0 : jcopy_markers_setup(&aSourceInfo, aCopyOption);
372 0 : jpeg_read_header(&aSourceInfo, TRUE);
373 0 : jtransform_request_workspace(&aSourceInfo, &aTransformOption);
374 :
375 0 : aSourceCoefArrays = jpeg_read_coefficients(&aSourceInfo);
376 0 : jpeg_copy_critical_parameters(&aSourceInfo, &aDestinationInfo);
377 :
378 0 : aDestinationCoefArrays = jtransform_adjust_parameters(&aSourceInfo, &aDestinationInfo, aSourceCoefArrays, &aTransformOption);
379 0 : jpeg_svstream_dest (&aDestinationInfo, pOutputStream);
380 :
381 : // Compute optimal Huffman coding tables instead of precomuted tables
382 0 : aDestinationInfo.optimize_coding = TRUE;
383 0 : jpeg_write_coefficients(&aDestinationInfo, aDestinationCoefArrays);
384 0 : jcopy_markers_execute(&aSourceInfo, &aDestinationInfo, aCopyOption);
385 0 : jtransform_execute_transformation(&aSourceInfo, &aDestinationInfo, aSourceCoefArrays, &aTransformOption);
386 :
387 0 : jpeg_finish_compress(&aDestinationInfo);
388 0 : jpeg_destroy_compress(&aDestinationInfo);
389 :
390 0 : jpeg_finish_decompress(&aSourceInfo);
391 0 : jpeg_destroy_decompress(&aSourceInfo);
392 :
393 0 : return 1;
394 801 : }
395 :
396 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|