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