[Libreoffice-commits] core.git: vcl/source

Tomaž Vajngerl tomaz.vajngerl at collabora.com
Tue Oct 4 12:19:07 UTC 2016


 vcl/source/filter/jpeg/JpegReader.cxx |  214 +++++-----------------------------
 vcl/source/filter/jpeg/JpegReader.hxx |   23 +--
 vcl/source/filter/jpeg/jpegc.cxx      |  149 ++++++++++++++---------
 3 files changed, 139 insertions(+), 247 deletions(-)

New commits:
commit 8a04fac29da8ae902bd5f0aac559129013274304
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.com>
Date:   Mon Oct 3 18:39:04 2016 +0200

    vcl: read image from JPEG one scanline at a time
    
    When reading an image from JPEG file we allocate a large buffer
    to hold the whole raw image before we read it into SalBitmap.
    If the image is large this step will spike the memory usage.
    
    This changes reading so that we allocate only the buffer for
    a scanline and read one scanline at a time.
    
    Change-Id: I405f4a14601e271c0293a2b75b4c9ec6c458d8a9
    Reviewed-on: https://gerrit.libreoffice.org/29504
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>

diff --git a/vcl/source/filter/jpeg/JpegReader.cxx b/vcl/source/filter/jpeg/JpegReader.cxx
index 8959e4e..cd00518 100644
--- a/vcl/source/filter/jpeg/JpegReader.cxx
+++ b/vcl/source/filter/jpeg/JpegReader.cxx
@@ -178,9 +178,6 @@ void jpeg_svstream_src (j_decompress_ptr cinfo, void* input)
 
 JPEGReader::JPEGReader( SvStream& rStream, void* /*pCallData*/, bool bSetLogSize ) :
     mrStream         ( rStream ),
-    mpAcc            ( nullptr ),
-    mpAcc1           ( nullptr ),
-    mpBuffer         ( nullptr ),
     mnLastPos        ( rStream.Tell() ),
     mnLastLines      ( 0 ),
     mbSetLogSize     ( bSetLogSize )
@@ -191,46 +188,32 @@ JPEGReader::JPEGReader( SvStream& rStream, void* /*pCallData*/, bool bSetLogSize
 
 JPEGReader::~JPEGReader()
 {
-    delete[] mpBuffer;
-
-    if( mpAcc )
-        Bitmap::ReleaseAccess( mpAcc );
-
-    if( mpAcc1 )
-        Bitmap::ReleaseAccess( mpAcc1 );
 }
 
-unsigned char * JPEGReader::CreateBitmap(JPEGCreateBitmapParam& rParam)
+bool JPEGReader::CreateBitmap(JPEGCreateBitmapParam& rParam)
 {
     if (rParam.nWidth > SAL_MAX_INT32 / 8 || rParam.nHeight > SAL_MAX_INT32 / 8)
-        return nullptr; // avoid overflows later
+        return false; // avoid overflows later
 
     if (rParam.nWidth == 0 || rParam.nHeight == 0)
-        return nullptr;
+        return false;
 
-    Size        aSize(rParam.nWidth, rParam.nHeight );
-    bool        bGray = rParam.bGray != 0;
+    Size aSize(rParam.nWidth, rParam.nHeight);
+    bool bGray = rParam.bGray;
 
-    unsigned char * pBmpBuf = nullptr;
+    maBitmap = Bitmap();
 
-    if( mpAcc )
-    {
-        Bitmap::ReleaseAccess( mpAcc );
-        maBmp = Bitmap();
-        mpAcc = nullptr;
-    }
+    sal_uInt64 nSize = aSize.Width() * aSize.Height();
 
-    sal_uInt64 nSize = aSize.Width();
-    nSize *= aSize.Height();
     if (nSize > SAL_MAX_INT32 / (bGray?1:3))
-        return nullptr;
+        return false;
 
     // Check if the bitmap is atypically large.
     if (nSize*(bGray?1:3) > MAX_BITMAP_BYTE_SIZE)
     {
         // Do not try to acquire resources for the large bitmap or to
         // read the bitmap into memory.
-        return nullptr;
+        return false;
     }
 
     if( bGray )
@@ -243,18 +226,18 @@ unsigned char * JPEGReader::CreateBitmap(JPEGCreateBitmapParam& rParam)
             aGrayPal[ n ] = BitmapColor( cGray, cGray, cGray );
         }
 
-        maBmp = Bitmap( aSize, 8, &aGrayPal );
+        maBitmap = Bitmap(aSize, 8, &aGrayPal);
     }
     else
     {
-        maBmp = Bitmap( aSize, 24 );
+        maBitmap = Bitmap(aSize, 24);
     }
 
