diff options
author | Tomaž Vajngerl <quikee@gmail.com> | 2013-04-21 13:50:09 +0200 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2013-04-21 21:55:44 +0200 |
commit | 759f24152a2ab44189de0fd242c3e409c9a479a8 (patch) | |
tree | 2412fb6166d63425e6e0b6541c787b2676dc87ab /vcl | |
parent | 252b6b608f5fc35b154846c3487809fe5e147ab7 (diff) |
Split JpegWriter and JpegReader out from jpeg.cxx/hxx
jpeg.cxx/hxx contains classes JpegWriter and JpegReader which are
considered private. Split this two classes and other related
functions into its own files.
Change-Id: I41c1139b30a4dc19e03b2232dfe0986cc05d0c08
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/Library_vcl.mk | 2 | ||||
-rw-r--r-- | vcl/source/filter/jpeg/JpegReader.cxx | 473 | ||||
-rw-r--r-- | vcl/source/filter/jpeg/JpegReader.hxx | 62 | ||||
-rw-r--r-- | vcl/source/filter/jpeg/JpegWriter.cxx | 274 | ||||
-rw-r--r-- | vcl/source/filter/jpeg/JpegWriter.hxx | 58 | ||||
-rw-r--r-- | vcl/source/filter/jpeg/jpeg.cxx | 712 | ||||
-rw-r--r-- | vcl/source/filter/jpeg/jpeg.h | 8 | ||||
-rw-r--r-- | vcl/source/filter/jpeg/jpeg.hxx | 72 | ||||
-rw-r--r-- | vcl/source/filter/jpeg/jpegc.c | 108 |
9 files changed, 961 insertions, 808 deletions
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index 17d1ef6c1723..057e508b7be0 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -178,6 +178,8 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/filter/ixbm/xbmread \ vcl/source/filter/ixpm/xpmread \ vcl/source/filter/jpeg/jpeg \ + vcl/source/filter/jpeg/JpegReader \ + vcl/source/filter/jpeg/JpegWriter \ vcl/source/filter/wmf/emfwr \ vcl/source/filter/wmf/enhwmf \ vcl/source/filter/wmf/winmtf \ diff --git a/vcl/source/filter/jpeg/JpegReader.cxx b/vcl/source/filter/jpeg/JpegReader.cxx new file mode 100644 index 000000000000..1fa4fa9edf10 --- /dev/null +++ b/vcl/source/filter/jpeg/JpegReader.cxx @@ -0,0 +1,473 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + + +#include <tools/solar.h> + +extern "C" +{ + #include "stdio.h" + #include "jpeg.h" + #include <jpeglib.h> + #include <jerror.h> +} + +#include "JpegReader.hxx" +#include <vcl/bmpacc.hxx> +#include <vcl/FilterConfigItem.hxx> +#include <vcl/graphicfilter.hxx> + +#define JPEG_MIN_READ 512 +#define BUFFER_SIZE 4096 + +extern "C" void* CreateBitmapFromJPEGReader( void* pJPEGReader, void* pJPEGCreateBitmapParam ) +{ + return ( (JPEGReader*) pJPEGReader )->CreateBitmap( pJPEGCreateBitmapParam ); +} + +/* Expanded data source object for stdio input */ + +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + SvStream* stream; /* source stream */ + JOCTET* buffer; /* start of buffer */ + boolean start_of_file; /* have we gotten any data yet? */ +} SourceManagerStruct; + +typedef SourceManagerStruct* SourceManagerStructPointer; + +/* + * Initialize source --- called by jpeg_read_header + * before any data is actually read. + */ +extern "C" void init_source (j_decompress_ptr cinfo) +{ + SourceManagerStructPointer source = (SourceManagerStructPointer) cinfo->src; + + /* We reset the empty-input-file flag for each image, + * but we don't clear the input buffer. + * This is correct behavior for reading a series of images from one source. + */ + source->start_of_file = sal_True; +} + +long StreamRead( SvStream* pStream, void* pBuffer, long nBufferSize ) +{ + long nRead = 0; + + if( pStream->GetError() != ERRCODE_IO_PENDING ) + { + long nInitialPosition = pStream->Tell(); + + nRead = (long) pStream->Read( pBuffer, nBufferSize ); + + if( pStream->GetError() == ERRCODE_IO_PENDING ) + { + // Damit wir wieder an die alte Position + // seeken koennen, setzen wir den Error temp.zurueck + pStream->ResetError(); + pStream->Seek( nInitialPosition ); + pStream->SetError( ERRCODE_IO_PENDING ); + } + } + + return nRead; +} + +extern "C" boolean fill_input_buffer (j_decompress_ptr cinfo) +{ + SourceManagerStructPointer source = (SourceManagerStructPointer) cinfo->src; + size_t nbytes; + + nbytes = StreamRead(source->stream, source->buffer, BUFFER_SIZE); + + if (!nbytes) + { + if (source->start_of_file) /* Treat empty input file as fatal error */ + { + ERREXIT(cinfo, JERR_INPUT_EMPTY); + } + WARNMS(cinfo, JWRN_JPEG_EOF); + /* Insert a fake EOI marker */ + source->buffer[0] = (JOCTET) 0xFF; + source->buffer[1] = (JOCTET) JPEG_EOI; + nbytes = 2; + } + + source->pub.next_input_byte = source->buffer; + source->pub.bytes_in_buffer = nbytes; + source->start_of_file = sal_False; + + return sal_True; +} + +extern "C" void skip_input_data (j_decompress_ptr cinfo, long numberOfBytes) +{ + SourceManagerStructPointer source = (SourceManagerStructPointer) cinfo->src; + + /* Just a dumb implementation for now. Could use fseek() except + * it doesn't work on pipes. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if (numberOfBytes > 0) + { + while (numberOfBytes > (long) source->pub.bytes_in_buffer) + { + numberOfBytes -= (long) source->pub.bytes_in_buffer; + (void) fill_input_buffer(cinfo); + + /* note we assume that fill_input_buffer will never return sal_False, + * so suspension need not be handled. + */ + } + source->pub.next_input_byte += (size_t) numberOfBytes; + source->pub.bytes_in_buffer -= (size_t) numberOfBytes; + } +} + +extern "C" void term_source (j_decompress_ptr) +{ + /* no work necessary here */ +} + +extern "C" void jpeg_svstream_src (j_decompress_ptr cinfo, void* input) +{ + SourceManagerStructPointer source; + SvStream* stream = (SvStream*)input; + + /* The source object and input buffer are made permanent so that a series + * of JPEG images can be read from the same file by calling jpeg_stdio_src + * only before the first one. (If we discarded the buffer at the end of + * one image, we'd likely lose the start of the next one.) + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + + if (cinfo->src == NULL) + { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(SourceManagerStruct)); + source = (SourceManagerStructPointer) cinfo->src; + source->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, BUFFER_SIZE * sizeof(JOCTET)); + } + + source = (SourceManagerStructPointer) cinfo->src; + source->pub.init_source = init_source; + source->pub.fill_input_buffer = fill_input_buffer; + source->pub.skip_input_data = skip_input_data; + source->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + source->pub.term_source = term_source; + source->stream = stream; + source->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + source->pub.next_input_byte = NULL; /* until buffer loaded */ +} + +JPEGReader::JPEGReader( SvStream& rStream, void* /*pCallData*/, sal_Bool bSetLogSize ) : + mrStream ( rStream ), + mpAcc ( NULL ), + mpAcc1 ( NULL ), + mpBuffer ( NULL ), + mnLastPos ( rStream.Tell() ), + mnLastLines ( 0 ), + mbSetLogSize ( bSetLogSize ) +{ + maUpperName = OUString("SVIJPEG"); + mnFormerPos = mnLastPos; +} + +JPEGReader::~JPEGReader() +{ + if( mpBuffer ) + rtl_freeMemory( mpBuffer ); + + if( mpAcc ) + maBmp.ReleaseAccess( mpAcc ); + + if( mpAcc1 ) + maBmp1.ReleaseAccess( mpAcc1 ); +} + +void* JPEGReader::CreateBitmap( void* _pParam ) +{ + JPEGCreateBitmapParam *pParam = (JPEGCreateBitmapParam *) _pParam; + + if (pParam->nWidth > SAL_MAX_INT32 / 8 || pParam->nHeight > SAL_MAX_INT32 / 8) + return NULL; // avoid overflows later + + Size aSize( pParam->nWidth, pParam->nHeight ); + sal_Bool bGray = pParam->bGray != 0; + + void* pBmpBuf = NULL; + + if( mpAcc ) + { + maBmp.ReleaseAccess( mpAcc ); + } + + sal_uInt64 nSize = aSize.Width(); + nSize *= aSize.Height(); + if (nSize > SAL_MAX_INT32 / 24) + return NULL; + + if( bGray ) + { + BitmapPalette aGrayPal( 256 ); + + for( sal_uInt16 n = 0; n < 256; n++ ) + { + const sal_uInt8 cGray = (sal_uInt8) n; + aGrayPal[ n ] = BitmapColor( cGray, cGray, cGray ); + } + + maBmp = Bitmap( aSize, 8, &aGrayPal ); + } + else + { + maBmp = Bitmap( aSize, 24 ); + } + + if ( mbSetLogSize ) + { + unsigned long nUnit = ((JPEGCreateBitmapParam*)pParam)->density_unit; + + if( ( ( 1 == nUnit ) || ( 2 == nUnit ) ) && pParam->X_density && pParam->Y_density ) + { + Point aEmptyPoint; + Fraction aFractX( 1, pParam->X_density ); + Fraction aFractY( 1, pParam->Y_density ); + MapMode aMapMode( nUnit == 1 ? MAP_INCH : MAP_CM, aEmptyPoint, aFractX, aFractY ); + Size aPrefSize = OutputDevice::LogicToLogic( aSize, aMapMode, MAP_100TH_MM ); + + maBmp.SetPrefSize( aPrefSize ); + maBmp.SetPrefMapMode( MapMode( MAP_100TH_MM ) ); + } + } + + mpAcc = maBmp.AcquireWriteAccess(); + + if( mpAcc ) + { + const sal_uLong nFormat = mpAcc->GetScanlineFormat(); + + if( + ( bGray && ( BMP_FORMAT_8BIT_PAL == nFormat ) ) || + ( !bGray && ( BMP_FORMAT_24BIT_TC_RGB == nFormat ) ) + ) + { + pBmpBuf = mpAcc->GetBuffer(); + pParam->nAlignedWidth = mpAcc->GetScanlineSize(); + pParam->bTopDown = mpAcc->IsTopDown(); + } + else + { + pParam->nAlignedWidth = AlignedWidth4Bytes( aSize.Width() * ( bGray ? 8 : 24 ) ); + pParam->bTopDown = sal_True; + pBmpBuf = mpBuffer = rtl_allocateMemory( pParam->nAlignedWidth * aSize.Height() ); + } + } + + // clean up, if no Bitmap buffer can be provided. + if ( !pBmpBuf ) + { + maBmp.ReleaseAccess( mpAcc ); + mpAcc = NULL; + } + + return pBmpBuf; +} + +void JPEGReader::FillBitmap() +{ + if( mpBuffer && mpAcc ) + { + HPBYTE pTmp; + BitmapColor aColor; + long nAlignedWidth; + long nWidth = mpAcc->Width(); + long nHeight = mpAcc->Height(); + + if( mpAcc->GetBitCount() == 8 ) + { + BitmapColor* pCols = new BitmapColor[ 256 ]; + + for( sal_uInt16 n = 0; n < 256; n++ ) + { + const sal_uInt8 cGray = (sal_uInt8) n; + pCols[ n ] = mpAcc->GetBestMatchingColor( BitmapColor( cGray, cGray, cGray ) ); + } + + nAlignedWidth = AlignedWidth4Bytes( mpAcc->Width() * 8L ); + + for( long nY = 0L; nY < nHeight; nY++ ) + { + pTmp = (sal_uInt8*) mpBuffer + nY * nAlignedWidth; + + for( long nX = 0L; nX < nWidth; nX++ ) + { + mpAcc->SetPixel( nY, nX, pCols[ *pTmp++ ] ); + } + } + + delete[] pCols; + } + else + { + nAlignedWidth = AlignedWidth4Bytes( mpAcc->Width() * 24L ); + + for( long nY = 0L; nY < nHeight; nY++ ) + { + pTmp = (sal_uInt8*) mpBuffer + nY * nAlignedWidth; + + for( long nX = 0L; nX < nWidth; nX++ ) + { + aColor.SetRed( *pTmp++ ); + aColor.SetGreen( *pTmp++ ); + aColor.SetBlue( *pTmp++ ); + mpAcc->SetPixel( nY, nX, aColor ); + } + } + } + } +} + +Graphic JPEGReader::CreateIntermediateGraphic( const Bitmap& rBitmap, long nLines ) +{ + Graphic aGraphic; + const Size aSizePixel( rBitmap.GetSizePixel() ); + + if( !mnLastLines ) + { + if( mpAcc1 ) + { + maBmp1.ReleaseAccess( mpAcc1 ); + } + + maBmp1 = Bitmap( rBitmap.GetSizePixel(), 1 ); + maBmp1.Erase( Color( COL_WHITE ) ); + mpAcc1 = maBmp1.AcquireWriteAccess(); + } + + if( nLines && ( nLines < aSizePixel.Height() ) ) + { + if( mpAcc1 ) + { + const long nNewLines = nLines - mnLastLines; + + if( nNewLines ) + { + mpAcc1->SetFillColor( Color( COL_BLACK ) ); + mpAcc1->FillRect( Rectangle( Point( 0, mnLastLines ), Size( mpAcc1->Width(), nNewLines ) ) ); + } + + maBmp1.ReleaseAccess( mpAcc1 ); + aGraphic = BitmapEx( rBitmap, maBmp1 ); + mpAcc1 = maBmp1.AcquireWriteAccess(); + } + else + { + aGraphic = rBitmap; + } + } + else + aGraphic = rBitmap; + + mnLastLines = nLines; + + return aGraphic; +} + +ReadState JPEGReader::Read( Graphic& rGraphic ) +{ + long nEndPosition; + long nLines; + ReadState eReadState; + sal_Bool bRet = sal_False; + sal_uInt8 cDummy; + + // TODO: is it possible to get rid of this seek to the end? + // check if the stream's end is already available + mrStream.Seek( STREAM_SEEK_TO_END ); + mrStream >> cDummy; + nEndPosition = mrStream.Tell(); + + // else check if at least JPEG_MIN_READ bytes can be read + if( mrStream.GetError() == ERRCODE_IO_PENDING ) + { + mrStream.ResetError(); + if( ( nEndPosition - mnFormerPos ) < JPEG_MIN_READ ) + { + mrStream.Seek( mnLastPos ); + return JPEGREAD_NEED_MORE; + } + } + + // seek back to the original position + mrStream.Seek( mnLastPos ); + + Size aPreviewSize = GetPreviewSize(); + SetJpegPreviewSizeHint( aPreviewSize.Width(), aPreviewSize.Height() ); + + // read the (partial) image + ReadJPEG( this, &mrStream, &nLines ); + + if( mpAcc ) + { + if( mpBuffer ) + { + FillBitmap(); + rtl_freeMemory( mpBuffer ); + mpBuffer = NULL; + } + + maBmp.ReleaseAccess( mpAcc ); + mpAcc = NULL; + + if( mrStream.GetError() == ERRCODE_IO_PENDING ) + { + rGraphic = CreateIntermediateGraphic( maBmp, nLines ); + } + else + { + rGraphic = maBmp; + } + + bRet = sal_True; + } + else if( mrStream.GetError() == ERRCODE_IO_PENDING ) + { + bRet = sal_True; + } + + // Set status ( Pending has priority ) + if( mrStream.GetError() == ERRCODE_IO_PENDING ) + { + eReadState = JPEGREAD_NEED_MORE; + mrStream.ResetError(); + mnFormerPos = mrStream.Tell(); + } + else + { + eReadState = bRet ? JPEGREAD_OK : JPEGREAD_ERROR; + } + + return eReadState; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/filter/jpeg/JpegReader.hxx b/vcl/source/filter/jpeg/JpegReader.hxx new file mode 100644 index 000000000000..792792fb24d0 --- /dev/null +++ b/vcl/source/filter/jpeg/JpegReader.hxx @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#ifndef _JPEG_READER_HXX +#define _JPEG_READER_HXX + +#include <vcl/graph.hxx> +#include <vcl/fltcall.hxx> +#include <com/sun/star/uno/Sequence.h> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> + +enum ReadState +{ + JPEGREAD_OK, + JPEGREAD_ERROR, + JPEGREAD_NEED_MORE +}; + +class JPEGReader : public GraphicReader +{ + SvStream& mrStream; + Bitmap maBmp; + Bitmap maBmp1; + BitmapWriteAccess* mpAcc; + BitmapWriteAccess* mpAcc1; + void* mpBuffer; + long mnLastPos; + long mnFormerPos; + long mnLastLines; + sal_Bool mbSetLogSize; + + Graphic CreateIntermediateGraphic( const Bitmap& rBitmap, long nLines ); + void FillBitmap(); + +public: + JPEGReader( SvStream& rStream, void* pCallData, sal_Bool bSetLogSize ); + virtual ~JPEGReader(); + + ReadState Read( Graphic& rGraphic ); + void* CreateBitmap( void* JPEGCreateBitmapParam ); +}; + +#endif //_JPEG_READER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/filter/jpeg/JpegWriter.cxx b/vcl/source/filter/jpeg/JpegWriter.cxx new file mode 100644 index 000000000000..44d77ea3caf9 --- /dev/null +++ b/vcl/source/filter/jpeg/JpegWriter.cxx @@ -0,0 +1,274 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +extern "C" +{ + #include "stdio.h" + #include "jpeg.h" + #include <jpeglib.h> + #include <jerror.h> +} + +#include <tools/solar.h> +#include <vcl/bmpacc.hxx> +#include <vcl/FilterConfigItem.hxx> +#include <vcl/graphicfilter.hxx> +#include "JpegWriter.hxx" + +#define BUFFER_SIZE 4096 + +extern "C" void* GetScanline( void* pJPEGWriter, long nY ) +{ + return ( (JPEGWriter*) pJPEGWriter )->GetScanline( nY ); +} + +struct JPEGCallbackStruct +{ + css::uno::Reference< css::task::XStatusIndicator > xStatusIndicator; +}; + +extern "C" long JPEGCallback( void* pCallbackData, long nPercent ) +{ + JPEGCallbackStruct* pCallbackStruct = (JPEGCallbackStruct*)pCallbackData; + if ( pCallbackStruct && pCallbackStruct->xStatusIndicator.is() ) + { + pCallbackStruct->xStatusIndicator->setValue( nPercent ); + } + return 0L; +} + +typedef struct +{ + struct jpeg_destination_mgr pub; /* public fields */ + SvStream* stream; /* target stream */ + JOCTET * buffer; /* start of buffer */ +} DestinationManagerStruct; + +typedef DestinationManagerStruct* DestinationManagerStructPointer; + +extern "C" void init_destination (j_compress_ptr cinfo) +{ + DestinationManagerStructPointer destination = (DestinationManagerStructPointer) cinfo->dest; + + /* Allocate the output buffer -- it will be released when done with image */ + destination->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, BUFFER_SIZE * sizeof(JOCTET)); + + destination->pub.next_output_byte = destination->buffer; + destination->pub.free_in_buffer = BUFFER_SIZE; +} + +extern "C" boolean empty_output_buffer (j_compress_ptr cinfo) +{ + DestinationManagerStructPointer destination = (DestinationManagerStructPointer) cinfo->dest; + + if (destination->stream->Write(destination->buffer, BUFFER_SIZE) != (size_t) BUFFER_SIZE) + { + ERREXIT(cinfo, JERR_FILE_WRITE); + } + + destination->pub.next_output_byte = destination->buffer; + destination->pub.free_in_buffer = BUFFER_SIZE; + + return sal_True; +} + +extern "C" void term_destination (j_compress_ptr cinfo) +{ + DestinationManagerStructPointer destination = (DestinationManagerStructPointer) cinfo->dest; + size_t datacount = BUFFER_SIZE - destination->pub.free_in_buffer; + + /* Write any data remaining in the buffer */ + if (datacount > 0) + { + if (destination->stream->Write(destination->buffer, datacount) != datacount) + { + ERREXIT(cinfo, JERR_FILE_WRITE); + } + } +} + +extern "C" void jpeg_svstream_dest (j_compress_ptr cinfo, void* output) +{ + SvStream* stream = (SvStream*) output; + DestinationManagerStructPointer destination; + + /* The destination object is made permanent so that multiple JPEG images + * can be written to the same file without re-executing jpeg_svstream_dest. + * This makes it dangerous to use this manager and a different destination + * manager serially with the same JPEG object, because their private object + * sizes may be different. Caveat programmer. + */ + if (cinfo->dest == NULL) + { /* first time for this JPEG object? */ + cinfo->dest = (struct jpeg_destination_mgr*) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(DestinationManagerStruct)); + } + + destination = (DestinationManagerStructPointer) cinfo->dest; + destination->pub.init_destination = init_destination; + destination->pub.empty_output_buffer = empty_output_buffer; + destination->pub.term_destination = term_destination; + destination->stream = stream; +} + +JPEGWriter::JPEGWriter( SvStream& rStream, const css::uno::Sequence< css::beans::PropertyValue >* pFilterData, bool* pExportWasGrey ) : + mrStream ( rStream ), + mpReadAccess ( NULL ), + mpBuffer ( NULL ), + mpExpWasGrey ( pExportWasGrey ) +{ + FilterConfigItem aConfigItem( (css::uno::Sequence< css::beans::PropertyValue >*) pFilterData ); + mbGreys = aConfigItem.ReadInt32( "ColorMode", 0 ) != 0; + mnQuality = aConfigItem.ReadInt32( "Quality", 75 ); + maChromaSubsampling = aConfigItem.ReadInt32( "ChromaSubsamplingMode", 0 ); + + if ( pFilterData ) + { + int nArgs = pFilterData->getLength(); + const css::beans::PropertyValue* pValues = pFilterData->getConstArray(); + while( nArgs-- ) + { + if ( pValues->Name == "StatusIndicator" ) + { + pValues->Value >>= mxStatusIndicator; + } + pValues++; + } + } +} + +void* JPEGWriter::GetScanline( long nY ) +{ + void* pScanline = NULL; + + if( mpReadAccess ) + { + if( mbNative ) + { + pScanline = mpReadAccess->GetScanline( nY ); + } + else if( mpBuffer ) + { + BitmapColor aColor; + long nWidth = mpReadAccess->Width(); + sal_uInt8* pTmp = mpBuffer; + + if( mpReadAccess->HasPalette() ) + { + for( long nX = 0L; nX < nWidth; nX++ ) + { + aColor = mpReadAccess->GetPaletteColor( (sal_uInt8) mpReadAccess->GetPixel( nY, nX ) ); + *pTmp++ = aColor.GetRed(); + if ( !mbGreys ) + { + *pTmp++ = aColor.GetGreen(); + *pTmp++ = aColor.GetBlue(); + } + } + } + else + { + for( long nX = 0L; nX < nWidth; nX++ ) + { + aColor = mpReadAccess->GetPixel( nY, nX ); + *pTmp++ = aColor.GetRed(); + if ( !mbGreys ) + { + *pTmp++ = aColor.GetGreen(); + *pTmp++ = aColor.GetBlue(); + } + } + } + + pScanline = mpBuffer; + } + } + + return pScanline; +} + +sal_Bool JPEGWriter::Write( const Graphic& rGraphic ) +{ + sal_Bool bRet = sal_False; + + if ( mxStatusIndicator.is() ) + { + OUString aMsg; + mxStatusIndicator->start( aMsg, 100 ); + } + + Bitmap aGraphicBmp( rGraphic.GetBitmap() ); + + if ( mbGreys ) + { + if ( !aGraphicBmp.Convert( BMP_CONVERSION_8BIT_GREYS ) ) + aGraphicBmp = rGraphic.GetBitmap(); + } + + mpReadAccess = aGraphicBmp.AcquireReadAccess(); + + if ( !mbGreys ) // bitmap was not explicitly converted into greyscale, + { // check if source is greyscale only + + sal_Bool bIsGrey = sal_True; + + long nWidth = mpReadAccess->Width(); + for ( long nY = 0; bIsGrey && ( nY < mpReadAccess->Height() ); nY++ ) + { + BitmapColor aColor; + for( long nX = 0L; bIsGrey && ( nX < nWidth ); nX++ ) + { + aColor = mpReadAccess->HasPalette() ? mpReadAccess->GetPaletteColor( (sal_uInt8) mpReadAccess->GetPixel( nY, nX ) ) + : mpReadAccess->GetPixel( nY, nX ); + bIsGrey = ( aColor.GetRed() == aColor.GetGreen() ) && ( aColor.GetRed() == aColor.GetBlue() ); + } + } + if ( bIsGrey ) + mbGreys = sal_True; + } + + if( mpExpWasGrey ) + *mpExpWasGrey = mbGreys; + + if( mpReadAccess ) + { + mbNative = ( mpReadAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB ); + + if( !mbNative ) + mpBuffer = new sal_uInt8[ AlignedWidth4Bytes( mbGreys ? mpReadAccess->Width() * 8L : mpReadAccess->Width() * 24L ) ]; + + JPEGCallbackStruct aCallbackData; + aCallbackData.xStatusIndicator = mxStatusIndicator; + bRet = (sal_Bool) WriteJPEG( this, &mrStream, mpReadAccess->Width(), mpReadAccess->Height(), mbGreys, mnQuality, maChromaSubsampling, &aCallbackData ); + + delete[] mpBuffer; + mpBuffer = NULL; + + aGraphicBmp.ReleaseAccess( mpReadAccess ); + mpReadAccess = NULL; + } + if ( mxStatusIndicator.is() ) + mxStatusIndicator->end(); + + return bRet; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/filter/jpeg/JpegWriter.hxx b/vcl/source/filter/jpeg/JpegWriter.hxx new file mode 100644 index 000000000000..439f5474c09c --- /dev/null +++ b/vcl/source/filter/jpeg/JpegWriter.hxx @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#ifndef _JPEG_WRITER_HXX +#define _JPEG_WRITER_HXX + +#include <vcl/graph.hxx> +#include <vcl/fltcall.hxx> +#include <com/sun/star/uno/Sequence.h> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> + +class JPEGWriter +{ + SvStream& mrStream; + Bitmap maBitmap; + BitmapReadAccess* mpReadAccess; + sal_uInt8* mpBuffer; + sal_Bool mbNative; + sal_Bool mbGreys; + sal_Int32 mnQuality; + sal_Int32 maChromaSubsampling; + + bool* mpExpWasGrey; + + com::sun::star::uno::Reference< com::sun::star::task::XStatusIndicator > mxStatusIndicator; + +public: + JPEGWriter( SvStream& rStream, + const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData, + bool* pExportWasGrey = NULL ); + + virtual ~JPEGWriter() {}; + + void* GetScanline( long nY ); + sal_Bool Write( const Graphic& rGraphic ); + +}; + +#endif // _JPEG_WRITER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/filter/jpeg/jpeg.cxx b/vcl/source/filter/jpeg/jpeg.cxx index a4d1b2dd5d52..6687df28b762 100644 --- a/vcl/source/filter/jpeg/jpeg.cxx +++ b/vcl/source/filter/jpeg/jpeg.cxx @@ -17,729 +17,61 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ +#include "JpegReader.hxx" +#include "JpegWriter.hxx" #include <tools/solar.h> - -extern "C" -{ - #include "stdio.h" - #include "jpeg.h" - #include <jpeglib.h> - #include <jerror.h> -} - -#define _JPEGPRIVATE #include <vcl/bmpacc.hxx> -#include "jpeg.hxx" #include <vcl/FilterConfigItem.hxx> #include <vcl/graphicfilter.hxx> -using namespace ::com::sun::star; - -#define JPEGMINREAD 512 - -extern "C" void* CreateBitmapFromJPEGReader( void* pJPEGReader, void* pJPEGCreateBitmapParam ) -{ - return ( (JPEGReader*) pJPEGReader )->CreateBitmap( pJPEGCreateBitmapParam ); -} - -extern "C" void* GetScanline( void* pJPEGWriter, long nY ) -{ - return ( (JPEGWriter*) pJPEGWriter )->GetScanline( nY ); -} - -struct JPEGCallbackStruct -{ - uno::Reference< task::XStatusIndicator > xStatusIndicator; -}; - -extern "C" long JPEGCallback( void* pCallbackData, long nPercent ) -{ - JPEGCallbackStruct* pS = (JPEGCallbackStruct*)pCallbackData; - if ( pS && pS->xStatusIndicator.is() ) - { - pS->xStatusIndicator->setValue( nPercent ); - } - return 0L; -} - -#define BUF_SIZE 4096 - -typedef struct -{ - struct jpeg_destination_mgr pub; /* public fields */ - - SvStream* outfile; /* target stream */ - JOCTET * buffer; /* start of buffer */ -} my_destination_mgr; - -typedef my_destination_mgr* my_dest_ptr; - -extern "C" void init_destination (j_compress_ptr cinfo) -{ - my_dest_ptr dest = (my_dest_ptr) cinfo->dest; - - /* Allocate the output buffer --- it will be released when done with image */ - dest->buffer = (JOCTET *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - BUF_SIZE * sizeof(JOCTET)); - - dest->pub.next_output_byte = dest->buffer; - dest->pub.free_in_buffer = BUF_SIZE; -} - -extern "C" boolean empty_output_buffer (j_compress_ptr cinfo) -{ - my_dest_ptr dest = (my_dest_ptr) cinfo->dest; - - if (dest->outfile->Write(dest->buffer, BUF_SIZE) != - (size_t) BUF_SIZE) - ERREXIT(cinfo, JERR_FILE_WRITE); - - dest->pub.next_output_byte = dest->buffer; - dest->pub.free_in_buffer = BUF_SIZE; - - return sal_True; -} - -extern "C" void term_destination (j_compress_ptr cinfo) -{ - my_dest_ptr dest = (my_dest_ptr) cinfo->dest; - size_t datacount = BUF_SIZE - dest->pub.free_in_buffer; - - /* Write any data remaining in the buffer */ - if (datacount > 0) { - if (dest->outfile->Write(dest->buffer, datacount) != datacount) - ERREXIT(cinfo, JERR_FILE_WRITE); - } -} - -extern "C" void jpeg_svstream_dest (j_compress_ptr cinfo, void* out) -{ - SvStream * outfile = (SvStream*)out; - my_dest_ptr dest; - - /* The destination object is made permanent so that multiple JPEG images - * can be written to the same file without re-executing jpeg_svstream_dest. - * This makes it dangerous to use this manager and a different destination - * manager serially with the same JPEG object, because their private object - * sizes may be different. Caveat programmer. - */ - if (cinfo->dest == NULL) { /* first time for this JPEG object? */ - cinfo->dest = (struct jpeg_destination_mgr *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - sizeof(my_destination_mgr)); - } - - dest = (my_dest_ptr) cinfo->dest; - dest->pub.init_destination = init_destination; - dest->pub.empty_output_buffer = empty_output_buffer; - dest->pub.term_destination = term_destination; - dest->outfile = outfile; -} - -/* Expanded data source object for stdio input */ - -typedef struct { - struct jpeg_source_mgr pub; /* public fields */ - - SvStream * infile; /* source stream */ - JOCTET * buffer; /* start of buffer */ - boolean start_of_file; /* have we gotten any data yet? */ -} my_source_mgr; - -typedef my_source_mgr * my_src_ptr; - -/* - * Initialize source --- called by jpeg_read_header - * before any data is actually read. - */ - -extern "C" void init_source (j_decompress_ptr cinfo) -{ - my_src_ptr src = (my_src_ptr) cinfo->src; - - /* We reset the empty-input-file flag for each image, - * but we don't clear the input buffer. - * This is correct behavior for reading a series of images from one source. - */ - src->start_of_file = sal_True; -} - -long StreamRead( SvStream* pSvStm, void* pBuffer, long nBufferSize ) +sal_Bool ImportJPEG( SvStream& rInputStream, Graphic& rGraphic, void* pCallerData, sal_Int32 nImportFlags ) { - long nRead = 0; - - if( pSvStm->GetError() != ERRCODE_IO_PENDING ) - { - long nActPos = pSvStm->Tell(); - - nRead = (long) pSvStm->Read( pBuffer, nBufferSize ); - - if( pSvStm->GetError() == ERRCODE_IO_PENDING ) - { - // Damit wir wieder an die alte Position - // seeken koennen, setzen wir den Error temp.zurueck - pSvStm->ResetError(); - pSvStm->Seek( nActPos ); - pSvStm->SetError( ERRCODE_IO_PENDING ); - } - } - - return nRead; -} - -extern "C" boolean fill_input_buffer (j_decompress_ptr cinfo) -{ - my_src_ptr src = (my_src_ptr) cinfo->src; - size_t nbytes; - - nbytes = StreamRead(src->infile, src->buffer, BUF_SIZE); - - if (!nbytes) { - if (src->start_of_file) /* Treat empty input file as fatal error */ - ERREXIT(cinfo, JERR_INPUT_EMPTY); - WARNMS(cinfo, JWRN_JPEG_EOF); - /* Insert a fake EOI marker */ - src->buffer[0] = (JOCTET) 0xFF; - src->buffer[1] = (JOCTET) JPEG_EOI; - nbytes = 2; - } - - src->pub.next_input_byte = src->buffer; - src->pub.bytes_in_buffer = nbytes; - src->start_of_file = sal_False; - - return sal_True; -} - -extern "C" void skip_input_data (j_decompress_ptr cinfo, long num_bytes) -{ - my_src_ptr src = (my_src_ptr) cinfo->src; - - /* Just a dumb implementation for now. Could use fseek() except - * it doesn't work on pipes. Not clear that being smart is worth - * any trouble anyway --- large skips are infrequent. - */ - if (num_bytes > 0) { - while (num_bytes > (long) src->pub.bytes_in_buffer) { - num_bytes -= (long) src->pub.bytes_in_buffer; - (void) fill_input_buffer(cinfo); - /* note we assume that fill_input_buffer will never return sal_False, - * so suspension need not be handled. - */ - } - src->pub.next_input_byte += (size_t) num_bytes; - src->pub.bytes_in_buffer -= (size_t) num_bytes; - } -} - -extern "C" void term_source (j_decompress_ptr) -{ - /* no work necessary here */ -} - -extern "C" void jpeg_svstream_src (j_decompress_ptr cinfo, void * in) -{ - my_src_ptr src; - SvStream * infile = (SvStream*)in; - - /* The source object and input buffer are made permanent so that a series - * of JPEG images can be read from the same file by calling jpeg_stdio_src - * only before the first one. (If we discarded the buffer at the end of - * one image, we'd likely lose the start of the next one.) - * This makes it unsafe to use this manager and a different source - * manager serially with the same JPEG object. Caveat programmer. - */ - if (cinfo->src == NULL) { /* first time for this JPEG object? */ - cinfo->src = (struct jpeg_source_mgr *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - sizeof(my_source_mgr)); - src = (my_src_ptr) cinfo->src; - src->buffer = (JOCTET *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - BUF_SIZE * sizeof(JOCTET)); - } - - src = (my_src_ptr) cinfo->src; - src->pub.init_source = init_source; - src->pub.fill_input_buffer = fill_input_buffer; - src->pub.skip_input_data = skip_input_data; - src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ - src->pub.term_source = term_source; - src->infile = infile; - src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ - src->pub.next_input_byte = NULL; /* until buffer loaded */ -} - -JPEGReader::JPEGReader( SvStream& rStm, void* /*pCallData*/, sal_Bool bSetLS ) : - rIStm ( rStm ), - pAcc ( NULL ), - pAcc1 ( NULL ), - pBuffer ( NULL ), - nLastPos ( rStm.Tell() ), - nLastLines ( 0 ), - bSetLogSize ( bSetLS ) -{ - maUpperName = OUString("SVIJPEG"); - nFormerPos = nLastPos; -} - -JPEGReader::~JPEGReader() -{ - if( pBuffer ) - rtl_freeMemory( pBuffer ); - - if( pAcc ) - aBmp.ReleaseAccess( pAcc ); - - if( pAcc1 ) - aBmp1.ReleaseAccess( pAcc1 ); -} - -void* JPEGReader::CreateBitmap( void* _pParam ) -{ - JPEGCreateBitmapParam *pParam = (JPEGCreateBitmapParam *) _pParam; - - if (pParam->nWidth > SAL_MAX_INT32/8 || pParam->nHeight > SAL_MAX_INT32/8) - return NULL; // avoid overflows later - - Size aSize( pParam->nWidth, pParam->nHeight ); - sal_Bool bGray = pParam->bGray != 0; - - void* pBmpBuf = NULL; - - if( pAcc ) - aBmp.ReleaseAccess( pAcc ); - - sal_uInt64 nSize = aSize.Width(); - nSize *= aSize.Height(); - if (nSize > SAL_MAX_INT32 / 24) - return NULL; - - if( bGray ) - { - BitmapPalette aGrayPal( 256 ); - - for( sal_uInt16 n = 0; n < 256; n++ ) - { - const sal_uInt8 cGray = (sal_uInt8) n; - aGrayPal[ n ] = BitmapColor( cGray, cGray, cGray ); - } - - aBmp = Bitmap( aSize, 8, &aGrayPal ); - } - else - aBmp = Bitmap( aSize, 24 ); - - if ( bSetLogSize ) - { - unsigned long nUnit = ((JPEGCreateBitmapParam*)pParam)->density_unit; - - if( ( ( 1 == nUnit ) || ( 2 == nUnit ) ) && - pParam->X_density && pParam->Y_density ) - { - Point aEmptyPoint; - Fraction aFractX( 1, pParam->X_density ); - Fraction aFractY( 1, pParam->Y_density ); - MapMode aMapMode( nUnit == 1 ? MAP_INCH : MAP_CM, aEmptyPoint, aFractX, aFractY ); - Size aPrefSize = OutputDevice::LogicToLogic( aSize, aMapMode, MAP_100TH_MM ); - - aBmp.SetPrefSize( aPrefSize ); - aBmp.SetPrefMapMode( MapMode( MAP_100TH_MM ) ); - } - } - - pAcc = aBmp.AcquireWriteAccess(); - - if( pAcc ) - { - const sal_uLong nFormat = pAcc->GetScanlineFormat(); - - if( - ( bGray && ( BMP_FORMAT_8BIT_PAL == nFormat ) ) || - ( !bGray && ( BMP_FORMAT_24BIT_TC_RGB == nFormat ) ) - ) - { - pBmpBuf = pAcc->GetBuffer(); - pParam->nAlignedWidth = pAcc->GetScanlineSize(); - pParam->bTopDown = pAcc->IsTopDown(); - } - else - { - pParam->nAlignedWidth = AlignedWidth4Bytes( aSize.Width() * ( bGray ? 8 : 24 ) ); - pParam->bTopDown = sal_True; - pBmpBuf = pBuffer = rtl_allocateMemory( pParam->nAlignedWidth * aSize.Height() ); - } - } - - // clean up, if no Bitmap buffer can be provided. - if ( !pBmpBuf ) - { - aBmp.ReleaseAccess( pAcc ); - pAcc = NULL; - } - - return pBmpBuf; -} - -void JPEGReader::FillBitmap() -{ - if( pBuffer && pAcc ) - { - HPBYTE pTmp; - BitmapColor aColor; - long nAlignedWidth; - long nWidth = pAcc->Width(); - long nHeight = pAcc->Height(); - - if( pAcc->GetBitCount() == 8 ) - { - BitmapColor* pCols = new BitmapColor[ 256 ]; - - for( sal_uInt16 n = 0; n < 256; n++ ) - { - const sal_uInt8 cGray = (sal_uInt8) n; - pCols[ n ] = pAcc->GetBestMatchingColor( BitmapColor( cGray, cGray, cGray ) ); - } - - nAlignedWidth = AlignedWidth4Bytes( pAcc->Width() * 8L ); - - for( long nY = 0L; nY < nHeight; nY++ ) - { - pTmp = (sal_uInt8*) pBuffer + nY * nAlignedWidth; - - for( long nX = 0L; nX < nWidth; nX++ ) - pAcc->SetPixel( nY, nX, pCols[ *pTmp++ ] ); - } - - delete[] pCols; - } - else - { - nAlignedWidth = AlignedWidth4Bytes( pAcc->Width() * 24L ); - - for( long nY = 0L; nY < nHeight; nY++ ) - { - pTmp = (sal_uInt8*) pBuffer + nY * nAlignedWidth; - - for( long nX = 0L; nX < nWidth; nX++ ) - { - aColor.SetRed( *pTmp++ ); - aColor.SetGreen( *pTmp++ ); - aColor.SetBlue( *pTmp++ ); - pAcc->SetPixel( nY, nX, aColor ); - } - } - } - } -} - -Graphic JPEGReader::CreateIntermediateGraphic( const Bitmap& rBitmap, long nLines ) -{ - Graphic aGraphic; - const Size aSizePix( rBitmap.GetSizePixel() ); - - if( !nLastLines ) - { - if( pAcc1 ) - aBmp1.ReleaseAccess( pAcc1 ); - - aBmp1 = Bitmap( rBitmap.GetSizePixel(), 1 ); - aBmp1.Erase( Color( COL_WHITE ) ); - pAcc1 = aBmp1.AcquireWriteAccess(); - } - - if( nLines && ( nLines < aSizePix.Height() ) ) - { - if( pAcc1 ) - { - const long nNewLines = nLines - nLastLines; - - if( nNewLines ) - { - pAcc1->SetFillColor( Color( COL_BLACK ) ); - pAcc1->FillRect( Rectangle( Point( 0, nLastLines ), - Size( pAcc1->Width(), nNewLines ) ) ); - } - - aBmp1.ReleaseAccess( pAcc1 ); - aGraphic = BitmapEx( rBitmap, aBmp1 ); - pAcc1 = aBmp1.AcquireWriteAccess(); - } - else - aGraphic = rBitmap; - } - else - aGraphic = rBitmap; - - nLastLines = nLines; - - return aGraphic; -} - -ReadState JPEGReader::Read( Graphic& rGraphic ) -{ - long nEndPos; - long nLines; + JPEGReader* pJPEGReader = (JPEGReader*) rGraphic.GetContext(); ReadState eReadState; - sal_Bool bRet = sal_False; - sal_uInt8 cDummy; - - // TODO: is it possible to get rid of this seek to the end? - // check if the stream's end is already available - rIStm.Seek( STREAM_SEEK_TO_END ); - rIStm >> cDummy; - nEndPos = rIStm.Tell(); - - // else check if at least JPEGMINREAD bytes can be read - if( rIStm.GetError() == ERRCODE_IO_PENDING ) - { - rIStm.ResetError(); - if( ( nEndPos - nFormerPos ) < JPEGMINREAD ) - { - rIStm.Seek( nLastPos ); - return JPEGREAD_NEED_MORE; - } - } - - // seek back to the original position - rIStm.Seek( nLastPos ); + sal_Bool bReturn = true; - - Size aPreviewSize = GetPreviewSize(); - SetJpegPreviewSizeHint( aPreviewSize.Width(), aPreviewSize.Height() ); - - // read the (partial) image - ReadJPEG( this, &rIStm, &nLines ); - - if( pAcc ) + if( !pJPEGReader ) { - if( pBuffer ) - { - FillBitmap(); - rtl_freeMemory( pBuffer ); - pBuffer = NULL; - } - - aBmp.ReleaseAccess( pAcc ); - pAcc = NULL; - - if( rIStm.GetError() == ERRCODE_IO_PENDING ) - { - rGraphic = CreateIntermediateGraphic( aBmp, nLines ); - } - else - { - rGraphic = aBmp; - } - - bRet = sal_True; + pJPEGReader = new JPEGReader( rInputStream, pCallerData, ( nImportFlags & GRFILTER_I_FLAGS_SET_LOGSIZE_FOR_JPEG ) != 0 ); } - else if( rIStm.GetError() == ERRCODE_IO_PENDING ) - bRet = sal_True; - // Set status ( Pending has priority ) - if( rIStm.GetError() == ERRCODE_IO_PENDING ) + if( nImportFlags & GRFILTER_I_FLAGS_FOR_PREVIEW ) { - eReadState = JPEGREAD_NEED_MORE; - rIStm.ResetError(); - nFormerPos = rIStm.Tell(); + pJPEGReader->SetPreviewSize( Size(128,128) ); } else { - if( bRet ) - eReadState = JPEGREAD_OK; - else - eReadState = JPEGREAD_ERROR; - } - - return eReadState; -} - -JPEGWriter::JPEGWriter( SvStream& rStream, const uno::Sequence< beans::PropertyValue >* pFilterData, bool* pExportWasGrey ) : - rOStm ( rStream ), - pAcc ( NULL ), - pBuffer ( NULL ), - pExpWasGrey ( pExportWasGrey ) -{ - FilterConfigItem aConfigItem( (uno::Sequence< beans::PropertyValue >*) pFilterData ); - bGreys = aConfigItem.ReadInt32( "ColorMode", 0 ) != 0; - nQuality = aConfigItem.ReadInt32( "Quality", 75 ); - aChromaSubsampling = aConfigItem.ReadInt32( "ChromaSubsamplingMode", 0 ); - - if ( pFilterData ) - { - int nArgs = pFilterData->getLength(); - const beans::PropertyValue* pValues = pFilterData->getConstArray(); - while( nArgs-- ) - { - if ( pValues->Name == "StatusIndicator" ) - { - pValues->Value >>= xStatusIndicator; - } - pValues++; - } - } -} - -void* JPEGWriter::GetScanline( long nY ) -{ - void* pScanline = NULL; - - if( pAcc ) - { - if( bNative ) - { - pScanline = pAcc->GetScanline( nY ); - } - else if( pBuffer ) - { - BitmapColor aColor; - long nWidth = pAcc->Width(); - sal_uInt8* pTmp = pBuffer; - - if( pAcc->HasPalette() ) - { - for( long nX = 0L; nX < nWidth; nX++ ) - { - aColor = pAcc->GetPaletteColor( (sal_uInt8) pAcc->GetPixel( nY, nX ) ); - *pTmp++ = aColor.GetRed(); - if ( !bGreys ) - { - *pTmp++ = aColor.GetGreen(); - *pTmp++ = aColor.GetBlue(); - } - } - } - else - { - for( long nX = 0L; nX < nWidth; nX++ ) - { - aColor = pAcc->GetPixel( nY, nX ); - *pTmp++ = aColor.GetRed(); - if ( !bGreys ) - { - *pTmp++ = aColor.GetGreen(); - *pTmp++ = aColor.GetBlue(); - } - } - } - - pScanline = pBuffer; - } - } - - return pScanline; -} - -sal_Bool JPEGWriter::Write( const Graphic& rGraphic ) -{ - sal_Bool bRet = sal_False; - - if ( xStatusIndicator.is() ) - { - OUString aMsg; - xStatusIndicator->start( aMsg, 100 ); - } - - Bitmap aGraphicBmp( rGraphic.GetBitmap() ); - - if ( bGreys ) - { - if ( !aGraphicBmp.Convert( BMP_CONVERSION_8BIT_GREYS ) ) - aGraphicBmp = rGraphic.GetBitmap(); - } - - pAcc = aGraphicBmp.AcquireReadAccess(); - - if ( !bGreys ) // bitmap was not explicitly converted into greyscale, - { // check if source is greyscale only - - sal_Bool bIsGrey = sal_True; - - long nWidth = pAcc->Width(); - for ( long nY = 0; bIsGrey && ( nY < pAcc->Height() ); nY++ ) - { - BitmapColor aColor; - for( long nX = 0L; bIsGrey && ( nX < nWidth ); nX++ ) - { - aColor = pAcc->HasPalette() ? pAcc->GetPaletteColor( (sal_uInt8) pAcc->GetPixel( nY, nX ) ) - : pAcc->GetPixel( nY, nX ); - bIsGrey = ( aColor.GetRed() == aColor.GetGreen() ) && ( aColor.GetRed() == aColor.GetBlue() ); - } - } - if ( bIsGrey ) - bGreys = sal_True; - } - - if( pExpWasGrey ) - *pExpWasGrey = bGreys; - - if( pAcc ) - { - bNative = ( pAcc->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB ); - - if( !bNative ) - pBuffer = new sal_uInt8[ AlignedWidth4Bytes( bGreys ? pAcc->Width() * 8L : pAcc->Width() * 24L ) ]; - - JPEGCallbackStruct aCallbackData; - aCallbackData.xStatusIndicator = xStatusIndicator; - bRet = (sal_Bool) WriteJPEG( this, &rOStm, pAcc->Width(), pAcc->Height(), bGreys, nQuality, aChromaSubsampling, &aCallbackData ); - - delete[] pBuffer; - pBuffer = NULL; - - aGraphicBmp.ReleaseAccess( pAcc ); - pAcc = NULL; - } - if ( xStatusIndicator.is() ) - xStatusIndicator->end(); - - return bRet; -} - -sal_Bool ImportJPEG( SvStream& rStm, Graphic& rGraphic, void* pCallerData, sal_Int32 nImportFlags ) -{ - JPEGReader* pJPEGReader = (JPEGReader*) rGraphic.GetContext(); - ReadState eReadState; - sal_Bool bRet = sal_True; - - if( !pJPEGReader ) - pJPEGReader = new JPEGReader( rStm, pCallerData, ( nImportFlags & GRFILTER_I_FLAGS_SET_LOGSIZE_FOR_JPEG ) != 0 ); - - if( nImportFlags & GRFILTER_I_FLAGS_FOR_PREVIEW ) - pJPEGReader->SetPreviewSize( Size(128,128) ); - else pJPEGReader->DisablePreviewMode(); + } rGraphic.SetContext( NULL ); eReadState = pJPEGReader->Read( rGraphic ); if( eReadState == JPEGREAD_ERROR ) { - bRet = sal_False; + bReturn = false; delete pJPEGReader; } else if( eReadState == JPEGREAD_OK ) + { delete pJPEGReader; + } else + { rGraphic.SetContext( pJPEGReader ); + } - return bRet; + return bReturn; } -// -------------- -// - ExportJPEG - -// -------------- - -sal_Bool ExportJPEG( SvStream& rOStm, const Graphic& rGraphic, - const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData, - bool* pExportWasGrey - ) +sal_Bool ExportJPEG(SvStream& rOutputStream, const Graphic& rGraphic, + const com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue>* pFilterData, + bool* pExportWasGrey) { - JPEGWriter aJPEGWriter( rOStm, pFilterData, pExportWasGrey ); - return aJPEGWriter.Write( rGraphic ); + JPEGWriter aJPEGWriter( rOutputStream, pFilterData, pExportWasGrey ); + bool bReturn = aJPEGWriter.Write( rGraphic ); + return bReturn; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/filter/jpeg/jpeg.h b/vcl/source/filter/jpeg/jpeg.h index cebf81c85f7e..ffb47e76363b 100644 --- a/vcl/source/filter/jpeg/jpeg.h +++ b/vcl/source/filter/jpeg/jpeg.h @@ -41,18 +41,18 @@ struct JPEGCreateBitmapParam long bTopDown; // CreateBitmap method in svtools }; -typedef struct my_error_mgr* my_error_ptr; -typedef unsigned char* HPBYTE; +typedef struct ErrorManagerStruct* ErrorManagerPointer; +typedef unsigned char* HPBYTE; void* JPEGMalloc( size_t size ); void JPEGFree( void *ptr ); long JPEGCallback( void* pCallbackData, long nPercent ); -long WriteJPEG( void* pJPEGWriter, void* pOStm, long nWidth, long nHeight, long bGreyScale, +long WriteJPEG( void* pJPEGWriter, void* pOutputStream, long nWidth, long nHeight, long bGreyScale, long nQualityPercent, long aChromaSubsampling, void* pCallbackData ); void* GetScanline( void* pJPEGWriter, long nY ); -void ReadJPEG( void* pJPEGReader, void* pIStm, long* pLines ); +void ReadJPEG( void* pJPEGReader, void* pInputStream, long* pLines ); void* CreateBitmapFromJPEGReader( void* pJPEGReader, void* pJPEGCreateBitmapParam ); /* TODO: when incompatible changes are possible again diff --git a/vcl/source/filter/jpeg/jpeg.hxx b/vcl/source/filter/jpeg/jpeg.hxx index e2f11b6cfeec..3b9e6ae1133d 100644 --- a/vcl/source/filter/jpeg/jpeg.hxx +++ b/vcl/source/filter/jpeg/jpeg.hxx @@ -26,77 +26,9 @@ #include <com/sun/star/beans/PropertyValue.hpp> #include <com/sun/star/task/XStatusIndicator.hpp> -#ifdef _JPEGPRIVATE +sal_Bool ImportJPEG( SvStream& rInputStream, Graphic& rGraphic, void* pCallerData, sal_Int32 nImportFlags ); -enum ReadState -{ - JPEGREAD_OK, - JPEGREAD_ERROR, - JPEGREAD_NEED_MORE -}; - -class JPEGReader : public GraphicReader -{ - SvStream& rIStm; - Bitmap aBmp; - Bitmap aBmp1; - BitmapWriteAccess* pAcc; - BitmapWriteAccess* pAcc1; - void* pBuffer; - long nLastPos; - long nFormerPos; - long nLastLines; - sal_Bool bSetLogSize; - - Graphic CreateIntermediateGraphic( const Bitmap& rBitmap, long nLines ); - void FillBitmap(); - -public: - - void* CreateBitmap( void* JPEGCreateBitmapParam ); - -public: - JPEGReader( SvStream& rStm, void* pCallData, sal_Bool bSetLogSize ); - virtual ~JPEGReader(); - - - ReadState Read( Graphic& rGraphic ); -}; - -class JPEGWriter -{ - SvStream& rOStm; - Bitmap aBmp; - BitmapReadAccess* pAcc; - sal_uInt8* pBuffer; - sal_Bool bNative; - - sal_Bool bGreys; - sal_Int32 nQuality; - sal_Int32 aChromaSubsampling; - - bool* pExpWasGrey; - - com::sun::star::uno::Reference< com::sun::star::task::XStatusIndicator > xStatusIndicator; - -public: - - void* GetScanline( long nY ); - - JPEGWriter( SvStream& rOStm, - const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData, - bool* pExportWasGrey = NULL ); - - ~JPEGWriter() {}; - - sal_Bool Write( const Graphic& rGraphic ); -}; - -#endif // _JPEGPRIVATE - -sal_Bool ImportJPEG( SvStream& rStream, Graphic& rGraphic, void* pCallerData, sal_Int32 nImportFlags ); - -sal_Bool ExportJPEG(SvStream& rStream, +sal_Bool ExportJPEG(SvStream& rOutputStream, const Graphic& rGraphic, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData, bool* pExportWasGrey = NULL); diff --git a/vcl/source/filter/jpeg/jpegc.c b/vcl/source/filter/jpeg/jpegc.c index 29d4ef01c8f9..4803984180d2 100644 --- a/vcl/source/filter/jpeg/jpegc.c +++ b/vcl/source/filter/jpeg/jpegc.c @@ -19,14 +19,15 @@ #include <stdio.h> #include <stdlib.h> -#include "setjmp.h" +#include <setjmp.h> #include <jpeglib.h> #include <jerror.h> +#include <rtl/alloc.h> +#include <osl/diagnose.h> + #include "jpeg.h" -#include "rtl/alloc.h" -#include "osl/diagnose.h" -struct my_error_mgr +struct ErrorManagerStruct { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; @@ -35,15 +36,15 @@ struct my_error_mgr 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) +METHODDEF( void ) errorExit (j_common_ptr cinfo) { - my_error_ptr myerr = (my_error_ptr) cinfo->err; + ErrorManagerPointer error = (ErrorManagerPointer) cinfo->err; (*cinfo->err->output_message) (cinfo); - longjmp(myerr->setjmp_buffer, 1); + longjmp(error->setjmp_buffer, 1); } -METHODDEF( void ) my_output_message (j_common_ptr cinfo) +METHODDEF( void ) outputMessage (j_common_ptr cinfo) { char buffer[JMSG_LENGTH_MAX]; (*cinfo->err->format_message) (cinfo, buffer); @@ -59,19 +60,19 @@ void SetJpegPreviewSizeHint( int nWidth, int nHeight ) nPreviewHeight = nHeight; } -void ReadJPEG( void* pJPEGReader, void* pIStm, long* pLines ) +void ReadJPEG( void* pJPEGReader, void* pInputStream, long* pLines ) { struct jpeg_decompress_struct cinfo; - struct my_error_mgr jerr; + struct ErrorManagerStruct jerr; struct JPEGCreateBitmapParam aCreateBitmapParam; HPBYTE pDIB; HPBYTE pTmp; long nWidth; long nHeight; long nAlignedWidth; - JSAMPLE * range_limit; - HPBYTE pScanLineBuffer = NULL; - long nScanLineBufferComponents = 0; + JSAMPLE* aRangeLimit; + HPBYTE pScanLineBuffer = NULL; + long nScanLineBufferComponents = 0; if ( setjmp( jerr.setjmp_buffer ) ) { @@ -80,11 +81,11 @@ void ReadJPEG( void* pJPEGReader, void* pIStm, long* pLines ) } cinfo.err = jpeg_std_error( &jerr.pub ); - jerr.pub.error_exit = my_error_exit; - jerr.pub.output_message = my_output_message; + jerr.pub.error_exit = errorExit; + jerr.pub.output_message = outputMessage; jpeg_create_decompress( &cinfo ); - jpeg_svstream_src( &cinfo, pIStm ); + jpeg_svstream_src( &cinfo, pInputStream ); jpeg_read_header( &cinfo, sal_True ); cinfo.scale_num = 1; @@ -102,14 +103,21 @@ void ReadJPEG( void* pJPEGReader, void* pIStm, long* pLines ) /* change scale for preview import */ if( nPreviewWidth || nPreviewHeight ) { - if( nPreviewWidth == 0 ) { - nPreviewWidth = ( cinfo.image_width*nPreviewHeight )/cinfo.image_height; + 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; + } + } + 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 ) @@ -141,7 +149,7 @@ void ReadJPEG( void* pJPEGReader, void* pIStm, long* pLines ) aCreateBitmapParam.bGray = cinfo.output_components == 1; pDIB = CreateBitmapFromJPEGReader( pJPEGReader, &aCreateBitmapParam ); nAlignedWidth = aCreateBitmapParam.nAlignedWidth; - range_limit=cinfo.sample_range_limit; + aRangeLimit = cinfo.sample_range_limit; if ( cinfo.out_color_space == JCS_CMYK ) { @@ -152,7 +160,9 @@ void ReadJPEG( void* pJPEGReader, void* pIStm, long* pLines ) if( pDIB ) { if( aCreateBitmapParam.bTopDown ) + { pTmp = pDIB; + } else { pTmp = pDIB + ( nHeight - 1 ) * nAlignedWidth; @@ -161,24 +171,28 @@ void ReadJPEG( void* pJPEGReader, void* pIStm, long* pLines ) 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_ ) ]; + 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 color_C = 255 - pScanLineBuffer[i+0]; + int color_M = 255 - pScanLineBuffer[i+1]; + int color_Y = 255 - pScanLineBuffer[i+2]; + int color_K = 255 - pScanLineBuffer[i+3]; + pTmp[j+0] = aRangeLimit[ 255L - ( color_C + color_K ) ]; + pTmp[j+1] = aRangeLimit[ 255L - ( color_M + color_K ) ]; + pTmp[j+2] = aRangeLimit[ 255L - ( color_Y + color_K ) ]; + } } - } else { - jpeg_read_scanlines( &cinfo, (JSAMPARRAY) &pTmp, 1 ); + else + { + jpeg_read_scanlines( &cinfo, (JSAMPARRAY) &pTmp, 1 ); } + /* PENDING ??? */ if ( cinfo.err->msg_code == 113 ) break; @@ -188,25 +202,29 @@ void ReadJPEG( void* pJPEGReader, void* pIStm, long* pLines ) } if ( pDIB ) + { jpeg_finish_decompress( &cinfo ); + } else + { jpeg_abort_decompress( &cinfo ); + } - if (pScanLineBuffer!=NULL) + if (pScanLineBuffer != NULL) { rtl_freeMemory( pScanLineBuffer ); - pScanLineBuffer=NULL; + pScanLineBuffer = NULL; } jpeg_destroy_decompress( &cinfo ); } -long WriteJPEG( void* pJPEGWriter, void* pOStm, +long WriteJPEG( void* pJPEGWriter, void* pOutputStream, long nWidth, long nHeight, long bGreys, long nQualityPercent, long aChromaSubsampling, void* pCallbackData ) { struct jpeg_compress_struct cinfo; - struct my_error_mgr jerr; + struct ErrorManagerStruct jerr; void* pScanline; long nY; @@ -217,11 +235,11 @@ long WriteJPEG( void* pJPEGWriter, void* pOStm, } cinfo.err = jpeg_std_error( &jerr.pub ); - jerr.pub.error_exit = my_error_exit; - jerr.pub.output_message = my_output_message; + jerr.pub.error_exit = errorExit; + jerr.pub.output_message = outputMessage; jpeg_create_compress( &cinfo ); - jpeg_svstream_dest( &cinfo, pOStm ); + jpeg_svstream_dest( &cinfo, pOutputStream ); cinfo.image_width = (JDIMENSION) nWidth; cinfo.image_height = (JDIMENSION) nHeight; @@ -265,7 +283,9 @@ long WriteJPEG( void* pJPEGWriter, void* pOStm, pScanline = GetScanline( pJPEGWriter, nY ); if( pScanline ) + { jpeg_write_scanlines( &cinfo, (JSAMPARRAY) &pScanline, 1 ); + } if( JPEGCallback( pCallbackData, nY * 100L / nHeight ) ) { |