Bump version to 5.0-14
[LibreOffice.git] / filter / source / graphicfilter / etiff / etiff.cxx
blob619187cdf4b62714e3a860f9f64216402afe3d33
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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
43 #define ColorMap 320
46 // - TIFFWriter -
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
58 class TIFFWriter
60 private:
62 SvStream& m_rOStm;
63 sal_uInt32 mnStreamOfs;
65 bool mbStatus;
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;
87 sal_uInt16 nDataSize;
88 sal_uInt16 nClearCode;
89 sal_uInt16 nEOICode;
90 sal_uInt16 nTableSize;
91 sal_uInt16 nCodeSize;
92 sal_uInt32 nOffset;
93 sal_uInt32 dwShift;
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 );
108 public:
110 TIFFWriter(SvStream &rStream);
111 ~TIFFWriter();
113 bool WriteTIFF( const Graphic& rGraphic, FilterConfigItem* pFilterConfigItem );
118 TIFFWriter::TIFFWriter(SvStream &rStream)
119 : m_rOStm(rStream)
120 , mnStreamOfs(0)
121 , mbStatus(true)
122 , mpAcc(NULL)
123 , mnWidth(0)
124 , mnHeight(0)
125 , mnColors(0)
126 , mnCurAllPictHeight(0)
127 , mnSumOfAllPictHeight(0)
128 , mnBitsPerPixel(0)
129 , mnLastPercent(0)
130 , mnLatestIfdPos(0)
131 , mnTagCount(0)
132 , mnCurrentTagCountPos(0)
133 , mnXResPos(0)
134 , mnYResPos(0)
135 , mnPalPos(0)
136 , mnBitmapPos(0)
137 , mnStripByteCountPos(0)
138 , pTable(NULL)
139 , pPrefix(NULL)
140 , nDataSize(0)
141 , nClearCode(0)
142 , nEOICode(0)
143 , nTableSize(0)
144 , nCodeSize(0)
145 , nOffset(0)
146 , dwShift(0)
150 TIFFWriter::~TIFFWriter()
154 bool TIFFWriter::WriteTIFF( const Graphic& rGraphic, FilterConfigItem* pFilterConfigItem)
156 if ( pFilterConfigItem )
158 xStatusIndicator = pFilterConfigItem->GetStatusIndicator();
159 if ( xStatusIndicator.is() )
161 OUString aMsg;
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
170 // TIFF header
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;
177 Bitmap aBmp;
179 if( mbStatus )
181 if ( rGraphic.IsAnimated() )
182 aAnimation = rGraphic.GetAnimation();
183 else
185 AnimationBitmap aAnimationBitmap( rGraphic.GetBitmap(), Point(), Size() );
186 aAnimation.Insert( aAnimationBitmap );
189 sal_uInt16 i;
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++ )
195 mnPalPos = 0;
196 const AnimationBitmap& rAnimationBitmap = aAnimation.Get( i );
197 aBmp = rAnimationBitmap.aBmpEx.GetBitmap();
198 mpAcc = aBmp.AcquireReadAccess();
199 if ( mpAcc )
201 mnBitsPerPixel = aBmp.GetBitCount();
203 // export code below only handles four discrete cases
204 mnBitsPerPixel =
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() );
218 if ( mnPalPos )
219 ImplWritePalette();
220 ImplWriteBody();
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 );
229 else
230 mbStatus = false;
233 m_rOStm.SetEndian( nOldFormat );
235 if ( xStatusIndicator.is() )
236 xStatusIndicator->end();
238 return mbStatus;
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 )
261 mnTagCount = 0;
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;
277 if ( bMultiPage )
278 nSubFileFlags |= 2;
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 );
284 sal_uInt8 nTemp;
285 switch ( mnBitsPerPixel )
287 case 1 :
288 nTemp = 1;
289 break;
290 case 4 :
291 case 8 :
292 nTemp = 3;
293 break;
294 case 24:
295 nTemp = 2;
296 break;
297 default:
298 nTemp = 0; // -Wall set a default...
299 break;
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
326 else
327 mbStatus = false;
329 return mbStatus;
334 void TIFFWriter::ImplWritePalette()
336 sal_uInt16 i;
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()
363 sal_uInt8 nTemp = 0;
364 sal_uInt8 nShift;
365 sal_uLong j, x, y;
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 );
372 StartCompression();
374 switch( mnBitsPerPixel )
376 case 24 :
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() );
390 break;
392 case 8 :
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 ) );
403 break;
405 case 4 :
407 for ( nShift = 0, y = 0; y < mnHeight; y++, mnCurAllPictHeight++ )
409 ImplCallback( 100 * mnCurAllPictHeight / mnSumOfAllPictHeight );
410 for ( x = 0; x < mnWidth; x++, nShift++ )
412 if (!( nShift & 1 ))
413 nTemp = ( mpAcc->GetPixelIndex( y, x ) << 4 );
414 else
415 Compress( (sal_uInt8)( nTemp | ( mpAcc->GetPixelIndex( y, x ) & 0xf ) ) );
417 if ( nShift & 1 )
418 Compress( nTemp );
421 break;
423 case 1 :
425 j = 1;
426 for ( y = 0; y < mnHeight; y++, mnCurAllPictHeight++ )
428 ImplCallback( 100 * mnCurAllPictHeight / mnSumOfAllPictHeight );
429 for ( x = 0; x < mnWidth; x++)
431 j <<= 1;
432 j |= ( ( ~mpAcc->GetPixelIndex( y, x ) ) & 1 );
433 if ( j & 0x100 )
435 Compress( (sal_uInt8)j );
436 j = 1;
439 if ( j != 1 )
441 Compress( (sal_uInt8)(j << ( ( ( x & 7) ^ 7 ) + 1 ) ) );
442 j = 1;
446 break;
448 default:
450 mbStatus = false;
452 break;
455 EndCompression();
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 );
464 return mbStatus;
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)
483 mnTagCount++;
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 ) );
498 nOffset -= nCodeLen;
499 while ( nOffset < 24 )
501 m_rOStm.WriteUChar( dwShift >> 24 );
502 dwShift <<= 8;
503 nOffset += 8;
505 if ( nCode == 257 && nOffset != 32 )
507 m_rOStm.WriteUChar( dwShift >> 24 );
513 void TIFFWriter::StartCompression()
515 sal_uInt16 i;
516 nDataSize = 8;
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
524 dwShift = 0;
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 );
534 pPrefix = NULL;
535 WriteBits( nClearCode, nCodeSize );
540 void TIFFWriter::Compress( sal_uInt8 nCompThis )
542 TIFFLZWCTreeNode* p;
543 sal_uInt16 i;
544 sal_uInt8 nV;
546 if( !pPrefix )
548 pPrefix = pTable + nCompThis;
550 else
552 nV = nCompThis;
553 for( p = pPrefix->pFirstChild; p != NULL; p = p->pBrother )
555 if ( p->nValue == nV )
556 break;
559 if( p )
560 pPrefix = p;
561 else
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;
575 else
577 if( nTableSize == (sal_uInt16)( ( 1 << nCodeSize ) - 1 ) )
578 nCodeSize++;
580 p = pTable + ( nTableSize++ );
581 p->pBrother = pPrefix->pFirstChild;
582 pPrefix->pFirstChild = p;
583 p->nValue = nV;
584 p->pFirstChild = NULL;
587 pPrefix = pTable + nV;
594 void TIFFWriter::EndCompression()
596 if( pPrefix )
597 WriteBits( pPrefix->nCode, nCodeSize );
599 WriteBits( nEOICode, nCodeSize );
600 delete[] pTable;
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
610 #endif
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: */