LCOV - code coverage report
Current view: top level - sdext/source/pdfimport/xpdfwrapper - pnghelper.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 114 181 63.0 %
Date: 2015-06-13 12:38:46 Functions: 11 13 84.6 %
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 "pnghelper.hxx"
      21             : #include <sal/macros.h>
      22             : 
      23             : #include <zlib.h>
      24             : 
      25             : using namespace pdfi;
      26             : 
      27             : // checksum helpers, courtesy of libpng.org
      28             : 
      29             : /* Table of CRCs of all 8-bit messages. */
      30             : sal_uInt32 PngHelper::crc_table[256];
      31             : 
      32             : /* Flag: has the table been computed? Initially false. */
      33             : bool PngHelper::bCRCTableInit = true;
      34             : 
      35             : /* Make the table for a fast CRC. */
      36           1 : void PngHelper::initCRCTable()
      37             : {
      38         257 :     for (sal_uInt32 n = 0; n < 256; n++)
      39             :     {
      40         256 :         sal_uInt32 c = n;
      41        2304 :         for (int k = 0; k < 8; k++)
      42             :         {
      43        2048 :             if (c & 1)
      44        1024 :                 c = 0xedb88320L ^ (c >> 1);
      45             :             else
      46        1024 :                 c = c >> 1;
      47             :         }
      48         256 :         crc_table[n] = c;
      49             :     }
      50           1 :     bCRCTableInit = false;
      51           1 : }
      52             : 
      53             : /* Update a running CRC with the bytes buf[0..len-1]--the CRC
      54             :   should be initialized to all 1's, and the transmitted value
      55             :   is the 1's complement of the final running CRC (see the
      56             :   crc() routine below)). */
      57             : 
      58           3 : void PngHelper::updateCRC( sal_uInt32& io_rCRC, const sal_uInt8* i_pBuf, size_t i_nLen )
      59             : {
      60           3 :     if( bCRCTableInit )
      61           1 :         initCRCTable();
      62             : 
      63           3 :     sal_uInt32 nCRC = io_rCRC;
      64       24262 :     for( size_t n = 0; n < i_nLen; n++ )
      65       24259 :         nCRC = crc_table[(nCRC ^ i_pBuf[n]) & 0xff] ^ (nCRC >> 8);
      66           3 :     io_rCRC = nCRC;
      67           3 : }
      68             : 
      69           3 : sal_uInt32 PngHelper::getCRC( const sal_uInt8* i_pBuf, size_t i_nLen )
      70             : {
      71           3 :     sal_uInt32 nCRC = 0xffffffff;
      72           3 :     updateCRC( nCRC, i_pBuf, i_nLen );
      73           3 :     return nCRC ^ 0xffffffff;
      74             : }
      75             : 
      76           1 : sal_uInt32 PngHelper::deflateBuffer( const Output_t* i_pBuf, size_t i_nLen, OutputBuffer& o_rOut )
      77             : {
      78           1 :     size_t nOrigSize = o_rOut.size();
      79             : 
      80             :     // prepare z stream
      81             :     z_stream aStream;
      82           1 :     aStream.zalloc  = Z_NULL;
      83           1 :     aStream.zfree   = Z_NULL;
      84           1 :     aStream.opaque  = Z_NULL;
      85           1 :     deflateInit( &aStream, Z_BEST_COMPRESSION );
      86           1 :     aStream.avail_in = uInt(i_nLen);
      87           1 :     aStream.next_in = const_cast<Bytef*>(i_pBuf);
      88             : 
      89             :     sal_uInt8 aOutBuf[ 32768 ];
      90           1 :     do
      91             :     {
      92           1 :         aStream.avail_out = sizeof( aOutBuf );
      93           1 :         aStream.next_out = aOutBuf;
      94             : 
      95           1 :         if( deflate( &aStream, Z_FINISH ) == Z_STREAM_ERROR )
      96             :         {
      97           0 :             deflateEnd( &aStream );
      98             :             // scrao the data of this broken stream
      99           0 :             o_rOut.resize( nOrigSize );
     100           0 :             return 0;
     101             :         }
     102             : 
     103             :         // append compressed bytes
     104           1 :         sal_uInt32 nCompressedBytes = sizeof( aOutBuf ) - aStream.avail_out;
     105           1 :         if( nCompressedBytes )
     106           1 :             o_rOut.insert( o_rOut.end(), aOutBuf, aOutBuf+nCompressedBytes );
     107             : 
     108           1 :     } while( aStream.avail_out == 0 );
     109             : 
     110             :     // cleanup
     111           1 :     deflateEnd( &aStream );
     112             : 
     113           1 :     return sal_uInt32( o_rOut.size() - nOrigSize );
     114             : }
     115             : 
     116           1 : void PngHelper::appendFileHeader( OutputBuffer& o_rOutputBuf )
     117             : {
     118             :     static const unsigned char aHeader[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
     119             : 
     120           1 :     o_rOutputBuf.insert( o_rOutputBuf.end(), aHeader, aHeader + SAL_N_ELEMENTS(aHeader) );
     121           1 : }
     122             : 
     123           3 : size_t PngHelper::startChunk( const char* pChunkName, OutputBuffer& o_rOutputBuf )
     124             : {
     125           3 :     size_t nIndex = sal_uInt32( o_rOutputBuf.size() );
     126           3 :     o_rOutputBuf.insert( o_rOutputBuf.end(), 4, (Output_t)0 );
     127           3 :     o_rOutputBuf.push_back( pChunkName[0] );
     128           3 :     o_rOutputBuf.push_back( pChunkName[1] );
     129           3 :     o_rOutputBuf.push_back( pChunkName[2] );
     130           3 :     o_rOutputBuf.push_back( pChunkName[3] );
     131           3 :     return nIndex;
     132             : }
     133             : 
     134           8 : void PngHelper::set( sal_uInt32 i_nValue, OutputBuffer& o_rOutputBuf, size_t i_nIndex )
     135             : {
     136           8 :     o_rOutputBuf[ i_nIndex   ] = (i_nValue & 0xff000000) >> 24;
     137           8 :     o_rOutputBuf[ i_nIndex+1 ] = (i_nValue & 0x00ff0000) >> 16;
     138           8 :     o_rOutputBuf[ i_nIndex+2 ] = (i_nValue & 0x0000ff00) >> 8;
     139           8 :     o_rOutputBuf[ i_nIndex+3 ] = (i_nValue & 0x000000ff);
     140           8 : }
     141             : 
     142           3 : void PngHelper::endChunk( size_t nStart, OutputBuffer& o_rOutputBuf )
     143             : {
     144           3 :     if( nStart+8 > o_rOutputBuf.size() )
     145           3 :         return; // something broken is going on
     146             : 
     147             :     // update chunk length
     148           3 :     size_t nLen = o_rOutputBuf.size() - nStart;
     149           3 :     sal_uInt32 nDataLen = sal_uInt32(nLen)-8;
     150           3 :     set( nDataLen, o_rOutputBuf, nStart );
     151             : 
     152             :     // append chunk crc
     153           3 :     sal_uInt32 nChunkCRC = getCRC( reinterpret_cast<sal_uInt8*>(&o_rOutputBuf[nStart+4]), nLen-4 );
     154           3 :     append( nChunkCRC, o_rOutputBuf );
     155             : }
     156             : 
     157           1 : void PngHelper::appendIHDR( OutputBuffer& o_rOutputBuf, int width, int height, int depth, int colortype )
     158             : {
     159           1 :     size_t nStart = startChunk( "IHDR", o_rOutputBuf );
     160           1 :     append( width, o_rOutputBuf );
     161           1 :     append( height, o_rOutputBuf );
     162           1 :     o_rOutputBuf.push_back( Output_t(depth) );
     163           1 :     o_rOutputBuf.push_back( Output_t(colortype) );
     164           1 :     o_rOutputBuf.push_back( 0 ); // compression method deflate
     165           1 :     o_rOutputBuf.push_back( 0 ); // filtering method 0 (default)
     166           1 :     o_rOutputBuf.push_back( 0 ); // no interlacing
     167           1 :     endChunk( nStart, o_rOutputBuf );
     168           1 : }
     169             : 
     170           1 : void PngHelper::appendIEND( OutputBuffer& o_rOutputBuf )
     171             : {
     172           1 :     size_t nStart = startChunk( "IEND", o_rOutputBuf );
     173           1 :     endChunk( nStart, o_rOutputBuf );
     174           1 : }
     175             : 
     176           0 : void PngHelper::createPng( OutputBuffer&     o_rOutputBuf,
     177             :                            Stream*           str,
     178             :                            int               width,
     179             :                            int               height,
     180             :                            GfxRGB&           zeroColor,
     181             :                            GfxRGB&           oneColor,
     182             :                            bool              bIsMask
     183             :                            )
     184             : {
     185           0 :     appendFileHeader( o_rOutputBuf );
     186           0 :     appendIHDR( o_rOutputBuf, width, height, 1, 3 );
     187             : 
     188             :     // write palette
     189           0 :     size_t nIdx = startChunk( "PLTE", o_rOutputBuf );
     190             :     // write colors 0 and 1
     191           0 :     o_rOutputBuf.push_back(colToByte(zeroColor.r));
     192           0 :     o_rOutputBuf.push_back(colToByte(zeroColor.g));
     193           0 :     o_rOutputBuf.push_back(colToByte(zeroColor.b));
     194           0 :     o_rOutputBuf.push_back(colToByte(oneColor.r));
     195           0 :     o_rOutputBuf.push_back(colToByte(oneColor.g));
     196           0 :     o_rOutputBuf.push_back(colToByte(oneColor.b));
     197             :     // end PLTE chunk
     198           0 :     endChunk( nIdx, o_rOutputBuf );
     199             : 
     200           0 :     if( bIsMask )
     201             :     {
     202             :         // write tRNS chunk
     203           0 :         nIdx = startChunk( "tRNS", o_rOutputBuf );
     204           0 :         o_rOutputBuf.push_back( 0xff );
     205           0 :         o_rOutputBuf.push_back( 0 );
     206             :         // end tRNS chunk
     207           0 :         endChunk( nIdx, o_rOutputBuf );
     208             :     }
     209             : 
     210             :     // create scan line data buffer
     211           0 :     OutputBuffer aScanlines;
     212           0 :     int nLineSize = (width + 7)/8;
     213           0 :     aScanlines.reserve( nLineSize * height + height );
     214             : 
     215           0 :     str->reset();
     216           0 :     for( int y = 0; y < height; y++ )
     217             :     {
     218             :         // determine filter type (none) for this scanline
     219           0 :         aScanlines.push_back( 0 );
     220           0 :         for( int x = 0; x < nLineSize; x++ )
     221           0 :             aScanlines.push_back( str->getChar() );
     222             :     }
     223             : 
     224             :     // begin IDAT chunk for scanline data
     225           0 :     nIdx = startChunk( "IDAT", o_rOutputBuf );
     226             :     // compress scanlines
     227           0 :     deflateBuffer( &aScanlines[0], aScanlines.size(), o_rOutputBuf );
     228             :     // end IDAT chunk
     229           0 :     endChunk( nIdx, o_rOutputBuf );
     230             : 
     231             :     // output IEND
     232           0 :     appendIEND( o_rOutputBuf );
     233           0 : }
     234             : 
     235           1 : void PngHelper::createPng( OutputBuffer& o_rOutputBuf,
     236             :                            Stream* str,
     237             :                            int width, int height, GfxImageColorMap* colorMap,
     238             :                            Stream* maskStr,
     239             :                            int maskWidth, int maskHeight, GfxImageColorMap* maskColorMap )
     240             : {
     241           1 :     appendFileHeader( o_rOutputBuf );
     242           1 :     appendIHDR( o_rOutputBuf, width, height, 8, 6 ); // RGBA image
     243             : 
     244             :     // initialize stream
     245             :     Guchar *p, *pm;
     246             :     GfxRGB rgb;
     247             :     GfxGray alpha;
     248             :     ImageStream* imgStr =
     249             :         new ImageStream(str,
     250             :                         width,
     251             :                         colorMap->getNumPixelComps(),
     252           1 :                         colorMap->getBits());
     253           1 :     imgStr->reset();
     254             : 
     255             :     // create scan line data buffer
     256           1 :     OutputBuffer aScanlines;
     257           1 :     aScanlines.reserve( width*height*4 + height );
     258             : 
     259         221 :     for( int y=0; y<height; ++y)
     260             :     {
     261         220 :         aScanlines.push_back( 0 );
     262         220 :         p = imgStr->getLine();
     263       48620 :         for( int x=0; x<width; ++x)
     264             :         {
     265       48400 :             colorMap->getRGB(p, &rgb);
     266       48400 :             aScanlines.push_back(colToByte(rgb.r));
     267       48400 :             aScanlines.push_back(colToByte(rgb.g));
     268       48400 :             aScanlines.push_back(colToByte(rgb.b));
     269       48400 :             aScanlines.push_back( 0xff );
     270             : 
     271       48400 :             p +=colorMap->getNumPixelComps();
     272             :         }
     273             :     }
     274             : 
     275             : 
     276             :     // now fill in the mask data
     277             : 
     278             :     // CAUTION: originally this was done in one single loop
     279             :     // it caused merry chaos; the reason is that maskStr and str are
     280             :     // not independent streams, it happens that reading one advances
     281             :     // the other, too. Hence the two passes are imperative !
     282             : 
     283             :     // initialize mask stream
     284             :     ImageStream* imgStrMask =
     285             :         new ImageStream(maskStr,
     286             :                         maskWidth,
     287             :                         maskColorMap->getNumPixelComps(),
     288           1 :                         maskColorMap->getBits());
     289             : 
     290           1 :     imgStrMask->reset();
     291         221 :     for( int y = 0; y < maskHeight; ++y )
     292             :     {
     293         220 :         pm = imgStrMask->getLine();
     294       48620 :         for( int x = 0; x < maskWidth; ++x )
     295             :         {
     296       48400 :             maskColorMap->getGray(pm,&alpha);
     297       48400 :             pm += maskColorMap->getNumPixelComps();
     298       96800 :             int nIndex = (y*height/maskHeight) * (width*4+1) + // mapped line
     299       96800 :                          (x*width/maskWidth)*4 + 1  + 3        // mapped column
     300             :                          ;
     301       48400 :             aScanlines[ nIndex ] = colToByte(alpha);
     302             :         }
     303             :     }
     304             : 
     305           1 :     delete imgStr;
     306           1 :     delete imgStrMask;
     307             : 
     308             :     // begind IDAT chunk for scanline data
     309           1 :     size_t nIdx = startChunk( "IDAT", o_rOutputBuf );
     310             :     // compress scanlines
     311           1 :     deflateBuffer( &aScanlines[0], aScanlines.size(), o_rOutputBuf );
     312             :     // end IDAT chunk
     313           1 :     endChunk( nIdx, o_rOutputBuf );
     314             :     // output IEND
     315           1 :     appendIEND( o_rOutputBuf );
     316           1 : }
     317             : 
     318             : // one bit mask; 0 bits opaque
     319           0 : void PngHelper::createPng( OutputBuffer& o_rOutputBuf,
     320             :                            Stream* str,
     321             :                            int width, int height, GfxImageColorMap* colorMap,
     322             :                            Stream* maskStr,
     323             :                            int maskWidth, int maskHeight,
     324             :                            bool maskInvert
     325             :                           )
     326             : {
     327           0 :     appendFileHeader( o_rOutputBuf );
     328           0 :     appendIHDR( o_rOutputBuf, width, height, 8, 6 ); // RGBA image
     329             : 
     330             :     // initialize stream
     331             :     Guchar *p;
     332             :     GfxRGB rgb;
     333             :     ImageStream* imgStr =
     334             :         new ImageStream(str,
     335             :                         width,
     336             :                         colorMap->getNumPixelComps(),
     337           0 :                         colorMap->getBits());
     338           0 :     imgStr->reset();
     339             : 
     340             :     // create scan line data buffer
     341           0 :     OutputBuffer aScanlines;
     342           0 :     aScanlines.reserve( width*height*4 + height );
     343             : 
     344           0 :     for( int y=0; y<height; ++y)
     345             :     {
     346           0 :         aScanlines.push_back( 0 );
     347           0 :         p = imgStr->getLine();
     348           0 :         for( int x=0; x<width; ++x)
     349             :         {
     350           0 :             colorMap->getRGB(p, &rgb);
     351           0 :             aScanlines.push_back(colToByte(rgb.r));
     352           0 :             aScanlines.push_back(colToByte(rgb.g));
     353           0 :             aScanlines.push_back(colToByte(rgb.b));
     354           0 :             aScanlines.push_back( 0xff );
     355             : 
     356           0 :             p +=colorMap->getNumPixelComps();
     357             :         }
     358             :     }
     359             : 
     360             : 
     361             :     // now fill in the mask data
     362             : 
     363             :     // CAUTION: originally this was done in one single loop
     364             :     // it caused merry chaos; the reason is that maskStr and str are
     365             :     // not independent streams, it happens that reading one advances
     366             :     // the other, too. Hence the two passes are imperative !
     367             : 
     368             :     // initialize mask stream
     369             :     ImageStream* imgStrMask =
     370           0 :         new ImageStream(maskStr, maskWidth, 1, 1);
     371             : 
     372           0 :     imgStrMask->reset();
     373           0 :     for( int y = 0; y < maskHeight; ++y )
     374             :     {
     375           0 :         for( int x = 0; x < maskWidth; ++x )
     376             :         {
     377           0 :             Guchar aPixel = 0;
     378           0 :             imgStrMask->getPixel( &aPixel );
     379           0 :             int nIndex = (y*height/maskHeight) * (width*4+1) + // mapped line
     380           0 :                          (x*width/maskWidth)*4 + 1  + 3        // mapped column
     381             :                          ;
     382           0 :             if( maskInvert )
     383           0 :                 aScanlines[ nIndex ] = aPixel ? 0xff : 0x00;
     384             :             else
     385           0 :                 aScanlines[ nIndex ] = aPixel ? 0x00 : 0xff;
     386             :         }
     387             :     }
     388             : 
     389           0 :     delete imgStr;
     390           0 :     delete imgStrMask;
     391             : 
     392             :     // begind IDAT chunk for scanline data
     393           0 :     size_t nIdx = startChunk( "IDAT", o_rOutputBuf );
     394             :     // compress scanlines
     395           0 :     deflateBuffer( &aScanlines[0], aScanlines.size(), o_rOutputBuf );
     396             :     // end IDAT chunk
     397           0 :     endChunk( nIdx, o_rOutputBuf );
     398             :     // output IEND
     399           0 :     appendIEND( o_rOutputBuf );
     400           0 : }
     401             : 
     402             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11