30#include <com/sun/star/task/XStatusIndicator.hpp>
45#pragma warning (disable: 4324)
50struct ErrorManagerStruct
53 jmp_buf setjmp_buffer;
66 char buffer[JMSG_LENGTH_MAX];
67 (*cinfo->err->format_message) (cinfo, buffer);
68 SAL_WARN(
"vcl.filter",
"fatal failure reading JPEG: " << buffer);
69 ErrorManagerStruct * error =
reinterpret_cast<ErrorManagerStruct *
>(cinfo->err);
70 longjmp(error->setjmp_buffer, 1);
75 char buffer[JMSG_LENGTH_MAX];
76 (*cinfo->err->format_message) (cinfo, buffer);
77 SAL_WARN(
"vcl.filter",
"failure reading JPEG: " << buffer);
93 constexpr int WarningLimit = 1000;
97 if (bFuzzing && cinfo->err->msg_code == JWRN_NOT_SEQUENTIAL)
99 cinfo->err->error_exit(cinfo);
102 if (++cinfo->err->num_warnings > WarningLimit)
103 cinfo->err->error_exit(cinfo);
105 cinfo->err->output_message(cinfo);
107 else if (cinfo->err->trace_level >= msg_level)
108 cinfo->err->output_message(cinfo);
115class JpegDecompressOwner
118 void set(jpeg_decompress_struct *cinfo)
122 ~JpegDecompressOwner()
124 if (m_cinfo !=
nullptr)
126 jpeg_destroy_decompress(m_cinfo);
130 jpeg_decompress_struct *m_cinfo =
nullptr;
133class JpegCompressOwner
136 void set(jpeg_compress_struct *cinfo)
142 if (m_cinfo !=
nullptr)
144 jpeg_destroy_compress(m_cinfo);
148 jpeg_compress_struct *m_cinfo =
nullptr;
153 jpeg_decompress_struct cinfo;
154 jpeg_progress_mgr progress;
155 ErrorManagerStruct jerr;
156 JpegDecompressOwner aOwner;
157 std::unique_ptr<BitmapScopedWriteAccess> pScopedAccess;
158 std::vector<sal_uInt8> pScanLineBuffer;
159 std::vector<sal_uInt8> pCYMKBuffer;
164#define LIMITSCANS 100
166void progress_monitor(j_common_ptr cinfo)
168 if (cinfo->is_decompressor)
170 jpeg_decompress_struct* decompressor =
reinterpret_cast<j_decompress_ptr
>(cinfo);
171 if (decompressor->input_scan_number >=
LIMITSCANS)
173 SAL_WARN(
"vcl.filter",
"too many progressive scans, cancelling import after: " << decompressor->input_scan_number <<
" scans");
185 if (setjmp(rContext.jerr.setjmp_buffer))
190 rContext.cinfo.err = jpeg_std_error(&rContext.jerr.pub);
191 rContext.jerr.pub.error_exit =
errorExit;
195 jpeg_create_decompress(&rContext.cinfo);
196 rContext.aOwner.set(&rContext.cinfo);
198 rContext.cinfo.progress = &rContext.progress;
199 rContext.progress.progress_monitor = progress_monitor;
201 jpeg_read_header(&rContext.cinfo,
TRUE);
203 rContext.cinfo.scale_num = 1;
204 rContext.cinfo.scale_denom = 1;
205 rContext.cinfo.output_gamma = 1.0;
206 rContext.cinfo.raw_data_out =
FALSE;
207 rContext.cinfo.quantize_colors =
FALSE;
209 jpeg_calc_output_dimensions(&rContext.cinfo);
212 tools::Long nHeight = rContext.cinfo.output_height;
219 if (rContext.cinfo.err->num_warnings && (nWidth > 8192 || nHeight > 8192))
223 bool bGray = (rContext.cinfo.output_components == 1);
227 aCreateBitmapParam.
nWidth = nWidth;
228 aCreateBitmapParam.
nHeight = nHeight;
230 aCreateBitmapParam.
density_unit = rContext.cinfo.density_unit;
231 aCreateBitmapParam.
X_density = rContext.cinfo.X_density;
232 aCreateBitmapParam.
Y_density = rContext.cinfo.Y_density;
233 aCreateBitmapParam.
bGray = bGray;
237 bool bBitmapCreated = bUseExistingBitmap;
239 bBitmapCreated = pJPEGReader->
CreateBitmap(aCreateBitmapParam);
241 if (bBitmapCreated && !bOnlyCreateBitmap)
254 J_COLOR_SPACE best_out_color_space = JCS_RGB;
260 best_out_color_space = JCS_GRAYSCALE;
264#if defined(JCS_EXTENSIONS)
267 best_out_color_space = JCS_EXT_BGR;
268 eScanlineFormat = eFinalFormat;
273 best_out_color_space = JCS_EXT_BGRA;
274 eScanlineFormat = eFinalFormat;
279 best_out_color_space = JCS_EXT_RGBA;
280 eScanlineFormat = eFinalFormat;
285 best_out_color_space = JCS_EXT_ARGB;
286 eScanlineFormat = eFinalFormat;
290 if (rContext.cinfo.jpeg_color_space == JCS_YCCK)
291 rContext.cinfo.out_color_space = JCS_CMYK;
293 if (rContext.cinfo.out_color_space != JCS_CMYK)
294 rContext.cinfo.out_color_space = best_out_color_space;
296 jpeg_start_decompress(&rContext.cinfo);
298 JSAMPLE* aRangeLimit = rContext.cinfo.sample_range_limit;
300 rContext.pScanLineBuffer.resize(nWidth * nPixelSize);
302 if (rContext.cinfo.out_color_space == JCS_CMYK)
304 rContext.pCYMKBuffer.resize(nWidth * 4);
310 size_t yIndex = *pLines;
312 sal_uInt8*
p = (rContext.cinfo.out_color_space == JCS_CMYK) ? rContext.pCYMKBuffer.data() : rContext.pScanLineBuffer.data();
313 jpeg_read_scanlines(&rContext.cinfo,
reinterpret_cast<JSAMPARRAY
>(&
p), 1);
315 if (rContext.cinfo.out_color_space == JCS_CMYK)
318 Scanline pScanline = pAccess->GetScanline(yIndex);
319 for (
tools::Long cmyk = 0,
x = 0; cmyk < nWidth * 4; cmyk += 4, ++
x)
321 int color_C = 255 - rContext.pCYMKBuffer[cmyk + 0];
322 int color_M = 255 - rContext.pCYMKBuffer[cmyk + 1];
323 int color_Y = 255 - rContext.pCYMKBuffer[cmyk + 2];
324 int color_K = 255 - rContext.pCYMKBuffer[cmyk + 3];
326 sal_uInt8 cRed = aRangeLimit[255L - (color_C + color_K)];
327 sal_uInt8 cGreen = aRangeLimit[255L - (color_M + color_K)];
328 sal_uInt8 cBlue = aRangeLimit[255L - (color_Y + color_K)];
330 pAccess->SetPixelOnData(pScanline,
x,
BitmapColor(cRed, cGreen, cBlue));
335 pAccess->CopyScanline(yIndex, rContext.pScanLineBuffer.data(), eScanlineFormat, rContext.pScanLineBuffer.size());
339 if (rContext.cinfo.err->msg_code == 113)
343 rContext.pScanLineBuffer.clear();
344 rContext.pCYMKBuffer.clear();
346 rContext.pScopedAccess.reset();
349 if (bBitmapCreated && !bOnlyCreateBitmap)
351 jpeg_finish_decompress(&rContext.cinfo);
355 jpeg_abort_decompress(&rContext.cinfo);
364 ReadJPEG(aContext, pJPEGReader, pInputStream, pLines, nImportFlags, ppAccess);
370 css::uno::Reference<css::task::XStatusIndicator>
const & status )
372 jpeg_compress_struct cinfo;
373 ErrorManagerStruct jerr;
377 JpegCompressOwner aOwner;
379 if ( setjmp( jerr.setjmp_buffer ) )
384 cinfo.err = jpeg_std_error( &jerr.pub );
388 jpeg_create_compress( &cinfo );
392 cinfo.image_width =
static_cast<JDIMENSION
>(nWidth);
393 cinfo.image_height =
static_cast<JDIMENSION
>(nHeight);
396 cinfo.input_components = 1;
397 cinfo.in_color_space = JCS_GRAYSCALE;
401 cinfo.input_components = 3;
402 cinfo.in_color_space = JCS_RGB;
405 jpeg_set_defaults( &cinfo );
406 jpeg_set_quality( &cinfo,
static_cast<int>(nQualityPercent),
FALSE );
410 cinfo.density_unit = 1;
419 if ( ( nWidth > 128 ) || ( nHeight > 128 ) )
420 jpeg_simple_progression( &cinfo );
422 if (aChromaSubsampling == 1)
424 cinfo.comp_info[0].h_samp_factor = 1;
425 cinfo.comp_info[0].v_samp_factor = 1;
427 else if (aChromaSubsampling == 2)
429 cinfo.comp_info[0].h_samp_factor = 2;
430 cinfo.comp_info[0].v_samp_factor = 1;
432 else if (aChromaSubsampling == 3)
434 cinfo.comp_info[0].h_samp_factor = 2;
435 cinfo.comp_info[0].v_samp_factor = 2;
438 jpeg_start_compress( &cinfo,
TRUE );
440 for( nY = 0; nY < nHeight; nY++ )
446 jpeg_write_scanlines( &cinfo,
reinterpret_cast<JSAMPARRAY
>(&pScanline), 1 );
451 status->setValue( nY * 100L / nHeight );
455 jpeg_finish_compress(&cinfo);
465 jpeg_decompress_struct aSourceInfo;
466 jpeg_compress_struct aDestinationInfo;
467 ErrorManagerStruct aSourceError;
468 ErrorManagerStruct aDestinationError;
470 jvirt_barray_ptr* aSourceCoefArrays =
nullptr;
471 jvirt_barray_ptr* aDestinationCoefArrays =
nullptr;
480 switch (nAngle.
get())
496 aSourceInfo.err = jpeg_std_error(&aSourceError.pub);
501 aDestinationInfo.err = jpeg_std_error(&aDestinationError.pub);
502 aDestinationInfo.err->error_exit =
errorExit;
505 aDestinationInfo.optimize_coding =
TRUE;
507 JpegDecompressOwner aDecompressOwner;
508 JpegCompressOwner aCompressOwner;
510 if (setjmp(aSourceError.setjmp_buffer))
512 jpeg_destroy_decompress(&aSourceInfo);
513 jpeg_destroy_compress(&aDestinationInfo);
516 if (setjmp(aDestinationError.setjmp_buffer))
518 jpeg_destroy_decompress(&aSourceInfo);
519 jpeg_destroy_compress(&aDestinationInfo);
523 jpeg_create_decompress(&aSourceInfo);
524 aDecompressOwner.set(&aSourceInfo);
525 jpeg_create_compress(&aDestinationInfo);
526 aCompressOwner.set(&aDestinationInfo);
531 jpeg_read_header(&aSourceInfo,
TRUE);
532 jtransform_request_workspace(&aSourceInfo, &aTransformOption);
534 aSourceCoefArrays = jpeg_read_coefficients(&aSourceInfo);
535 jpeg_copy_critical_parameters(&aSourceInfo, &aDestinationInfo);
537 aDestinationCoefArrays = jtransform_adjust_parameters(&aSourceInfo, &aDestinationInfo, aSourceCoefArrays, &aTransformOption);
541 aDestinationInfo.optimize_coding =
TRUE;
542 jpeg_write_coefficients(&aDestinationInfo, aDestinationCoefArrays);
544 jtransform_execute_transformation(&aSourceInfo, &aDestinationInfo, aSourceCoefArrays, &aTransformOption);
546 jpeg_finish_compress(&aDestinationInfo);
548 jpeg_finish_decompress(&aSourceInfo);
vcl::ScopedBitmapAccess< BitmapWriteAccess, Bitmap, &Bitmap::AcquireWriteAccess > BitmapScopedWriteAccess
bool CreateBitmap(JPEGCreateBitmapParam const ¶m)
void * GetScanline(tools::Long nY)
This template handles BitmapAccess the RAII way.
@ UseExistingBitmap
Read pixel data into an existing bitmap.
@ OnlyCreateBitmap
Only create a bitmap, do not read pixel data.
void jpeg_svstream_dest(j_compress_ptr cinfo, void *outfile)
void jpeg_svstream_src(j_decompress_ptr cinfo, void *infile)
static void errorExit(j_common_ptr cinfo)
static void ReadJPEG(JpegStuff &rContext, JPEGReader *pJPEGReader, void *pInputStream, tools::Long *pLines, GraphicFilterImportFlags nImportFlags, BitmapScopedWriteAccess *ppAccess)
void Transform(void *pInputStream, void *pOutputStream, Degree10 nAngle)
static void emitMessage(j_common_ptr cinfo, int msg_level)
bool WriteJPEG(JPEGWriter *pJPEGWriter, void *pOutputStream, tools::Long nWidth, tools::Long nHeight, basegfx::B2DSize const &rPPI, bool bGreys, tools::Long nQualityPercent, tools::Long aChromaSubsampling, css::uno::Reference< css::task::XStatusIndicator > const &status)
static void outputMessage(j_common_ptr cinfo)
#define SAL_WARN(area, stream)
void set(css::uno::UnoInterfaceReference const &value)
std::enable_if< std::is_signed< T >::value, bool >::type checked_multiply(T a, T b, T &result)
constexpr std::enable_if_t< std::is_floating_point_v< F > &&std::is_integral_v< I >, bool > convertsToAtMost(F value, I max)
tools::ULong density_unit
int no_data_available_failures
UNDERLYING_TYPE get() const
jcopy_markers_setup(j_decompress_ptr srcinfo, JCOPY_OPTION option)
jcopy_markers_execute(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, JCOPY_OPTION option)