-    if ( mbSetLogSize )
+    if (mbSetLogSize)
     {
         unsigned long nUnit = rParam.density_unit;
 
-        if( ( ( 1 == nUnit ) || ( 2 == nUnit ) ) && rParam.X_density && rParam.Y_density )
+        if (((1 == nUnit) || (2 == nUnit)) && rParam.X_density && rParam.Y_density )
         {
             Point       aEmptyPoint;
             Fraction    aFractX( 1, rParam.X_density );
@@ -262,161 +245,48 @@ unsigned char * JPEGReader::CreateBitmap(JPEGCreateBitmapParam& rParam)
             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 ) );
+            maBitmap.SetPrefSize(aPrefSize);
+            maBitmap.SetPrefMapMode(MapMode(MAP_100TH_MM));
         }
     }
 
-    mpAcc = maBmp.AcquireWriteAccess();
-
-    if( mpAcc )
-    {
-        const ScanlineFormat nFormat = mpAcc->GetScanlineFormat();
-
-        if(
-            ( bGray && ( ScanlineFormat::N8BitPal == nFormat ) ) ||
-            ( !bGray && ( ScanlineFormat::N24BitTcRgb == nFormat ) )
-          )
-        {
-            pBmpBuf = mpAcc->GetBuffer();
-            rParam.nAlignedWidth = mpAcc->GetScanlineSize();
-            rParam.bTopDown = mpAcc->IsTopDown();
-        }
-        else
-        {
-            rParam.nAlignedWidth = AlignedWidth4Bytes( aSize.Width() * ( bGray ? 8 : 24 ) );
-            rParam.bTopDown = true;
-            pBmpBuf = mpBuffer = new unsigned char[rParam.nAlignedWidth * aSize.Height()];
-        }
-    }
-
-    // clean up, if no Bitmap buffer can be provided.
-    if ( !pBmpBuf )
-    {
-        Bitmap::ReleaseAccess( mpAcc );
-        maBmp = Bitmap();
-        mpAcc = nullptr;
-    }
-
-    return pBmpBuf;
-}
-
-void JPEGReader::FillBitmap()
-{
-    if( mpBuffer && mpAcc )
-    {
-        unsigned char * pTmp;
-        BitmapColor aColor;
-        long        nAlignedWidth;
-        long        nWidth = mpAcc->Width();
-        long        nHeight = mpAcc->Height();
-
-        if( mpAcc->GetBitCount() == 8 )
-        {
-            std::unique_ptr<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 = mpBuffer + nY * nAlignedWidth;
-
-                for( long nX = 0L; nX < nWidth; nX++ )
-                {
-                    mpAcc->SetPixel( nY, nX, pCols[ *pTmp++ ] );
-                }
-            }
-        }
-        else
-        {
-            nAlignedWidth = AlignedWidth4Bytes( mpAcc->Width() * 24L );
-
-            for( long nY = 0L; nY < nHeight; nY++ )
-            {
-                // #i122985# Added fast-lane implementations using CopyScanline with direct supported mem formats
-                static bool bCheckOwnReader(true);
-
-                if(bCheckOwnReader)
-                {
-                    // #i122985# Trying to copy the RGB data from jpeg import to make things faster. Unfortunately
-                    // it has no GBR format, so RGB three-byte groups need to be 'flipped' to GBR first,
-                    // then CopyScanline can use a memcpy to do the data transport. CopyScanline can also
-                    // do the needed conversion from ScanlineFormat::N24BitTcRgb (and it works well), but this
-                    // is not faster that the old loop below using SetPixel.
-                    sal_uInt8* aSource(mpBuffer + nY * nAlignedWidth);
-                    sal_uInt8* aEnd(aSource + (nWidth * 3));
-
-                    for(sal_uInt8* aTmp(aSource); aTmp < aEnd; aTmp += 3)
-                    {
-                        ::std::swap(*aTmp, *(aTmp + 2));
-                    }
-
-                    mpAcc->CopyScanline(nY, aSource, ScanlineFormat::N24BitTcBgr, nWidth * 3);
-                }
-                else
-                {
-                    // old version: WritePixel
-                    pTmp = mpBuffer + nY * nAlignedWidth;
-
-                    for( long nX = 0L; nX < nWidth; nX++ )
-                    {
-                        aColor.SetRed( *pTmp++ );
-                        aColor.SetGreen( *pTmp++ );
-                        aColor.SetBlue( *pTmp++ );
-                        mpAcc->SetPixel( nY, nX, aColor );
-                    }
-                }
-            }
-        }
-    }
+    return true;
 }
 
