xref: /AOO41X/main/filter/source/graphicfilter/etiff/etiff.cxx (revision 87bc88d3ed834c36654f277ed18005c290f77bdd)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_filter.hxx"
26 
27 #include <vcl/graph.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/msgbox.hxx>
30 #include <vcl/bmpacc.hxx>
31 #include <svl/solar.hrc>
32 #include <svtools/fltcall.hxx>
33 #include <svtools/FilterConfigItem.hxx>
34 
35 #define NewSubfileType              254
36 #define ImageWidth                  256
37 #define ImageLength                 257
38 #define BitsPerSample               258
39 #define Compression                 259
40 #define PhotometricInterpretation   262
41 #define StripOffsets                273
42 #define SamplesPerPixel             277
43 #define RowsPerStrip                278
44 #define StripByteCounts             279
45 #define XResolution                 282
46 #define YResolution                 283
47 #define PlanarConfiguration         284
48 #define ResolutionUnit              296
49 #define ColorMap                    320
50 #define ReferenceBlackWhite         532
51 
52 // -------------
53 // - TIFFWriter -
54 // -------------
55 
56 struct TIFFLZWCTreeNode
57 {
58 
59     TIFFLZWCTreeNode*   pBrother;       // naechster Knoten, der den selben Vater hat
60     TIFFLZWCTreeNode*   pFirstChild;    // erster Sohn
61     sal_uInt16              nCode;          // Der Code fuer den String von Pixelwerten, der sich ergibt, wenn
62     sal_uInt16              nValue;         // Der Pixelwert
63 };
64 
65 class TIFFWriter
66 {
67 private:
68 
69     SvStream*           mpOStm;
70     sal_uInt32              mnStreamOfs;
71 
72     sal_Bool                mbStatus;
73     BitmapReadAccess*   mpAcc;
74 
75     sal_uInt32              mnWidth, mnHeight, mnColors;
76     sal_uInt32              mnCurAllPictHeight;
77     sal_uInt32              mnSumOfAllPictHeight;
78     sal_uInt32              mnBitsPerPixel;
79     sal_uInt32              mnLastPercent;
80 
81     sal_uInt32              mnLatestIfdPos;
82     sal_uInt16              mnTagCount;                 // number of tags already written
83     sal_uInt32              mnCurrentTagCountPos;       // offset to the position where the current
84                                                     // tag count is to insert
85 
86     sal_uInt32              mnXResPos;                  // if != 0 this DWORDs stores the
87     sal_uInt32              mnYResPos;                  // actual streamposition of the
88     sal_uInt32              mnPalPos;                   // Tag Entry
89     sal_uInt32              mnBitmapPos;
90     sal_uInt32              mnStripByteCountPos;
91 
92     TIFFLZWCTreeNode*   pTable;
93     TIFFLZWCTreeNode*   pPrefix;
94     sal_uInt16              nDataSize;
95     sal_uInt16              nClearCode;
96     sal_uInt16              nEOICode;
97     sal_uInt16              nTableSize;
98     sal_uInt16              nCodeSize;
99     sal_uLong               nOffset;
100     sal_uLong               dwShift;
101 
102     com::sun::star::uno::Reference< com::sun::star::task::XStatusIndicator > xStatusIndicator;
103 
104     void                ImplCallback( sal_uInt32 nPercent );
105     sal_Bool                ImplWriteHeader( sal_Bool bMultiPage );
106     void                ImplWritePalette();
107     sal_Bool                ImplWriteBody();
108     void                ImplWriteTag( sal_uInt16 TagID, sal_uInt16 DataType, sal_uInt32 NumberOfItems, sal_uInt32 Value);
109     void                ImplWriteResolution( sal_uLong nStreamPos, sal_uInt32 nResolutionUnit );
110     void                StartCompression();
111     void                Compress( sal_uInt8 nSrc );
112     void                EndCompression();
113     inline void         WriteBits( sal_uInt16 nCode, sal_uInt16 nCodeLen );
114 
115 public:
116 
117                         TIFFWriter();
118                         ~TIFFWriter();
119 
120     sal_Bool                WriteTIFF( const Graphic& rGraphic, SvStream& rTIFF, FilterConfigItem* pFilterConfigItem );
121 };
122 
123 // ------------------------------------------------------------------------
124 
TIFFWriter()125 TIFFWriter::TIFFWriter() :
126         mbStatus            ( sal_True ),
127         mpAcc               ( NULL ),
128         mnCurAllPictHeight  ( 0 ),
129         mnSumOfAllPictHeight( 0 ),
130         mnLastPercent       ( 0 ),
131         mnXResPos           ( 0 ),
132         mnYResPos           ( 0 ),
133         mnBitmapPos         ( 0 ),
134         mnStripByteCountPos ( 0 )
135 {
136 }
137 
138 // ------------------------------------------------------------------------
139 
~TIFFWriter()140 TIFFWriter::~TIFFWriter()
141 {
142 }
143 
144 // ------------------------------------------------------------------------
145 
WriteTIFF(const Graphic & rGraphic,SvStream & rTIFF,FilterConfigItem * pFilterConfigItem)146 sal_Bool TIFFWriter::WriteTIFF( const Graphic& rGraphic, SvStream& rTIFF, FilterConfigItem* pFilterConfigItem)
147 {
148     sal_uLong*  pDummy = new sal_uLong; delete pDummy; // damit unter OS/2
149                                                // das richtige (Tools-)new
150                                                // verwendet wird, da es sonst
151                                                // in dieser DLL nur Vector-news
152                                                // gibt;
153 
154     if ( pFilterConfigItem )
155     {
156         xStatusIndicator = pFilterConfigItem->GetStatusIndicator();
157         if ( xStatusIndicator.is() )
158         {
159             rtl::OUString aMsg;
160             xStatusIndicator->start( aMsg, 100 );
161         }
162     }
163 
164     // #i69169# copy stream
165     mpOStm = &rTIFF;
166 
167     const sal_uInt16    nOldFormat = mpOStm->GetNumberFormatInt();
168     mnStreamOfs = mpOStm->Tell();
169 
170     // we will use the BIG Endian Mode
171     // TIFF header
172     mpOStm->SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN );
173     *mpOStm << (sal_uInt32)0x4d4d002a;      // TIFF identifier
174     mnLatestIfdPos = mpOStm->Tell();
175     *mpOStm << (sal_uInt32)0;
176 
177     Animation   aAnimation;
178     Bitmap      aBmp;
179 
180     if( mbStatus )
181     {
182         if ( rGraphic.IsAnimated() )
183             aAnimation = rGraphic.GetAnimation();
184         else
185         {
186             AnimationBitmap aAnimationBitmap( rGraphic.GetBitmap(), Point(), Size() );
187             aAnimation.Insert( aAnimationBitmap );
188         }
189 
190         sal_uInt16 i;
191         for ( i = 0; i < aAnimation.Count(); i++ )
192             mnSumOfAllPictHeight += aAnimation.Get( i ).aBmpEx.GetSizePixel().Height();
193 
194         for ( i = 0; mbStatus && ( i < aAnimation.Count() ); i++ )
195         {
196             mnPalPos = 0;
197             const AnimationBitmap& rAnimationBitmap = aAnimation.Get( i );
198             aBmp = rAnimationBitmap.aBmpEx.GetBitmap();
199             mpAcc = aBmp.AcquireReadAccess();
200             if ( mpAcc )
201             {
202                 mnBitsPerPixel = aBmp.GetBitCount();
203 
204                 // export code below only handles four discrete cases
205                 mnBitsPerPixel =
206                     mnBitsPerPixel <= 1 ? 1 : mnBitsPerPixel <= 4 ? 4 : mnBitsPerPixel <= 8 ? 8 : 24;
207 
208                 if ( ImplWriteHeader( ( aAnimation.Count() > 0 ) ) )
209                 {
210                     Size aDestMapSize( 300, 300 );
211                     const MapMode aMapMode( aBmp.GetPrefMapMode() );
212                     if ( aMapMode.GetMapUnit() != MAP_PIXEL )
213                     {
214                         const Size aPrefSize( rGraphic.GetPrefSize() );
215                         aDestMapSize = OutputDevice::LogicToLogic( aPrefSize, aMapMode, MAP_INCH );
216                     }
217                     ImplWriteResolution( mnXResPos, aDestMapSize.Width() );
218                     ImplWriteResolution( mnYResPos, aDestMapSize.Height() );
219                     if  ( mnPalPos )
220                         ImplWritePalette();
221                     ImplWriteBody();
222                 }
223                 sal_uInt32 nCurPos = mpOStm->Tell();
224                 mpOStm->Seek( mnCurrentTagCountPos );
225                 *mpOStm << mnTagCount;
226                 mpOStm->Seek( nCurPos );
227 
228                 aBmp.ReleaseAccess( mpAcc );
229             }
230             else
231                 mbStatus = sal_False;
232         }
233     }
234     mpOStm->SetNumberFormatInt( nOldFormat );
235 
236     if ( xStatusIndicator.is() )
237         xStatusIndicator->end();
238 
239     return mbStatus;
240 }
241 
242 // ------------------------------------------------------------------------
243 
ImplCallback(sal_uInt32 nPercent)244 void TIFFWriter::ImplCallback( sal_uInt32 nPercent )
245 {
246     if ( xStatusIndicator.is() )
247     {
248         if( nPercent >= mnLastPercent + 3 )
249         {
250             mnLastPercent = nPercent;
251             if ( nPercent <= 100 )
252                 xStatusIndicator->setValue( nPercent );
253         }
254     }
255 }
256 
257 
258 // ------------------------------------------------------------------------
259 
ImplWriteHeader(sal_Bool bMultiPage)260 sal_Bool TIFFWriter::ImplWriteHeader( sal_Bool bMultiPage )
261 {
262     mnTagCount = 0;
263     mnWidth = mpAcc->Width();
264     mnHeight = mpAcc->Height();
265 
266     if ( mnWidth && mnHeight && mnBitsPerPixel && mbStatus )
267     {
268         sal_uInt32 nCurrentPos = mpOStm->Tell();
269         mpOStm->Seek( mnLatestIfdPos );
270         *mpOStm << (sal_uInt32)( nCurrentPos - mnStreamOfs );   // offset to the IFD
271         mpOStm->Seek( nCurrentPos );
272 
273         // (OFS8) TIFF image file directory (IFD)
274         mnCurrentTagCountPos = mpOStm->Tell();
275         *mpOStm << (sal_uInt16)0;               // the number of tagentrys is to insert later
276 
277         sal_uInt32 nSubFileFlags = 0;
278         if ( bMultiPage )
279             nSubFileFlags |= 2;
280         ImplWriteTag( NewSubfileType, 4, 1, nSubFileFlags );
281         ImplWriteTag( ImageWidth, 4, 1, mnWidth );
282         ImplWriteTag( ImageLength, 4, 1, mnHeight);
283         ImplWriteTag( BitsPerSample, 3, 1, ( mnBitsPerPixel == 24 ) ? 8 : mnBitsPerPixel );
284         ImplWriteTag( Compression, 3, 1, 5 );
285         sal_uInt8 nTemp;
286         switch ( mnBitsPerPixel )
287         {
288             case 1 :
289                 nTemp = 1;
290                 break;
291             case 4 :
292             case 8 :
293                 nTemp = 3;
294                 break;
295             case 24:
296                 nTemp = 2;
297                 break;
298             default:
299                 nTemp = 0;  // -Wall set a default...
300                 break;
301         }
302         ImplWriteTag( PhotometricInterpretation, 3, 1, nTemp );
303         mnBitmapPos = mpOStm->Tell();
304         ImplWriteTag( StripOffsets, 4, 1, 0 );
305         ImplWriteTag( SamplesPerPixel, 3, 1, ( mnBitsPerPixel == 24 ) ? 3 : 1 );
306         ImplWriteTag( RowsPerStrip, 4, 1, mnHeight );   //0xffffffff );
307         mnStripByteCountPos = mpOStm->Tell();
308         ImplWriteTag( StripByteCounts, 4, 1, ( ( mnWidth * mnBitsPerPixel * mnHeight ) + 7 ) >> 3 );
309         mnXResPos = mpOStm->Tell();
310         ImplWriteTag( XResolution, 5, 1, 0 );
311         mnYResPos = mpOStm->Tell();
312         ImplWriteTag( YResolution, 5, 1, 0 );
313         if ( mnBitsPerPixel != 1 )
314             ImplWriteTag( PlanarConfiguration, 3, 1, 1 );   //  ( RGB ORDER )
315         ImplWriteTag( ResolutionUnit, 3, 1, 2);             // Resolution Unit is Inch
316         if ( ( mnBitsPerPixel == 4 ) || ( mnBitsPerPixel == 8 ) )
317         {
318             mnColors = mpAcc->GetPaletteEntryCount();
319             mnPalPos = mpOStm->Tell();
320             ImplWriteTag( ColorMap, 3, 3 * mnColors, 0 );
321         }
322 
323         // and last we write zero to close the num dir entries list
324         mnLatestIfdPos = mpOStm->Tell();
325         *mpOStm << (sal_uInt32)0;               // there are no more IFD
326     }
327     else
328         mbStatus = sal_False;
329 
330     return mbStatus;
331 }
332 
333 // ------------------------------------------------------------------------
334 
ImplWritePalette()335 void TIFFWriter::ImplWritePalette()
336 {
337     sal_uInt16 i;
338     sal_uLong nCurrentPos = mpOStm->Tell();
339     mpOStm->Seek( mnPalPos + 8 );           // the palette tag entry needs the offset
340     *mpOStm << static_cast<sal_uInt32>(nCurrentPos - mnStreamOfs);  // to the palette colors
341     mpOStm->Seek( nCurrentPos );
342 
343     for ( i = 0; i < mnColors; i++ )
344     {
345         const BitmapColor& rColor = mpAcc->GetPaletteColor( i );
346         *mpOStm << (sal_uInt16)( rColor.GetRed() << 8 );
347     }
348     for ( i = 0; i < mnColors; i++ )
349     {
350         const BitmapColor& rColor = mpAcc->GetPaletteColor( i );
351         *mpOStm << (sal_uInt16)( rColor.GetGreen() << 8 );
352     }
353     for ( i = 0; i < mnColors; i++ )
354     {
355         const BitmapColor& rColor = mpAcc->GetPaletteColor( i );
356         *mpOStm << (sal_uInt16)( rColor.GetBlue() << 8 );
357     }
358 }
359 
360 // ------------------------------------------------------------------------
361 
ImplWriteBody()362 sal_Bool TIFFWriter::ImplWriteBody()
363 {
364     sal_uInt8   nTemp = 0;
365     sal_uInt8    nShift;
366     sal_uLong   j, x, y;
367 
368     sal_uLong nGfxBegin = mpOStm->Tell();
369     mpOStm->Seek( mnBitmapPos + 8 );        // the strip offset tag entry needs the offset
370     *mpOStm << static_cast<sal_uInt32>(nGfxBegin - mnStreamOfs);        // to the bitmap data
371     mpOStm->Seek( nGfxBegin );
372 
373     StartCompression();
374 
375     switch( mnBitsPerPixel )
376     {
377         case 24 :
378         {
379             for ( y = 0; y < mnHeight; y++, mnCurAllPictHeight++ )
380             {
381                 ImplCallback( 100 * mnCurAllPictHeight / mnSumOfAllPictHeight );
382                 for ( x = 0; x < mnWidth; x++ )
383                 {
384                     const BitmapColor& rColor = mpAcc->GetPixel( y, x );
385                     Compress( rColor.GetRed() );
386                     Compress( rColor.GetGreen() );
387                     Compress( rColor.GetBlue() );
388                 }
389             }
390         }
391         break;
392 
393         case 8 :
394         {
395             for ( y = 0; y < mnHeight; y++, mnCurAllPictHeight++ )
396             {
397                 ImplCallback( 100 * mnCurAllPictHeight / mnSumOfAllPictHeight );
398                 for ( x = 0; x < mnWidth; x++ )
399                 {
400                     Compress( mpAcc->GetPixelIndex( y, x ) );
401                 }
402             }
403         }
404         break;
405 
406         case 4 :
407         {
408             for ( nShift = 0, y = 0; y < mnHeight; y++, mnCurAllPictHeight++ )
409             {
410                 ImplCallback( 100 * mnCurAllPictHeight / mnSumOfAllPictHeight );
411                 for ( x = 0; x < mnWidth; x++, nShift++ )
412                 {
413                     if (!( nShift & 1 ))
414                         nTemp = ( mpAcc->GetPixelIndex( y, x ) << 4 );
415                     else
416                         Compress( (sal_uInt8)( nTemp | ( mpAcc->GetPixelIndex( y, x ) & 0xf ) ) );
417                 }
418                 if ( nShift & 1 )
419                     Compress( nTemp );
420             }
421         }
422         break;
423 
424         case 1 :
425         {
426             j = 1;
427             for ( y = 0; y < mnHeight; y++, mnCurAllPictHeight++ )
428             {
429                 ImplCallback( 100 * mnCurAllPictHeight / mnSumOfAllPictHeight );
430                 for ( x = 0; x < mnWidth; x++)
431                 {
432                     j <<= 1;
433                     j |= ( ( ~mpAcc->GetPixelIndex( y, x ) ) & 1 );
434                     if ( j & 0x100 )
435                     {
436                         Compress( (sal_uInt8)j );
437                         j = 1;
438                     }
439                 }
440                 if ( j != 1 )
441                 {
442                     Compress( (sal_uInt8)(j << ( ( ( x & 7) ^ 7 ) + 1 ) ) );
443                     j = 1;
444                 }
445             }
446         }
447         break;
448 
449         default:
450         {
451             mbStatus = sal_False;
452         }
453         break;
454     }
455 
456     EndCompression();
457 
458     if ( mnStripByteCountPos && mbStatus )
459     {
460         sal_uLong nGfxEnd = mpOStm->Tell();
461         mpOStm->Seek( mnStripByteCountPos + 8 );
462         *mpOStm << static_cast<sal_uInt32>( nGfxEnd - nGfxBegin );      // mnStripByteCountPos needs the size of the compression data
463         mpOStm->Seek( nGfxEnd );
464     }
465     return mbStatus;
466 }
467 
468 // ------------------------------------------------------------------------
469 
ImplWriteResolution(sal_uLong nStreamPos,sal_uInt32 nResolutionUnit)470 void TIFFWriter::ImplWriteResolution( sal_uLong nStreamPos, sal_uInt32 nResolutionUnit )
471 {
472     sal_uLong nCurrentPos = mpOStm->Tell();
473     mpOStm->Seek( nStreamPos + 8 );
474     *mpOStm << (sal_uInt32)nCurrentPos - mnStreamOfs;
475     mpOStm->Seek( nCurrentPos );
476     *mpOStm << (sal_uInt32)1;
477     *mpOStm << nResolutionUnit;
478 }
479 
480 // ------------------------------------------------------------------------
481 
ImplWriteTag(sal_uInt16 nTagID,sal_uInt16 nDataType,sal_uInt32 nNumberOfItems,sal_uInt32 nValue)482 void TIFFWriter::ImplWriteTag( sal_uInt16 nTagID, sal_uInt16 nDataType, sal_uInt32 nNumberOfItems, sal_uInt32 nValue)
483 {
484         mnTagCount++;
485 
486         *mpOStm << nTagID;
487         *mpOStm << nDataType;
488         *mpOStm << nNumberOfItems;
489         if ( nDataType == 3 )
490             nValue <<=16;           // in Big Endian Mode WORDS needed to be shifted to a DWORD
491         *mpOStm << nValue;
492 }
493 
494 // ------------------------------------------------------------------------
495 
WriteBits(sal_uInt16 nCode,sal_uInt16 nCodeLen)496 inline void TIFFWriter::WriteBits( sal_uInt16 nCode, sal_uInt16 nCodeLen )
497 {
498     dwShift |= ( nCode << ( nOffset - nCodeLen ) );
499     nOffset -= nCodeLen;
500     while ( nOffset < 24 )
501     {
502         *mpOStm << (sal_uInt8)( dwShift >> 24 );
503         dwShift <<= 8;
504         nOffset += 8;
505     }
506     if ( nCode == 257 && nOffset != 32 )
507     {
508         *mpOStm << (sal_uInt8)( dwShift >> 24 );
509     }
510 }
511 
512 // ------------------------------------------------------------------------
513 
StartCompression()514 void TIFFWriter::StartCompression()
515 {
516     sal_uInt16 i;
517     nDataSize = 8;
518 
519     nClearCode = 1 << nDataSize;
520     nEOICode = nClearCode + 1;
521     nTableSize = nEOICode + 1;
522     nCodeSize = nDataSize + 1;
523 
524     nOffset = 32;                       // anzahl freier bits in dwShift
525     dwShift = 0;
526 
527     pTable = new TIFFLZWCTreeNode[ 4096 ];
528 
529     for ( i = 0; i < 4096; i++)
530     {
531         pTable[ i ].pBrother = pTable[ i ].pFirstChild = NULL;
532         pTable[ i ].nValue = (sal_uInt8)( pTable[ i ].nCode = i );
533     }
534 
535     pPrefix = NULL;
536     WriteBits( nClearCode, nCodeSize );
537 }
538 
539 // ------------------------------------------------------------------------
540 
Compress(sal_uInt8 nCompThis)541 void TIFFWriter::Compress( sal_uInt8 nCompThis )
542 {
543     TIFFLZWCTreeNode*    p;
544     sal_uInt16              i;
545     sal_uInt8               nV;
546 
547     if( !pPrefix )
548     {
549         pPrefix = pTable + nCompThis;
550     }
551     else
552     {
553         nV = nCompThis;
554         for( p = pPrefix->pFirstChild; p != NULL; p = p->pBrother )
555         {
556             if ( p->nValue == nV )
557                 break;
558         }
559 
560         if( p )
561             pPrefix = p;
562         else
563         {
564             WriteBits( pPrefix->nCode, nCodeSize );
565 
566             if ( nTableSize == 409 )
567             {
568                 WriteBits( nClearCode, nCodeSize );
569 
570                 for ( i = 0; i < nClearCode; i++ )
571                     pTable[ i ].pFirstChild = NULL;
572 
573                 nCodeSize = nDataSize + 1;
574                 nTableSize = nEOICode + 1;
575             }
576             else
577             {
578                 if( nTableSize == (sal_uInt16)( ( 1 << nCodeSize ) - 1 ) )
579                     nCodeSize++;
580 
581                 p = pTable + ( nTableSize++ );
582                 p->pBrother = pPrefix->pFirstChild;
583                 pPrefix->pFirstChild = p;
584                 p->nValue = nV;
585                 p->pFirstChild = NULL;
586             }
587 
588             pPrefix = pTable + nV;
589         }
590     }
591 }
592 
593 // ------------------------------------------------------------------------
594 
EndCompression()595 void TIFFWriter::EndCompression()
596 {
597     if( pPrefix )
598         WriteBits( pPrefix->nCode, nCodeSize );
599 
600     WriteBits( nEOICode, nCodeSize );
601     delete[] pTable;
602 }
603 
604 // ------------------------------------------------------------------------
605 
606 // ---------------------
607 // - exported function -
608 // ---------------------
609 
GraphicExport(SvStream & rStream,Graphic & rGraphic,FilterConfigItem * pFilterConfigItem,sal_Bool)610 extern "C" sal_Bool __LOADONCALLAPI GraphicExport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pFilterConfigItem, sal_Bool )
611 {
612     return TIFFWriter().WriteTIFF( rGraphic, rStream, pFilterConfigItem );
613 }
614 
615