blob: 3e320aac09531bb0169b692584cb27e657756464 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "setjmp.h"
#include "jpeglib.h"
#include "jerror.h"
#include "jpeg.h"
#include "rtl/alloc.h"
#include "osl/diagnose.h"
struct my_error_mgr
{
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
};
void jpeg_svstream_src (j_decompress_ptr cinfo, void* infile);
void jpeg_svstream_dest (j_compress_ptr cinfo, void* outfile);
METHODDEF( void )
my_error_exit (j_common_ptr cinfo)
{
my_error_ptr myerr = (my_error_ptr) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(myerr->setjmp_buffer, 1);
}
METHODDEF( void )
my_output_message (j_common_ptr cinfo)
{
char buffer[JMSG_LENGTH_MAX];
(*cinfo->err->format_message) (cinfo, buffer);
}
/* TODO: when incompatible changes are possible again
the preview size hint should be redone */
static int nPreviewWidth = 0;
static int nPreviewHeight = 0;
void SetJpegPreviewSizeHint( int nWidth, int nHeight )
{
nPreviewWidth = nWidth;
nPreviewHeight = nHeight;
}
void ReadJPEG( void* pJPEGReader, void* pIStm, long* pLines )
{
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
struct JPEGCreateBitmapParam aCreateBitmapParam;
HPBYTE pDIB;
HPBYTE pTmp;
long nWidth;
long nHeight;
long nAlignedWidth;
JSAMPLE * range_limit;
HPBYTE pScanLineBuffer = NULL;
long nScanLineBufferComponents = 0;
// declare bDecompCreated volatile because of gcc
// warning: variable 'bDecompCreated' might be clobbered by `longjmp' or `vfork'
volatile long bDecompCreated = 0;
/* Falls der Stream nicht ausreicht (IO_PENDING)
wird ueber ein longjmp in der Schleife nach Exit
gesprungen, wir geben dann die Anzahl
der bisher bearbeiteten Scanlines zurueck*/
if ( setjmp( jerr.setjmp_buffer ) )
goto Exit;
cinfo.err = jpeg_std_error( &jerr.pub );
jerr.pub.error_exit = my_error_exit;
jerr.pub.output_message = my_output_message;
jpeg_create_decompress( &cinfo );
bDecompCreated = 1;
jpeg_svstream_src( &cinfo, pIStm );
jpeg_read_header( &cinfo, sal_True );
cinfo.scale_num = 1;
cinfo.scale_denom = 1;
cinfo.output_gamma = 1.0;
cinfo.raw_data_out = sal_False;
cinfo.quantize_colors = sal_False;
if ( cinfo.jpeg_color_space == JCS_YCbCr )
cinfo.out_color_space = JCS_RGB;
else if ( cinfo.jpeg_color_space == JCS_YCCK )
cinfo.out_color_space = JCS_CMYK;
OSL_ASSERT(cinfo.out_color_space == JCS_CMYK || cinfo.out_color_space == JCS_GRAYSCALE || cinfo.out_color_space == JCS_RGB);
/* change scale for preview import */
if( nPreviewWidth || nPreviewHeight )
{
if( nPreviewWidth == 0 ) {
nPreviewWidth = ( cinfo.image_width*nPreviewHeight )/cinfo.image_height;
if( nPreviewWidth <= 0 )
nPreviewWidth = 1;
} else if( nPreviewHeight == 0 ) {
nPreviewHeight = ( cinfo.image_height*nPreviewWidth )/cinfo.image_width;
if( nPreviewHeight <= 0 )
nPreviewHeight = 1;
}
for( cinfo.scale_denom = 1; cinfo.scale_denom < 8; cinfo.scale_denom *= 2 )
{
if( cinfo.image_width < nPreviewWidth * cinfo.scale_denom )
break;
if( cinfo.image_height < nPreviewHeight * cinfo.scale_denom )
break;
}
if( cinfo.scale_denom > 1 )
{
cinfo.dct_method = JDCT_FASTEST;
cinfo.do_fancy_upsampling = sal_False;
cinfo.do_block_smoothing = sal_False;
}
}
jpeg_start_decompress( &cinfo );
nWidth = cinfo.output_width;
nHeight = cinfo.output_height;
aCreateBitmapParam.nWidth = nWidth;
aCreateBitmapParam.nHeight = nHeight;
aCreateBitmapParam.density_unit = cinfo.density_unit;
aCreateBitmapParam.X_density = cinfo.X_density;
aCreateBitmapParam.Y_density = cinfo.Y_density;
aCreateBitmapParam.bGray = cinfo.output_components == 1;
pDIB = CreateBitmap( pJPEGReader, &aCreateBitmapParam );
nAlignedWidth = aCreateBitmapParam.nAlignedWidth;
range_limit=cinfo.sample_range_limit;
if ( cinfo.out_color_space == JCS_CMYK )
{
nScanLineBufferComponents = cinfo.output_width * 4;
pScanLineBuffer = rtl_allocateMemory( nScanLineBufferComponents );
}
if( pDIB )
{
if( aCreateBitmapParam.bTopDown )
pTmp = pDIB;
else
{
pTmp = pDIB + ( nHeight - 1 ) * nAlignedWidth;
nAlignedWidth = -nAlignedWidth;
}
for ( *pLines = 0; *pLines < nHeight; (*pLines)++ )
{
if (pScanLineBuffer!=NULL) { // in other words cinfo.out_color_space == JCS_CMYK
int i;
int j;
jpeg_read_scanlines( &cinfo, (JSAMPARRAY) &pScanLineBuffer, 1 );
// convert CMYK to RGB
for( i=0, j=0; i < nScanLineBufferComponents; i+=4, j+=3 )
{
int c_=255-pScanLineBuffer[i+0];
int m_=255-pScanLineBuffer[i+1];
int y_=255-pScanLineBuffer[i+2];
int k_=255-pScanLineBuffer[i+3];
pTmp[j+0]=range_limit[ 255L - ( c_ + k_ ) ];
pTmp[j+1]=range_limit[ 255L - ( m_ + k_ ) ];
pTmp[j+2]=range_limit[ 255L - ( y_ + k_ ) ];
}
} else {
jpeg_read_scanlines( &cinfo, (JSAMPARRAY) &pTmp, 1 );
}
/* PENDING ??? */
if ( cinfo.err->msg_code == 113 )
break;
pTmp += nAlignedWidth;
}
}
if ( pDIB )
{
jpeg_finish_decompress( &cinfo );
}
else
{
jpeg_abort_decompress( &cinfo );
}
if (pScanLineBuffer!=NULL) {
rtl_freeMemory( pScanLineBuffer );
pScanLineBuffer=NULL;
}
Exit:
if( bDecompCreated )
jpeg_destroy_decompress( &cinfo );
}
long WriteJPEG( void* pJPEGWriter, void* pOStm,
long nWidth, long nHeight, long bGreys,
long nQualityPercent, void* pCallbackData )
{
struct jpeg_compress_struct cinfo;
struct my_error_mgr jerr;
void* pScanline;
long nY;
// declare bCompCreated, bRet volatile because of gcc
// warning: variable 'bCompCreated' might be clobbered by `longjmp' or `vfork'
volatile long bCompCreated = 0;
volatile long bRet = 0;
if ( setjmp( jerr.setjmp_buffer ) )
goto Exit;
cinfo.err = jpeg_std_error( &jerr.pub );
jerr.pub.error_exit = my_error_exit;
jerr.pub.output_message = my_output_message;
jpeg_create_compress( &cinfo );
bCompCreated = 1;
jpeg_svstream_dest( &cinfo, pOStm );
cinfo.image_width = (JDIMENSION) nWidth;
cinfo.image_height = (JDIMENSION) nHeight;
if ( bGreys )
{
cinfo.input_components = 1;
cinfo.in_color_space = JCS_GRAYSCALE;
}
else
{
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
}
jpeg_set_defaults( &cinfo );
jpeg_set_quality( &cinfo, (int) nQualityPercent, sal_False );
if ( ( nWidth > 128 ) || ( nHeight > 128 ) )
jpeg_simple_progression( &cinfo );
jpeg_start_compress( &cinfo, sal_True );
for( nY = 0; nY < nHeight; nY++ )
{
pScanline = GetScanline( pJPEGWriter, nY );
if( pScanline )
jpeg_write_scanlines( &cinfo, (JSAMPARRAY) &pScanline, 1 );
if( JPEGCallback( pCallbackData, nY * 100L / nHeight ) )
goto Exit;
}
bRet = 1;
jpeg_finish_compress(&cinfo);
Exit:
if ( bCompCreated )
jpeg_destroy_compress( &cinfo );
return bRet;
}