LCOV - code coverage report
Current view: top level - vcl/source/filter/jpeg - JpegReader.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 138 207 66.7 %
Date: 2015-06-13 12:38:46 Functions: 14 15 93.3 %
Legend: Lines: hit not hit

          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 "jpeg.h"
      23             : #include <jpeglib.h>
      24             : #include <jerror.h>
      25             : 
      26             : #include "JpegReader.hxx"
      27             : #include <vcl/bmpacc.hxx>
      28             : #include <vcl/FilterConfigItem.hxx>
      29             : #include <vcl/graphicfilter.hxx>
      30             : #include <tools/fract.hxx>
      31             : #include <boost/scoped_array.hpp>
      32             : 
      33             : #define JPEG_MIN_READ 512
      34             : #define BUFFER_SIZE  4096
      35             : namespace {
      36             :     // Arbitrary maximal size (512M) of a bitmap after it has been decoded.
      37             :     // It is used to prevent excessive swapping due to large buffers in
      38             :     // virtual memory.
      39             :     // May have to be tuned if it turns out to be too large or too small.
      40             :     static const sal_uInt64 MAX_BITMAP_BYTE_SIZE = sal_uInt64(512 * 1024 * 1024);
      41             : }
      42             : 
      43             : /* Expanded data source object for stdio input */
      44             : 
      45             : struct SourceManagerStruct {
      46             :     jpeg_source_mgr pub;                /* public fields */
      47             :     SvStream*   stream;                 /* source stream */
      48             :     JOCTET*     buffer;                 /* start of buffer */
      49             :     boolean     start_of_file;          /* have we gotten any data yet? */
      50             : };
      51             : 
      52             : /*
      53             :  * Initialize source --- called by jpeg_read_header
      54             :  * before any data is actually read.
      55             :  */
      56         439 : extern "C" void init_source (j_decompress_ptr cinfo)
      57             : {
      58         439 :     SourceManagerStruct * source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
      59             : 
      60             :     /* We reset the empty-input-file flag for each image,
      61             :      * but we don't clear the input buffer.
      62             :      * This is correct behavior for reading a series of images from one source.
      63             :      */
      64         439 :     source->start_of_file = TRUE;
      65         439 : }
      66             : 
      67        8346 : long StreamRead( SvStream* pStream, void* pBuffer, long nBufferSize )
      68             : {
      69        8346 :     long nRead = 0;
      70             : 
      71        8346 :     if( pStream->GetError() != ERRCODE_IO_PENDING )
      72             :     {
      73        8346 :         long nInitialPosition = pStream->Tell();
      74             : 
      75        8346 :         nRead = (long) pStream->Read( pBuffer, nBufferSize );
      76             : 
      77        8346 :         if( pStream->GetError() == ERRCODE_IO_PENDING )
      78             :         {
      79             :             // in order to search from the old position
      80             :             // we temporarily reset the error
      81           0 :             pStream->ResetError();
      82           0 :             pStream->Seek( nInitialPosition );
      83           0 :             pStream->SetError( ERRCODE_IO_PENDING );
      84             :         }
      85             :     }
      86             : 
      87        8346 :     return nRead;
      88             : }
      89             : 
      90        8346 : extern "C" boolean fill_input_buffer (j_decompress_ptr cinfo)
      91             : {
      92        8346 :     SourceManagerStruct * source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
      93             :     size_t nbytes;
      94             : 
      95        8346 :     nbytes = StreamRead(source->stream, source->buffer, BUFFER_SIZE);
      96             : 
      97        8346 :     if (!nbytes)
      98             :     {
      99           0 :         if (source->start_of_file)     /* Treat empty input file as fatal error */
     100             :         {
     101           0 :             ERREXIT(cinfo, JERR_INPUT_EMPTY);
     102             :         }
     103           0 :         WARNMS(cinfo, JWRN_JPEG_EOF);
     104             :         /* Insert a fake EOI marker */
     105           0 :         source->buffer[0] = (JOCTET) 0xFF;
     106           0 :         source->buffer[1] = (JOCTET) JPEG_EOI;
     107           0 :         nbytes = 2;
     108             :     }
     109             : 
     110        8346 :     source->pub.next_input_byte = source->buffer;
     111        8346 :     source->pub.bytes_in_buffer = nbytes;
     112        8346 :     source->start_of_file = FALSE;
     113             : 
     114        8346 :     return TRUE;
     115             : }
     116             : 
     117         469 : extern "C" void skip_input_data (j_decompress_ptr cinfo, long numberOfBytes)
     118             : {
     119         469 :     SourceManagerStruct * source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
     120             : 
     121             :     /* Just a dumb implementation for now.  Could use fseek() except
     122             :      * it doesn't work on pipes.  Not clear that being smart is worth
     123             :      * any trouble anyway --- large skips are infrequent.
     124             :      */
     125         469 :     if (numberOfBytes > 0)
     126             :     {
     127        1070 :         while (numberOfBytes > (long) source->pub.bytes_in_buffer)
     128             :         {
     129         132 :             numberOfBytes -= (long) source->pub.bytes_in_buffer;
     130         132 :             (void) fill_input_buffer(cinfo);
     131             : 
     132             :             /* note we assume that fill_input_buffer will never return false,
     133             :              * so suspension need not be handled.
     134             :              */
     135             :         }
     136         469 :         source->pub.next_input_byte += (size_t) numberOfBytes;
     137         469 :         source->pub.bytes_in_buffer -= (size_t) numberOfBytes;
     138             :     }
     139         469 : }
     140             : 
     141         433 : extern "C" void term_source (j_decompress_ptr)
     142             : {
     143             :     /* no work necessary here */
     144         433 : }
     145             : 
     146         439 : void jpeg_svstream_src (j_decompress_ptr cinfo, void* input)
     147             : {
     148             :     SourceManagerStruct * source;
     149         439 :     SvStream* stream = static_cast<SvStream*>(input);
     150             : 
     151             :     /* The source object and input buffer are made permanent so that a series
     152             :      * of JPEG images can be read from the same file by calling jpeg_stdio_src
     153             :      * only before the first one.  (If we discarded the buffer at the end of
     154             :      * one image, we'd likely lose the start of the next one.)
     155             :      * This makes it unsafe to use this manager and a different source
     156             :      * manager serially with the same JPEG object.  Caveat programmer.
     157             :      */
     158             : 
     159         439 :     if (cinfo->src == NULL)
     160             :     { /* first time for this JPEG object? */
     161             :         cinfo->src = static_cast<jpeg_source_mgr *>(
     162         439 :             (*cinfo->mem->alloc_small) (reinterpret_cast<j_common_ptr>(cinfo), JPOOL_PERMANENT, sizeof(SourceManagerStruct)));
     163         439 :         source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
     164             :         source->buffer = static_cast<JOCTET *>(
     165         439 :             (*cinfo->mem->alloc_small) (reinterpret_cast<j_common_ptr>(cinfo), JPOOL_PERMANENT, BUFFER_SIZE * sizeof(JOCTET)));
     166             :     }
     167             : 
     168         439 :     source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
     169         439 :     source->pub.init_source = init_source;
     170         439 :     source->pub.fill_input_buffer = fill_input_buffer;
     171         439 :     source->pub.skip_input_data = skip_input_data;
     172         439 :     source->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
     173         439 :     source->pub.term_source = term_source;
     174         439 :     source->stream = stream;
     175         439 :     source->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
     176         439 :     source->pub.next_input_byte = NULL; /* until buffer loaded */
     177         439 : }
     178             : 
     179         439 : JPEGReader::JPEGReader( SvStream& rStream, void* /*pCallData*/, bool bSetLogSize ) :
     180             :     mrStream         ( rStream ),
     181             :     mpAcc            ( NULL ),
     182             :     mpAcc1           ( NULL ),
     183             :     mpBuffer         ( NULL ),
     184         439 :     mnLastPos        ( rStream.Tell() ),
     185             :     mnLastLines      ( 0 ),
     186         878 :     mbSetLogSize     ( bSetLogSize )
     187             : {
     188         439 :     maUpperName = "SVIJPEG";
     189         439 :     mnFormerPos = mnLastPos;
     190         439 : }
     191             : 
     192        1317 : JPEGReader::~JPEGReader()
     193             : {
     194         439 :     if( mpBuffer )
     195           0 :         delete[] mpBuffer;
     196             : 
     197         439 :     if( mpAcc )
     198           0 :         Bitmap::ReleaseAccess( mpAcc );
     199             : 
     200         439 :     if( mpAcc1 )
     201           0 :         Bitmap::ReleaseAccess( mpAcc1 );
     202         878 : }
     203             : 
     204         434 : unsigned char * JPEGReader::CreateBitmap( JPEGCreateBitmapParam * pParam )
     205             : {
     206         434 :     if (pParam->nWidth > SAL_MAX_INT32 / 8 || pParam->nHeight > SAL_MAX_INT32 / 8)
     207           0 :         return NULL; // avoid overflows later
     208             : 
     209         434 :     if (pParam->nWidth == 0 || pParam->nHeight == 0)
     210           0 :         return NULL;
     211             : 
     212         434 :     Size        aSize( pParam->nWidth, pParam->nHeight );
     213         434 :     bool        bGray = pParam->bGray != 0;
     214             : 
     215         434 :     unsigned char * pBmpBuf = NULL;
     216             : 
     217         434 :     if( mpAcc )
     218             :     {
     219           0 :         Bitmap::ReleaseAccess( mpAcc );
     220           0 :         maBmp = Bitmap();
     221           0 :         mpAcc = NULL;
     222             :     }
     223             : 
     224         434 :     sal_uInt64 nSize = aSize.Width();
     225         434 :     nSize *= aSize.Height();
     226         434 :     if (nSize > SAL_MAX_INT32 / (bGray?1:3))
     227           0 :         return NULL;
     228             : 
     229             :     // Check if the bitmap is untypically large.
     230         434 :     if (nSize*(bGray?1:3) > MAX_BITMAP_BYTE_SIZE)
     231             :     {
     232             :         // Do not try to acquire resources for the large bitmap or to
     233             :         // read the bitmap into memory.
     234           0 :         return NULL;
     235             :     }
     236             : 
     237         434 :     if( bGray )
     238             :     {
     239           1 :         BitmapPalette aGrayPal( 256 );
     240             : 
     241         257 :         for( sal_uInt16 n = 0; n < 256; n++ )
     242             :         {
     243         256 :             const sal_uInt8 cGray = (sal_uInt8) n;
     244         256 :             aGrayPal[ n ] = BitmapColor( cGray, cGray, cGray );
     245             :         }
     246             : 
     247           1 :         maBmp = Bitmap( aSize, 8, &aGrayPal );
     248             :     }
     249             :     else
     250             :     {
     251         433 :         maBmp = Bitmap( aSize, 24 );
     252             :     }
     253             : 
     254         434 :     if ( mbSetLogSize )
     255             :     {
     256         434 :         unsigned long nUnit = pParam->density_unit;
     257             : 
     258         434 :         if( ( ( 1 == nUnit ) || ( 2 == nUnit ) ) && pParam->X_density && pParam->Y_density )
     259             :         {
     260         161 :             Point       aEmptyPoint;
     261         161 :             Fraction    aFractX( 1, pParam->X_density );
     262         322 :             Fraction    aFractY( 1, pParam->Y_density );
     263         322 :             MapMode     aMapMode( nUnit == 1 ? MAP_INCH : MAP_CM, aEmptyPoint, aFractX, aFractY );
     264         161 :             Size        aPrefSize = OutputDevice::LogicToLogic( aSize, aMapMode, MAP_100TH_MM );
     265             : 
     266         161 :             maBmp.SetPrefSize( aPrefSize );
     267         322 :             maBmp.SetPrefMapMode( MapMode( MAP_100TH_MM ) );
     268             :         }
     269             :     }
     270             : 
     271         434 :     mpAcc = maBmp.AcquireWriteAccess();
     272             : 
     273         434 :     if( mpAcc )
     274             :     {
     275         434 :         const sal_uLong nFormat = mpAcc->GetScanlineFormat();
     276             : 
     277         434 :         if(
     278         434 :             ( bGray && ( BMP_FORMAT_8BIT_PAL == nFormat ) ) ||
     279         866 :             ( !bGray && ( BMP_FORMAT_24BIT_TC_RGB == nFormat ) )
     280             :           )
     281             :         {
     282           1 :             pBmpBuf = mpAcc->GetBuffer();
     283           1 :             pParam->nAlignedWidth = mpAcc->GetScanlineSize();
     284           1 :             pParam->bTopDown = mpAcc->IsTopDown();
     285             :         }
     286             :         else
     287             :         {
     288         433 :             pParam->nAlignedWidth = AlignedWidth4Bytes( aSize.Width() * ( bGray ? 8 : 24 ) );
     289         433 :             pParam->bTopDown = true;
     290         433 :             pBmpBuf = mpBuffer = new unsigned char[pParam->nAlignedWidth * aSize.Height()];
     291             :         }
     292             :     }
     293             : 
     294             :     // clean up, if no Bitmap buffer can be provided.
     295         434 :     if ( !pBmpBuf )
     296             :     {
     297           0 :         Bitmap::ReleaseAccess( mpAcc );
     298           0 :         maBmp = Bitmap();
     299           0 :         mpAcc = NULL;
     300             :     }
     301             : 
     302         434 :     return pBmpBuf;
     303             : }
     304             : 
     305         433 : void JPEGReader::FillBitmap()
     306             : {
     307         433 :     if( mpBuffer && mpAcc )
     308             :     {
     309             :         unsigned char * pTmp;
     310         433 :         BitmapColor aColor;
     311             :         long        nAlignedWidth;
     312         433 :         long        nWidth = mpAcc->Width();
     313         433 :         long        nHeight = mpAcc->Height();
     314             : 
     315         433 :         if( mpAcc->GetBitCount() == 8 )
     316             :         {
     317           0 :             boost::scoped_array<BitmapColor> pCols(new BitmapColor[ 256 ]);
     318             : 
     319           0 :             for( sal_uInt16 n = 0; n < 256; n++ )
     320             :             {
     321           0 :                 const sal_uInt8 cGray = (sal_uInt8) n;
     322           0 :                 pCols[ n ] = mpAcc->GetBestMatchingColor( BitmapColor( cGray, cGray, cGray ) );
     323             :             }
     324             : 
     325           0 :             nAlignedWidth = AlignedWidth4Bytes( mpAcc->Width() * 8L );
     326             : 
     327           0 :             for( long nY = 0L; nY < nHeight; nY++ )
     328             :             {
     329           0 :                 pTmp = mpBuffer + nY * nAlignedWidth;
     330             : 
     331           0 :                 for( long nX = 0L; nX < nWidth; nX++ )
     332             :                 {
     333           0 :                     mpAcc->SetPixel( nY, nX, pCols[ *pTmp++ ] );
     334             :                 }
     335           0 :             }
     336             :         }
     337             :         else
     338             :         {
     339         433 :             nAlignedWidth = AlignedWidth4Bytes( mpAcc->Width() * 24L );
     340             : 
     341      179003 :             for( long nY = 0L; nY < nHeight; nY++ )
     342             :             {
     343             :                 // #i122985# Added fast-lane implementations using CopyScanline with direct supported mem formats
     344             :                 static bool bCheckOwnReader(true);
     345             : 
     346      178570 :                 if(bCheckOwnReader)
     347             :                 {
     348             :                     // #i122985# Trying to copy the RGB data from jpeg import to make things faster. Unfortunately
     349             :                     // it has no GBR format, so RGB three-byte groups need to be 'flipped' to GBR first,
     350             :                     // then CopyScanline can use a memcpy to do the data transport. CopyScanline can also
     351             :                     // do the needed conversion from BMP_FORMAT_24BIT_TC_RGB (and it works well), but this
     352             :                     // is not faster that the old loop below using SetPixel.
     353      178570 :                     sal_uInt8* aSource(mpBuffer + nY * nAlignedWidth);
     354      178570 :                     sal_uInt8* aEnd(aSource + (nWidth * 3));
     355             : 
     356   125280614 :                     for(sal_uInt8* aTmp(aSource); aTmp < aEnd; aTmp += 3)
     357             :                     {
     358   125102044 :                         ::std::swap(*aTmp, *(aTmp + 2));
     359             :                     }
     360             : 
     361      178570 :                     mpAcc->CopyScanline(nY, aSource, BMP_FORMAT_24BIT_TC_BGR, nWidth * 3);
     362             :                 }
     363             :                 else
     364             :                 {
     365             :                     // old version: WritePixel
     366           0 :                     pTmp = mpBuffer + nY * nAlignedWidth;
     367             : 
     368           0 :                     for( long nX = 0L; nX < nWidth; nX++ )
     369             :                     {
     370           0 :                         aColor.SetRed( *pTmp++ );
     371           0 :                         aColor.SetGreen( *pTmp++ );
     372           0 :                         aColor.SetBlue( *pTmp++ );
     373           0 :                         mpAcc->SetPixel( nY, nX, aColor );
     374             :                     }
     375             :                 }
     376             :             }
     377         433 :         }
     378             :     }
     379         433 : }
     380             : 
     381           0 : Graphic JPEGReader::CreateIntermediateGraphic( const Bitmap& rBitmap, long nLines )
     382             : {
     383           0 :     Graphic     aGraphic;
     384           0 :     const Size  aSizePixel( rBitmap.GetSizePixel() );
     385             : 
     386           0 :     if( !mnLastLines )
     387             :     {
     388           0 :         if( mpAcc1 )
     389             :         {
     390           0 :             Bitmap::ReleaseAccess( mpAcc1 );
     391             :         }
     392             : 
     393           0 :         maBmp1 = Bitmap( rBitmap.GetSizePixel(), 1 );
     394           0 :         maBmp1.Erase( Color( COL_WHITE ) );
     395           0 :         mpAcc1 = maBmp1.AcquireWriteAccess();
     396             :     }
     397             : 
     398           0 :     if( nLines && ( nLines < aSizePixel.Height() ) )
     399             :     {
     400           0 :         if( mpAcc1 )
     401             :         {
     402           0 :             const long nNewLines = nLines - mnLastLines;
     403             : 
     404           0 :             if( nNewLines )
     405             :             {
     406           0 :                 mpAcc1->SetFillColor( Color( COL_BLACK ) );
     407           0 :                 mpAcc1->FillRect( Rectangle( Point( 0, mnLastLines ), Size( mpAcc1->Width(), nNewLines ) ) );
     408             :             }
     409             : 
     410           0 :             Bitmap::ReleaseAccess( mpAcc1 );
     411           0 :             aGraphic = BitmapEx( rBitmap, maBmp1 );
     412           0 :             mpAcc1 = maBmp1.AcquireWriteAccess();
     413             :         }
     414             :         else
     415             :         {
     416           0 :             aGraphic = rBitmap;
     417             :         }
     418             :     }
     419             :     else
     420           0 :         aGraphic = rBitmap;
     421             : 
     422           0 :     mnLastLines = nLines;
     423             : 
     424           0 :     return aGraphic;
     425             : }
     426             : 
     427         439 : ReadState JPEGReader::Read( Graphic& rGraphic )
     428             : {
     429             :     long        nEndPosition;
     430             :     long        nLines;
     431             :     ReadState   eReadState;
     432         439 :     bool        bRet = false;
     433             :     sal_uInt8   cDummy;
     434             : 
     435             :     // TODO: is it possible to get rid of this seek to the end?
     436             :     // check if the stream's end is already available
     437         439 :     mrStream.Seek( STREAM_SEEK_TO_END );
     438         439 :     mrStream.ReadUChar( cDummy );
     439         439 :     nEndPosition = mrStream.Tell();
     440             : 
     441             :     // else check if at least JPEG_MIN_READ bytes can be read
     442         439 :     if( mrStream.GetError() == ERRCODE_IO_PENDING )
     443             :     {
     444           0 :         mrStream.ResetError();
     445           0 :         if( ( nEndPosition  - mnFormerPos ) < JPEG_MIN_READ )
     446             :         {
     447           0 :             mrStream.Seek( mnLastPos );
     448           0 :             return JPEGREAD_NEED_MORE;
     449             :         }
     450             :     }
     451             : 
     452             :     // seek back to the original position
     453         439 :     mrStream.Seek( mnLastPos );
     454             : 
     455             :     // read the (partial) image
     456         439 :     ReadJPEG( this, &mrStream, &nLines, GetPreviewSize() );
     457             : 
     458         439 :     if( mpAcc )
     459             :     {
     460         434 :         if( mpBuffer )
     461             :         {
     462         433 :             FillBitmap();
     463         433 :             delete[] mpBuffer;
     464         433 :             mpBuffer = NULL;
     465             :         }
     466             : 
     467         434 :         Bitmap::ReleaseAccess( mpAcc );
     468         434 :         mpAcc = NULL;
     469             : 
     470         434 :         if( mrStream.GetError() == ERRCODE_IO_PENDING )
     471             :         {
     472           0 :             rGraphic = CreateIntermediateGraphic( maBmp, nLines );
     473             :         }
     474             :         else
     475             :         {
     476         434 :             rGraphic = maBmp;
     477             :         }
     478             : 
     479         434 :         bRet = true;
     480             :     }
     481           5 :     else if( mrStream.GetError() == ERRCODE_IO_PENDING )
     482             :     {
     483           0 :         bRet = true;
     484             :     }
     485             : 
     486             :     // Set status ( Pending has priority )
     487         439 :     if( mrStream.GetError() == ERRCODE_IO_PENDING )
     488             :     {
     489           0 :         eReadState = JPEGREAD_NEED_MORE;
     490           0 :         mrStream.ResetError();
     491           0 :         mnFormerPos = mrStream.Tell();
     492             :     }
     493             :     else
     494             :     {
     495         439 :         eReadState = bRet ? JPEGREAD_OK : JPEGREAD_ERROR;
     496             :     }
     497             : 
     498         439 :     return eReadState;
     499         801 : }
     500             : 
     501             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11