1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <vcl/graph.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/msgbox.hxx>
24 #include <vcl/bmpacc.hxx>
25 #include <svl/solar.hrc>
26 #include <vcl/fltcall.hxx>
27 #include <vcl/FilterConfigItem.hxx>
29 #define NewSubfileType 254
30 #define ImageWidth 256
31 #define ImageLength 257
32 #define BitsPerSample 258
33 #define Compression 259
34 #define PhotometricInterpretation 262
35 #define StripOffsets 273
36 #define SamplesPerPixel 277
37 #define RowsPerStrip 278
38 #define StripByteCounts 279
39 #define XResolution 282
40 #define YResolution 283
41 #define PlanarConfiguration 284
42 #define ResolutionUnit 296
49 struct TIFFLZWCTreeNode
52 TIFFLZWCTreeNode
* pBrother
; // next node with the same father
53 TIFFLZWCTreeNode
* pFirstChild
; // first son
54 sal_uInt16 nCode
; // The code for the string of pixel values, which arises if... <missing comment>
55 sal_uInt16 nValue
; // pixel value
63 sal_uInt32 mnStreamOfs
;
66 BitmapReadAccess
* mpAcc
;
68 sal_uInt32 mnWidth
, mnHeight
, mnColors
;
69 sal_uInt32 mnCurAllPictHeight
;
70 sal_uInt32 mnSumOfAllPictHeight
;
71 sal_uInt32 mnBitsPerPixel
;
72 sal_uInt32 mnLastPercent
;
74 sal_uInt32 mnLatestIfdPos
;
75 sal_uInt16 mnTagCount
; // number of tags already written
76 sal_uInt32 mnCurrentTagCountPos
; // offset to the position where the current
77 // tag count is to insert
79 sal_uInt32 mnXResPos
; // if != 0 this DWORDs stores the
80 sal_uInt32 mnYResPos
; // actual streamposition of the
81 sal_uInt32 mnPalPos
; // Tag Entry
82 sal_uInt32 mnBitmapPos
;
83 sal_uInt32 mnStripByteCountPos
;
85 TIFFLZWCTreeNode
* pTable
;
86 TIFFLZWCTreeNode
* pPrefix
;
88 sal_uInt16 nClearCode
;
90 sal_uInt16 nTableSize
;
95 com::sun::star::uno::Reference
< com::sun::star::task::XStatusIndicator
> xStatusIndicator
;
97 void ImplCallback( sal_uInt32 nPercent
);
98 bool ImplWriteHeader( bool bMultiPage
);
99 void ImplWritePalette();
100 bool ImplWriteBody();
101 void ImplWriteTag( sal_uInt16 TagID
, sal_uInt16 DataType
, sal_uInt32 NumberOfItems
, sal_uInt32 Value
);
102 void ImplWriteResolution( sal_uLong nStreamPos
, sal_uInt32 nResolutionUnit
);
103 void StartCompression();
104 void Compress( sal_uInt8 nSrc
);
105 void EndCompression();
106 inline void WriteBits( sal_uInt16 nCode
, sal_uInt16 nCodeLen
);
110 TIFFWriter(SvStream
&rStream
);
113 bool WriteTIFF( const Graphic
& rGraphic
, FilterConfigItem
* pFilterConfigItem
);
118 TIFFWriter::TIFFWriter(SvStream
&rStream
)
126 , mnCurAllPictHeight(0)
127 , mnSumOfAllPictHeight(0)
132 , mnCurrentTagCountPos(0)
137 , mnStripByteCountPos(0)
150 TIFFWriter::~TIFFWriter()
154 bool TIFFWriter::WriteTIFF( const Graphic
& rGraphic
, FilterConfigItem
* pFilterConfigItem
)
156 if ( pFilterConfigItem
)
158 xStatusIndicator
= pFilterConfigItem
->GetStatusIndicator();
159 if ( xStatusIndicator
.is() )
162 xStatusIndicator
->start( aMsg
, 100 );
166 const SvStreamEndian nOldFormat
= m_rOStm
.GetEndian();
167 mnStreamOfs
= m_rOStm
.Tell();
169 // we will use the BIG Endian Mode
171 m_rOStm
.SetEndian( SvStreamEndian::BIG
);
172 m_rOStm
.WriteUInt32( 0x4d4d002a ); // TIFF identifier
173 mnLatestIfdPos
= m_rOStm
.Tell();
174 m_rOStm
.WriteUInt32( 0 );
176 Animation aAnimation
;
181 if ( rGraphic
.IsAnimated() )
182 aAnimation
= rGraphic
.GetAnimation();
185 AnimationBitmap
aAnimationBitmap( rGraphic
.GetBitmap(), Point(), Size() );
186 aAnimation
.Insert( aAnimationBitmap
);
190 for ( i
= 0; i
< aAnimation
.Count(); i
++ )
191 mnSumOfAllPictHeight
+= aAnimation
.Get( i
).aBmpEx
.GetSizePixel().Height();
193 for ( i
= 0; mbStatus
&& ( i
< aAnimation
.Count() ); i
++ )
196 const AnimationBitmap
& rAnimationBitmap
= aAnimation
.Get( i
);
197 aBmp
= rAnimationBitmap
.aBmpEx
.GetBitmap();
198 mpAcc
= aBmp
.AcquireReadAccess();
201 mnBitsPerPixel
= aBmp
.GetBitCount();
203 // export code below only handles four discrete cases
205 mnBitsPerPixel
<= 1 ? 1 : mnBitsPerPixel
<= 4 ? 4 : mnBitsPerPixel
<= 8 ? 8 : 24;
207 if ( ImplWriteHeader( ( aAnimation
.Count() > 0 ) ) )
209 Size
aDestMapSize( 300, 300 );
210 const MapMode
aMapMode( aBmp
.GetPrefMapMode() );
211 if ( aMapMode
.GetMapUnit() != MAP_PIXEL
)
213 const Size
aPrefSize( rGraphic
.GetPrefSize() );
214 aDestMapSize
= OutputDevice::LogicToLogic( aPrefSize
, aMapMode
, MAP_INCH
);
216 ImplWriteResolution( mnXResPos
, aDestMapSize
.Width() );
217 ImplWriteResolution( mnYResPos
, aDestMapSize
.Height() );
222 sal_uInt32 nCurPos
= m_rOStm
.Tell();
223 m_rOStm
.Seek( mnCurrentTagCountPos
);
224 m_rOStm
.WriteUInt16( mnTagCount
);
225 m_rOStm
.Seek( nCurPos
);
227 Bitmap::ReleaseAccess( mpAcc
);
233 m_rOStm
.SetEndian( nOldFormat
);
235 if ( xStatusIndicator
.is() )
236 xStatusIndicator
->end();
243 void TIFFWriter::ImplCallback( sal_uInt32 nPercent
)
245 if ( xStatusIndicator
.is() )
247 if( nPercent
>= mnLastPercent
+ 3 )
249 mnLastPercent
= nPercent
;
250 if ( nPercent
<= 100 )
251 xStatusIndicator
->setValue( nPercent
);
259 bool TIFFWriter::ImplWriteHeader( bool bMultiPage
)
262 mnWidth
= mpAcc
->Width();
263 mnHeight
= mpAcc
->Height();
265 if ( mnWidth
&& mnHeight
&& mnBitsPerPixel
&& mbStatus
)
267 sal_uInt32 nCurrentPos
= m_rOStm
.Tell();
268 m_rOStm
.Seek( mnLatestIfdPos
);
269 m_rOStm
.WriteUInt32( nCurrentPos
- mnStreamOfs
); // offset to the IFD
270 m_rOStm
.Seek( nCurrentPos
);
272 // (OFS8) TIFF image file directory (IFD)
273 mnCurrentTagCountPos
= m_rOStm
.Tell();
274 m_rOStm
.WriteUInt16( 0 ); // the number of tagentrys is to insert later
276 sal_uInt32 nSubFileFlags
= 0;
279 ImplWriteTag( NewSubfileType
, 4, 1, nSubFileFlags
);
280 ImplWriteTag( ImageWidth
, 4, 1, mnWidth
);
281 ImplWriteTag( ImageLength
, 4, 1, mnHeight
);
282 ImplWriteTag( BitsPerSample
, 3, 1, ( mnBitsPerPixel
== 24 ) ? 8 : mnBitsPerPixel
);
283 ImplWriteTag( Compression
, 3, 1, 5 );
285 switch ( mnBitsPerPixel
)
298 nTemp
= 0; // -Wall set a default...
301 ImplWriteTag( PhotometricInterpretation
, 3, 1, nTemp
);
302 mnBitmapPos
= m_rOStm
.Tell();
303 ImplWriteTag( StripOffsets
, 4, 1, 0 );
304 ImplWriteTag( SamplesPerPixel
, 3, 1, ( mnBitsPerPixel
== 24 ) ? 3 : 1 );
305 ImplWriteTag( RowsPerStrip
, 4, 1, mnHeight
); //0xffffffff );
306 mnStripByteCountPos
= m_rOStm
.Tell();
307 ImplWriteTag( StripByteCounts
, 4, 1, ( ( mnWidth
* mnBitsPerPixel
* mnHeight
) + 7 ) >> 3 );
308 mnXResPos
= m_rOStm
.Tell();
309 ImplWriteTag( XResolution
, 5, 1, 0 );
310 mnYResPos
= m_rOStm
.Tell();
311 ImplWriteTag( YResolution
, 5, 1, 0 );
312 if ( mnBitsPerPixel
!= 1 )
313 ImplWriteTag( PlanarConfiguration
, 3, 1, 1 ); // ( RGB ORDER )
314 ImplWriteTag( ResolutionUnit
, 3, 1, 2); // Resolution Unit is Inch
315 if ( ( mnBitsPerPixel
== 4 ) || ( mnBitsPerPixel
== 8 ) )
317 mnColors
= mpAcc
->GetPaletteEntryCount();
318 mnPalPos
= m_rOStm
.Tell();
319 ImplWriteTag( ColorMap
, 3, 3 * mnColors
, 0 );
322 // and last we write zero to close the num dir entries list
323 mnLatestIfdPos
= m_rOStm
.Tell();
324 m_rOStm
.WriteUInt32( 0 ); // there are no more IFD
334 void TIFFWriter::ImplWritePalette()
337 sal_uLong nCurrentPos
= m_rOStm
.Tell();
338 m_rOStm
.Seek( mnPalPos
+ 8 ); // the palette tag entry needs the offset
339 m_rOStm
.WriteUInt32( nCurrentPos
- mnStreamOfs
); // to the palette colors
340 m_rOStm
.Seek( nCurrentPos
);
342 for ( i
= 0; i
< mnColors
; i
++ )
344 const BitmapColor
& rColor
= mpAcc
->GetPaletteColor( i
);
345 m_rOStm
.WriteUInt16( rColor
.GetRed() << 8 );
347 for ( i
= 0; i
< mnColors
; i
++ )
349 const BitmapColor
& rColor
= mpAcc
->GetPaletteColor( i
);
350 m_rOStm
.WriteUInt16( rColor
.GetGreen() << 8 );
352 for ( i
= 0; i
< mnColors
; i
++ )
354 const BitmapColor
& rColor
= mpAcc
->GetPaletteColor( i
);
355 m_rOStm
.WriteUInt16( rColor
.GetBlue() << 8 );
361 bool TIFFWriter::ImplWriteBody()
367 sal_uLong nGfxBegin
= m_rOStm
.Tell();
368 m_rOStm
.Seek( mnBitmapPos
+ 8 ); // the strip offset tag entry needs the offset
369 m_rOStm
.WriteUInt32( nGfxBegin
- mnStreamOfs
); // to the bitmap data
370 m_rOStm
.Seek( nGfxBegin
);
374 switch( mnBitsPerPixel
)
378 for ( y
= 0; y
< mnHeight
; y
++, mnCurAllPictHeight
++ )
380 ImplCallback( 100 * mnCurAllPictHeight
/ mnSumOfAllPictHeight
);
381 for ( x
= 0; x
< mnWidth
; x
++ )
383 const BitmapColor
& rColor
= mpAcc
->GetPixel( y
, x
);
384 Compress( rColor
.GetRed() );
385 Compress( rColor
.GetGreen() );
386 Compress( rColor
.GetBlue() );
394 for ( y
= 0; y
< mnHeight
; y
++, mnCurAllPictHeight
++ )
396 ImplCallback( 100 * mnCurAllPictHeight
/ mnSumOfAllPictHeight
);
397 for ( x
= 0; x
< mnWidth
; x
++ )
399 Compress( mpAcc
->GetPixelIndex( y
, x
) );
407 for ( nShift
= 0, y
= 0; y
< mnHeight
; y
++, mnCurAllPictHeight
++ )
409 ImplCallback( 100 * mnCurAllPictHeight
/ mnSumOfAllPictHeight
);
410 for ( x
= 0; x
< mnWidth
; x
++, nShift
++ )
413 nTemp
= ( mpAcc
->GetPixelIndex( y
, x
) << 4 );
415 Compress( (sal_uInt8
)( nTemp
| ( mpAcc
->GetPixelIndex( y
, x
) & 0xf ) ) );
426 for ( y
= 0; y
< mnHeight
; y
++, mnCurAllPictHeight
++ )
428 ImplCallback( 100 * mnCurAllPictHeight
/ mnSumOfAllPictHeight
);
429 for ( x
= 0; x
< mnWidth
; x
++)
432 j
|= ( ( ~mpAcc
->GetPixelIndex( y
, x
) ) & 1 );
435 Compress( (sal_uInt8
)j
);
441 Compress( (sal_uInt8
)(j
<< ( ( ( x
& 7) ^ 7 ) + 1 ) ) );
457 if ( mnStripByteCountPos
&& mbStatus
)
459 sal_uLong nGfxEnd
= m_rOStm
.Tell();
460 m_rOStm
.Seek( mnStripByteCountPos
+ 8 );
461 m_rOStm
.WriteUInt32( nGfxEnd
- nGfxBegin
); // mnStripByteCountPos needs the size of the compression data
462 m_rOStm
.Seek( nGfxEnd
);
469 void TIFFWriter::ImplWriteResolution( sal_uLong nStreamPos
, sal_uInt32 nResolutionUnit
)
471 sal_uLong nCurrentPos
= m_rOStm
.Tell();
472 m_rOStm
.Seek( nStreamPos
+ 8 );
473 m_rOStm
.WriteUInt32( nCurrentPos
- mnStreamOfs
);
474 m_rOStm
.Seek( nCurrentPos
);
475 m_rOStm
.WriteUInt32( 1 );
476 m_rOStm
.WriteUInt32( nResolutionUnit
);
481 void TIFFWriter::ImplWriteTag( sal_uInt16 nTagID
, sal_uInt16 nDataType
, sal_uInt32 nNumberOfItems
, sal_uInt32 nValue
)
485 m_rOStm
.WriteUInt16( nTagID
);
486 m_rOStm
.WriteUInt16( nDataType
);
487 m_rOStm
.WriteUInt32( nNumberOfItems
);
488 if ( nDataType
== 3 )
489 nValue
<<=16; // in Big Endian Mode WORDS needed to be shifted to a DWORD
490 m_rOStm
.WriteUInt32( nValue
);
495 inline void TIFFWriter::WriteBits( sal_uInt16 nCode
, sal_uInt16 nCodeLen
)
497 dwShift
|= ( nCode
<< ( nOffset
- nCodeLen
) );
499 while ( nOffset
< 24 )
501 m_rOStm
.WriteUChar( dwShift
>> 24 );
505 if ( nCode
== 257 && nOffset
!= 32 )
507 m_rOStm
.WriteUChar( dwShift
>> 24 );
513 void TIFFWriter::StartCompression()
518 nClearCode
= 1 << nDataSize
;
519 nEOICode
= nClearCode
+ 1;
520 nTableSize
= nEOICode
+ 1;
521 nCodeSize
= nDataSize
+ 1;
523 nOffset
= 32; // number of free bits in dwShift
526 pTable
= new TIFFLZWCTreeNode
[ 4096 ];
528 for ( i
= 0; i
< 4096; i
++)
530 pTable
[ i
].pBrother
= pTable
[ i
].pFirstChild
= NULL
;
531 pTable
[ i
].nValue
= (sal_uInt8
)( pTable
[ i
].nCode
= i
);
535 WriteBits( nClearCode
, nCodeSize
);
540 void TIFFWriter::Compress( sal_uInt8 nCompThis
)
548 pPrefix
= pTable
+ nCompThis
;
553 for( p
= pPrefix
->pFirstChild
; p
!= NULL
; p
= p
->pBrother
)
555 if ( p
->nValue
== nV
)
563 WriteBits( pPrefix
->nCode
, nCodeSize
);
565 if ( nTableSize
== 409 )
567 WriteBits( nClearCode
, nCodeSize
);
569 for ( i
= 0; i
< nClearCode
; i
++ )
570 pTable
[ i
].pFirstChild
= NULL
;
572 nCodeSize
= nDataSize
+ 1;
573 nTableSize
= nEOICode
+ 1;
577 if( nTableSize
== (sal_uInt16
)( ( 1 << nCodeSize
) - 1 ) )
580 p
= pTable
+ ( nTableSize
++ );
581 p
->pBrother
= pPrefix
->pFirstChild
;
582 pPrefix
->pFirstChild
= p
;
584 p
->pFirstChild
= NULL
;
587 pPrefix
= pTable
+ nV
;
594 void TIFFWriter::EndCompression()
597 WriteBits( pPrefix
->nCode
, nCodeSize
);
599 WriteBits( nEOICode
, nCodeSize
);
605 // this needs to be kept in sync with
606 // ImpFilterLibCacheEntry::GetImportFunction() from
607 // vcl/source/filter/graphicfilter.cxx
608 #if defined(DISABLE_DYNLOADING)
609 #define GraphicExport etiGraphicExport
612 extern "C" SAL_DLLPUBLIC_EXPORT
bool SAL_CALL
613 GraphicExport( SvStream
& rStream
, Graphic
& rGraphic
, FilterConfigItem
* pFilterConfigItem
)
615 TIFFWriter
aWriter(rStream
);
616 return aWriter
.WriteTIFF( rGraphic
, pFilterConfigItem
);
619 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */