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