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

Generated by: LCOV version 1.10