-Graphic JPEGReader::CreateIntermediateGraphic( const Bitmap& rBitmap, long nLines )
+Graphic JPEGReader::CreateIntermediateGraphic(long nLines)
 {
-    Graphic     aGraphic;
-    const Size  aSizePixel( rBitmap.GetSizePixel() );
+    Graphic aGraphic;
+    const Size aSizePixel(maBitmap.GetSizePixel());
 
-    if( !mnLastLines )
+    if (!mnLastLines)
     {
-        if( mpAcc1 )
-        {
-            Bitmap::ReleaseAccess( mpAcc1 );
-        }
-
-        maBmp1 = Bitmap( rBitmap.GetSizePixel(), 1 );
-        maBmp1.Erase( Color( COL_WHITE ) );
-        mpAcc1 = maBmp1.AcquireWriteAccess();
+        maIncompleteAlpha = Bitmap(aSizePixel, 1);
+        maIncompleteAlpha.Erase(Color(COL_WHITE));
     }
 
-    if( nLines && ( nLines < aSizePixel.Height() ) )
+    if (nLines && (nLines < aSizePixel.Height()))
     {
-        if( mpAcc1 )
-        {
-            const long nNewLines = nLines - mnLastLines;
+        const long nNewLines = nLines - mnLastLines;
 
-            if( nNewLines )
+        if (nNewLines > 0)
+        {
             {
-                mpAcc1->SetFillColor( Color( COL_BLACK ) );
-                mpAcc1->FillRect( Rectangle( Point( 0, mnLastLines ), Size( mpAcc1->Width(), nNewLines ) ) );
+                Bitmap::ScopedWriteAccess pAccess(maIncompleteAlpha);
+                pAccess->SetFillColor(Color(COL_BLACK));
+                pAccess->FillRect(Rectangle(Point(0, mnLastLines), Size(pAccess->Width(), nNewLines)));
             }
 
-            Bitmap::ReleaseAccess( mpAcc1 );
-            aGraphic = BitmapEx( rBitmap, maBmp1 );
-            mpAcc1 = maBmp1.AcquireWriteAccess();
+            aGraphic = BitmapEx(maBitmap, maIncompleteAlpha);
         }
         else
         {
-            aGraphic = rBitmap;
+            aGraphic = maBitmap;
         }
     }
     else
-        aGraphic = rBitmap;
+    {
+        aGraphic = maBitmap;
+    }
 
     mnLastLines = nLines;
 
@@ -454,25 +324,15 @@ ReadState JPEGReader::Read( Graphic& rGraphic )
     // read the (partial) image
     ReadJPEG( this, &mrStream, &nLines, GetPreviewSize() );
 
-    if( mpAcc )
+    if (!maBitmap.IsEmpty())
     {
-        if( mpBuffer )
-        {
-            FillBitmap();
-            delete[] mpBuffer;
-            mpBuffer = nullptr;
-        }
-
-        Bitmap::ReleaseAccess( mpAcc );
-        mpAcc = nullptr;
-
         if( mrStream.GetError() == ERRCODE_IO_PENDING )
         {
-            rGraphic = CreateIntermediateGraphic( maBmp, nLines );
+            rGraphic = CreateIntermediateGraphic(nLines);
         }
         else
         {
-            rGraphic = maBmp;
+            rGraphic = maBitmap;
         }
 
         bRet = true;
diff --git a/vcl/source/filter/jpeg/JpegReader.hxx b/vcl/source/filter/jpeg/JpegReader.hxx
index 71f8b9a..c3b52fd 100644
--- a/vcl/source/filter/jpeg/JpegReader.hxx
+++ b/vcl/source/filter/jpeg/JpegReader.hxx
@@ -39,34 +39,33 @@ struct JPEGCreateBitmapParam
     unsigned long density_unit;
     unsigned long X_density;
     unsigned long Y_density;
-    long     bGray;
 
-    long     nAlignedWidth;  // these members will be filled by the
-    bool     bTopDown;      // CreateBitmap method in svtools
+    bool bGray;
+    bool bTopDown;      // CreateBitmap method in svtools
 };
 
 class JPEGReader : public GraphicReader
 {
     SvStream&           mrStream;
-    Bitmap              maBmp;
-    Bitmap              maBmp1;
-    BitmapWriteAccess*  mpAcc;
-    BitmapWriteAccess*  mpAcc1;
-    unsigned char *     mpBuffer;
+    Bitmap              maBitmap;
+    Bitmap              maIncompleteAlpha;
+
     long                mnLastPos;
     long                mnFormerPos;
     long                mnLastLines;
     bool                mbSetLogSize;
 
-    Graphic CreateIntermediateGraphic( const Bitmap& rBitmap, long nLines );
-    void    FillBitmap();
+    Graphic CreateIntermediateGraphic(long nLines);
 
 public:
             JPEGReader( SvStream& rStream, void* pCallData, bool bSetLogSize );
     virtual ~JPEGReader() override;
 
-    ReadState   Read( Graphic& rGraphic );
-    unsigned char * CreateBitmap( JPEGCreateBitmapParam& param );
+    ReadState Read(Graphic& rGraphic);
+
+    bool CreateBitmap(JPEGCreateBitmapParam& param);
+
+    Bitmap& GetBitmap() { return maBitmap; }
 };
 
 #endif // INCLUDED_VCL_SOURCE_FILTER_JPEG_JPEGREADER_HXX
diff --git a/vcl/source/filter/jpeg/jpegc.cxx b/vcl/source/filter/jpeg/jpegc.cxx
index 74be061..28af8c9 100644
--- a/vcl/source/filter/jpeg/jpegc.cxx
+++ b/vcl/source/filter/jpeg/jpegc.cxx
@@ -36,6 +36,7 @@ extern "C" {
 #include <JpegReader.hxx>
 #include <JpegWriter.hxx>
 #include <memory>
+#include <vcl/bitmapaccess.hxx>
 
 #ifdef _MSC_VER
 #pragma warning(push, 1) /* disable to __declspec(align()) aligned warning */
@@ -68,16 +69,8 @@ extern "C" void outputMessage (j_common_ptr cinfo)
 void ReadJPEG( JPEGReader* pJPEGReader, void* pInputStream, long* pLines,
                Size const & previewSize )
 {
-    jpeg_decompress_struct          cinfo;
-    ErrorManagerStruct              jerr;
-    JPEGCreateBitmapParam           aCreateBitmapParam;
-    unsigned char *                 pDIB;
-    unsigned char *                 pTmp;
-    long                            nWidth;
-    long                            nHeight;
-    long                            nAlignedWidth;
-    JSAMPLE*                        aRangeLimit;
-    std::unique_ptr<unsigned char[]> pScanLineBuffer;
+    jpeg_decompress_struct cinfo;
+    ErrorManagerStruct jerr;
 
     if ( setjmp( jerr.setjmp_buffer ) )
     {
@@ -151,72 +144,114 @@ void ReadJPEG( JPEGReader* pJPEGReader, void* pInputStream, long* pLines,
 
     jpeg_start_decompress( &cinfo );
 
-    nWidth = cinfo.output_width;
-    nHeight = cinfo.output_height;
+    long nWidth = cinfo.output_width;
+    long nHeight = cinfo.output_height;
+
+    bool bGray = (cinfo.output_components == 1);
+
+    JPEGCreateBitmapParam aCreateBitmapParam;
+
     aCreateBitmapParam.nWidth = nWidth;
     aCreateBitmapParam.nHeight = nHeight;
 
     aCreateBitmapParam.density_unit = cinfo.density_unit;
     aCreateBitmapParam.X_density = cinfo.X_density;
     aCreateBitmapParam.Y_density = cinfo.Y_density;
-    aCreateBitmapParam.bGray = long(cinfo.output_components == 1);
-    pDIB = pJPEGReader->CreateBitmap(aCreateBitmapParam);
-    nAlignedWidth = aCreateBitmapParam.nAlignedWidth;
-    aRangeLimit = cinfo.sample_range_limit;
+    aCreateBitmapParam.bGray = bGray;
 
-    long nScanLineBufferComponents = 0;
-    if ( cinfo.out_color_space == JCS_CMYK )
-    {
-        nScanLineBufferComponents = cinfo.output_width * 4;
-        pScanLineBuffer.reset(new unsigned char[nScanLineBufferComponents]);
-    }
+    bool bBitmapCreated = pJPEGReader->CreateBitmap(aCreateBitmapParam);
 
-    if( pDIB )
+    if (bBitmapCreated)
     {
-        if( aCreateBitmapParam.bTopDown )
-        {
-            pTmp = pDIB;
-        }
-        else
-        {
-            pTmp = pDIB + ( nHeight - 1 ) * nAlignedWidth;
-            nAlignedWidth = -nAlignedWidth;
-        }
+        Bitmap::ScopedWriteAccess pAccess(pJPEGReader->GetBitmap());
 
-        for ( *pLines = 0; *pLines < nHeight; (*pLines)++ )
+        if (pAccess)
         {
-            if (pScanLineBuffer)
-            { // in other words cinfo.out_color_space == JCS_CMYK
-                int i;
-                int j;
-                unsigned char *pSLB = pScanLineBuffer.get();
-                jpeg_read_scanlines( &cinfo, reinterpret_cast<JSAMPARRAY>(&pSLB), 1 );
-                // convert CMYK to RGB
-                for( i=0, j=0; i < nScanLineBufferComponents; i+=4, j+=3 )
+            JSAMPLE* aRangeLimit = cinfo.sample_range_limit;
+
+            std::vector<sal_uInt8> pScanLineBuffer(nWidth * (bGray ? 1 : 3));
+            std::vector<sal_uInt8> pCYMKBuffer;
+
+            if (cinfo.out_color_space == JCS_CMYK)
+            {
+                pCYMKBuffer.resize(nWidth * 4);
+            }
+
+            const ScanlineFormat nFormat = pAccess->GetScanlineFormat();
+
+            bool bTopDown = true;
+
+            if (( bGray && nFormat == ScanlineFormat::N8BitPal) ||
+                (!bGray && nFormat == ScanlineFormat::N24BitTcRgb))
+            {
+                bTopDown = pAccess->IsTopDown();
+            }
+
+            std::unique_ptr<BitmapColor[]> pCols;
+
+            if (bGray)
+            {
+                pCols.reset(new BitmapColor[256]);
+
+                for (sal_uInt16 n = 0; n < 256; n++)
                 {
-                    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 ) ];
+                    const sal_uInt8 cGray = n;
+                    pCols[n] = pAccess->GetBestMatchingColor(BitmapColor(cGray, cGray, cGray));
                 }
             }
-            else
+
+            for (*pLines = 0; *pLines < nHeight; (*pLines)++)
             {
-                jpeg_read_scanlines( &cinfo, reinterpret_cast<JSAMPARRAY>(&pTmp), 1 );
-            }
+                size_t yIndex = *pLines;
 
-            /* PENDING ??? */
-            if ( cinfo.err->msg_code == 113 )
-                break;
+                if (cinfo.out_color_space == JCS_CMYK)
+                {
+                    sal_uInt8* p = pCYMKBuffer.data();
+                    jpeg_read_scanlines(&cinfo, reinterpret_cast<JSAMPARRAY>(&p), 1);
+
+                    // convert CMYK to RGB
+                    for (int cmyk = 0, rgb = 0; cmyk < nWidth * 4; cmyk += 4, rgb += 3)
+                    {
+                        int color_C = 255 - pCYMKBuffer[cmyk + 0];
+                        int color_M = 255 - pCYMKBuffer[cmyk + 1];
+                        int color_Y = 255 - pCYMKBuffer[cmyk + 2];
+                        int color_K = 255 - pCYMKBuffer[cmyk + 3];
+
+                        pScanLineBuffer[rgb + 0] = aRangeLimit[255L - (color_C + color_K)];
+                        pScanLineBuffer[rgb + 1] = aRangeLimit[255L - (color_M + color_K)];
+                        pScanLineBuffer[rgb + 2] = aRangeLimit[255L - (color_Y + color_K)];
+                    }
+                }
+                else
+                {
+                    sal_uInt8* p = pScanLineBuffer.data();
+                    jpeg_read_scanlines(&cinfo, reinterpret_cast<JSAMPARRAY>(&p), 1);
+                }
+
+                if (!bTopDown)
+                    yIndex = nHeight - 1 - yIndex;
 
-            pTmp += nAlignedWidth;
+                if (bGray)
+                {
+                    for (long x = 0; x < nWidth; ++x)
+                    {
+                        sal_uInt8 nColorGray = pScanLineBuffer[x];
+                        pAccess->SetPixel(yIndex, x, pCols[nColorGray]);
+                    }
+                }
+                else
+                {
+                    pAccess->CopyScanline(yIndex, pScanLineBuffer.data(), ScanlineFormat::N24BitTcRgb, nWidth * 3);
+                }
+
+                /* PENDING ??? */
+                if (cinfo.err->msg_code == 113)
+                    break;
+            }
         }
     }
 
-    if ( pDIB )
+    if (bBitmapCreated)
     {
         jpeg_finish_decompress( &cinfo );
     }
@@ -225,8 +260,6 @@ void ReadJPEG( JPEGReader* pJPEGReader, void* pInputStream, long* pLines,
         jpeg_abort_decompress( &cinfo );
     }
 
-    pScanLineBuffer.reset();
-
     jpeg_destroy_decompress( &cinfo );
 }
 


More information about the Libreoffice-commits mailing list