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/BitmapTools.hxx>
23 #include <tools/stream.hxx>
25 #include <filter/TgaReader.hxx>
27 class FilterConfigItem
;
29 //============================ TGAReader ==================================
35 sal_uInt8 nImageIDLength
;
36 sal_uInt8 nColorMapType
;
38 sal_uInt16 nColorMapFirstEntryIndex
;
39 sal_uInt16 nColorMapLength
;
40 sal_uInt8 nColorMapEntrySize
;
41 sal_uInt16 nColorMapXOrigin
;
42 sal_uInt16 nColorMapYOrigin
;
43 sal_uInt16 nImageWidth
;
44 sal_uInt16 nImageHeight
;
45 sal_uInt8 nPixelDepth
;
46 sal_uInt8 nImageDescriptor
;
49 #define SizeOfTGAFileFooter 26
53 sal_uInt32 nExtensionFileOffset
;
54 sal_uInt32 nDeveloperDirectoryOffset
;
55 sal_uInt32 nSignature
[4];
57 sal_uInt8 nStringTerminator
;
60 #define SizeOfTGAExtension 495
64 sal_uInt16 nExtensionSize
;
66 char sAuthorComment
[324];
67 char sDateTimeStamp
[12];
70 sal_uInt16 nSoftwareVersionNumber
;
71 sal_uInt8 nSoftwareVersionLetter
;
73 sal_uInt16 nPixelAspectRatioNumerator
;
74 sal_uInt16 nPixelAspectRatioDeNumerator
;
75 sal_uInt16 nGammaValueNumerator
;
76 sal_uInt16 nGammaValueDeNumerator
;
77 sal_uInt32 nColorCorrectionOffset
;
78 sal_uInt32 nPostageStampOffset
;
79 sal_uInt32 nScanLineOffset
;
80 sal_uInt8 nAttributesType
;
89 std::unique_ptr
<vcl::bitmap::RawBitmap
> mpBitmap
;
90 std::vector
<Color
> mvPalette
;
91 std::unique_ptr
<TGAFileHeader
>
93 std::unique_ptr
<TGAFileFooter
>
95 std::unique_ptr
<TGAExtension
>
97 std::unique_ptr
<sal_uInt32
[]>
102 sal_uInt8 mnTGAVersion
; // Enhanced TGA is defined as Version 2.0
103 sal_uInt16 mnDestBitDepth
;
104 bool mbIndexing
; // sal_True if source contains indexing color values
105 bool mbEncoding
; // sal_True if source is compressed
107 bool ImplReadHeader();
108 bool ImplReadPalette();
112 explicit TGAReader(SvStream
&rTGA
);
113 bool ReadTGA(Graphic
&rGraphic
);
118 //=================== Methods of TGAReader ==============================
120 TGAReader::TGAReader(SvStream
&rTGA
)
130 bool TGAReader::ReadTGA(Graphic
& rGraphic
)
132 if ( m_rTGA
.GetError() )
135 m_rTGA
.SetEndian( SvStreamEndian::LITTLE
);
139 if ( !m_rTGA
.GetError() )
141 mbStatus
= ImplReadHeader();
143 mbStatus
= mpFileHeader
->nImageWidth
&& mpFileHeader
->nImageHeight
;
146 sal_Size nSize
= mpFileHeader
->nImageWidth
;
147 nSize
*= mpFileHeader
->nImageHeight
;
148 if (nSize
> SAL_MAX_INT32
/2/3)
151 mpBitmap
.reset( new vcl::bitmap::RawBitmap( Size( mpFileHeader
->nImageWidth
, mpFileHeader
->nImageHeight
), 24 ) );
153 mbStatus
= ImplReadPalette();
155 mbStatus
= ImplReadBody();
158 rGraphic
= vcl::bitmap::CreateFromData(std::move(*mpBitmap
));
165 bool TGAReader::ImplReadHeader()
167 mpFileHeader
.reset( new TGAFileHeader
);
169 m_rTGA
.ReadUChar( mpFileHeader
->nImageIDLength
).ReadUChar( mpFileHeader
->nColorMapType
).ReadUChar( mpFileHeader
->nImageType
). ReadUInt16( mpFileHeader
->nColorMapFirstEntryIndex
).ReadUInt16( mpFileHeader
->nColorMapLength
).ReadUChar( mpFileHeader
->nColorMapEntrySize
). ReadUInt16( mpFileHeader
->nColorMapXOrigin
).ReadUInt16( mpFileHeader
->nColorMapYOrigin
).ReadUInt16( mpFileHeader
->nImageWidth
). ReadUInt16( mpFileHeader
->nImageHeight
).ReadUChar( mpFileHeader
->nPixelDepth
).ReadUChar( mpFileHeader
->nImageDescriptor
);
174 if ( mpFileHeader
->nColorMapType
> 1 )
176 if ( mpFileHeader
->nColorMapType
== 1 )
179 // first we want to get the version
180 mpFileFooter
.reset( new TGAFileFooter
); // read the TGA-File-Footer to determine whether
181 // we got an old TGA format or the new one
183 sal_uInt64 nCurStreamPos
= m_rTGA
.Tell();
184 m_rTGA
.Seek( STREAM_SEEK_TO_END
);
185 sal_uInt64 nTemp
= m_rTGA
.Tell();
186 m_rTGA
.Seek( nTemp
- SizeOfTGAFileFooter
);
188 m_rTGA
.ReadUInt32( mpFileFooter
->nExtensionFileOffset
).ReadUInt32( mpFileFooter
->nDeveloperDirectoryOffset
). ReadUInt32( mpFileFooter
->nSignature
[0] ).ReadUInt32( mpFileFooter
->nSignature
[1] ).ReadUInt32( mpFileFooter
->nSignature
[2] ). ReadUInt32( mpFileFooter
->nSignature
[3] ).ReadUChar( mpFileFooter
->nPadByte
).ReadUChar( mpFileFooter
->nStringTerminator
);
194 // check for sal_True, VISI, ON-X, FILE in the signatures
195 if ( mpFileFooter
->nSignature
[ 0 ] == (('T'<<24)|('R'<<16)|('U'<<8)|'E') &&
196 mpFileFooter
->nSignature
[ 1 ] == (('V'<<24)|('I'<<16)|('S'<<8)|'I') &&
197 mpFileFooter
->nSignature
[ 2 ] == (('O'<<24)|('N'<<16)|('-'<<8)|'X') &&
198 mpFileFooter
->nSignature
[ 3 ] == (('F'<<24)|('I'<<16)|('L'<<8)|'E') )
200 mpExtension
.reset( new TGAExtension
);
202 m_rTGA
.Seek( mpFileFooter
->nExtensionFileOffset
);
203 m_rTGA
.ReadUInt16( mpExtension
->nExtensionSize
);
206 if ( mpExtension
->nExtensionSize
>= SizeOfTGAExtension
)
210 m_rTGA
.ReadBytes(mpExtension
->sAuthorName
, 41);
211 m_rTGA
.ReadBytes(mpExtension
->sAuthorComment
, 324);
212 m_rTGA
.ReadBytes(mpExtension
->sDateTimeStamp
, 12);
213 m_rTGA
.ReadBytes(mpExtension
->sJobNameID
, 12);
214 m_rTGA
.ReadChar( mpExtension
->sJobNameID
[ 0 ] ).ReadChar( mpExtension
->sJobNameID
[ 1 ] ).ReadChar( mpExtension
->sJobNameID
[ 2 ] );
215 m_rTGA
.ReadBytes(mpExtension
->sSoftwareID
, 41);
216 m_rTGA
.ReadUInt16( mpExtension
->nSoftwareVersionNumber
).ReadUChar( mpExtension
->nSoftwareVersionLetter
)
217 .ReadUInt32( mpExtension
->nKeyColor
).ReadUInt16( mpExtension
->nPixelAspectRatioNumerator
)
218 .ReadUInt16( mpExtension
->nPixelAspectRatioDeNumerator
).ReadUInt16( mpExtension
->nGammaValueNumerator
)
219 .ReadUInt16( mpExtension
->nGammaValueDeNumerator
).ReadUInt32( mpExtension
->nColorCorrectionOffset
)
220 .ReadUInt32( mpExtension
->nPostageStampOffset
).ReadUInt32( mpExtension
->nScanLineOffset
)
221 .ReadUChar( mpExtension
->nAttributesType
);
227 m_rTGA
.Seek( nCurStreamPos
);
229 // using the TGA file specification this was the correct form but adobe photoshop sets nImageDescriptor
230 // equal to nPixelDepth
231 // mnDestBitDepth = mpFileHeader->nPixelDepth - ( mpFileHeader->nImageDescriptor & 0xf );
232 mnDestBitDepth
= mpFileHeader
->nPixelDepth
;
234 if ( mnDestBitDepth
== 8 ) // this is a patch for grayscale pictures not including a palette
237 if ( mnDestBitDepth
> 32 ) // maybe the pixeldepth is invalid
239 else if ( mnDestBitDepth
> 8 )
241 else if ( mnDestBitDepth
> 4 )
243 else if ( mnDestBitDepth
> 2 )
246 if ( !mbIndexing
&& ( mnDestBitDepth
< 15 ) )
249 switch ( mpFileHeader
->nImageType
)
251 case 9 : // encoding for colortype 9, 10, 11
258 if ( mpFileHeader
->nImageIDLength
) // skip the Image ID
259 m_rTGA
.SeekRel( mpFileHeader
->nImageIDLength
);
265 bool TGAReader::ImplReadBody()
268 sal_uInt16 nXCount
, nYCount
, nRGB16
;
269 sal_uInt8 nRed
, nGreen
, nBlue
, nRunCount
, nDummy
, nDepth
;
271 // this four variables match the image direction
272 tools::Long nY
, nYAdd
, nX
, nXAdd
, nXStart
;
274 nX
= nXStart
= nY
= 0;
275 nXCount
= nYCount
= 0;
278 if ( mpFileHeader
->nImageDescriptor
& 0x10 )
280 nX
= nXStart
= mpFileHeader
->nImageWidth
- 1;
284 if ( !(mpFileHeader
->nImageDescriptor
& 0x20 ) )
286 nY
= mpFileHeader
->nImageHeight
- 1;
290 nDepth
= mpFileHeader
->nPixelDepth
;
298 // 16 bit encoding + indexing
300 while ( nYCount
< mpFileHeader
->nImageHeight
)
302 m_rTGA
.ReadUChar( nRunCount
);
305 if ( nRunCount
& 0x80 ) // a run length packet
307 m_rTGA
.ReadUInt16( nRGB16
);
310 if ( nRGB16
>= mpFileHeader
->nColorMapLength
)
312 nRed
= static_cast<sal_uInt8
>( mpColorMap
[ nRGB16
] >> 16 );
313 nGreen
= static_cast<sal_uInt8
>( mpColorMap
[ nRGB16
] >> 8 );
314 nBlue
= static_cast<sal_uInt8
>( mpColorMap
[ nRGB16
] );
315 for ( sal_uInt16 i
= 0; i
< ( ( nRunCount
& 0x7f ) + 1 ); i
++ )
317 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, nGreen
, nBlue
) );
320 if ( nXCount
== mpFileHeader
->nImageWidth
)
327 if( nYCount
>= mpFileHeader
->nImageHeight
)
334 for ( sal_uInt16 i
= 0; i
< ( ( nRunCount
& 0x7f ) + 1 ); i
++ )
336 m_rTGA
.ReadUInt16( nRGB16
);
339 if ( nRGB16
>= mpFileHeader
->nColorMapLength
)
341 nRed
= static_cast<sal_uInt8
>( mpColorMap
[ nRGB16
] >> 16 );
342 nGreen
= static_cast<sal_uInt8
>( mpColorMap
[ nRGB16
] >> 8 );
343 nBlue
= static_cast<sal_uInt8
>( mpColorMap
[ nRGB16
] );
346 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, nGreen
, nBlue
) );
349 if ( nXCount
== mpFileHeader
->nImageWidth
)
356 if( nYCount
>= mpFileHeader
->nImageHeight
)
364 // 8 bit encoding + indexing
366 while ( nYCount
< mpFileHeader
->nImageHeight
)
368 m_rTGA
.ReadUChar( nRunCount
);
371 if ( nRunCount
& 0x80 ) // a run length packet
373 m_rTGA
.ReadUChar( nDummy
);
376 if ( nDummy
>= mpFileHeader
->nColorMapLength
)
378 for ( sal_uInt16 i
= 0; i
< ( ( nRunCount
& 0x7f ) + 1 ); i
++ )
380 mpBitmap
->SetPixel( nY
, nX
, mvPalette
[nDummy
] );
383 if ( nXCount
== mpFileHeader
->nImageWidth
)
390 if( nYCount
>= mpFileHeader
->nImageHeight
)
397 for ( sal_uInt16 i
= 0; i
< ( ( nRunCount
& 0x7f ) + 1 ); i
++ )
399 m_rTGA
.ReadUChar( nDummy
);
402 if ( nDummy
>= mpFileHeader
->nColorMapLength
)
404 mpBitmap
->SetPixel( nY
, nX
, mvPalette
[nDummy
] );
407 if ( nXCount
== mpFileHeader
->nImageWidth
)
414 if( nYCount
>= mpFileHeader
->nImageHeight
)
429 // 32 bit transparent true color encoding
432 while ( nYCount
< mpFileHeader
->nImageHeight
)
434 m_rTGA
.ReadUChar( nRunCount
);
437 if ( nRunCount
& 0x80 ) // a run length packet
439 m_rTGA
.ReadUChar( nBlue
).ReadUChar( nGreen
).ReadUChar( nRed
).ReadUChar( nDummy
);
442 for ( sal_uInt16 i
= 0; i
< ( ( nRunCount
& 0x7f ) + 1 ); i
++ )
444 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, nGreen
, nBlue
) );
447 if ( nXCount
== mpFileHeader
->nImageWidth
)
454 if( nYCount
>= mpFileHeader
->nImageHeight
)
461 for ( sal_uInt16 i
= 0; i
< ( ( nRunCount
& 0x7f ) + 1 ); i
++ )
463 m_rTGA
.ReadUChar( nBlue
).ReadUChar( nGreen
).ReadUChar( nRed
).ReadUChar( nDummy
);
466 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, nGreen
, nBlue
) );
469 if ( nXCount
== mpFileHeader
->nImageWidth
)
476 if( nYCount
>= mpFileHeader
->nImageHeight
)
485 // 24 bit true color encoding
487 while ( nYCount
< mpFileHeader
->nImageHeight
)
489 m_rTGA
.ReadUChar( nRunCount
);
492 if ( nRunCount
& 0x80 ) // a run length packet
494 m_rTGA
.ReadUChar( nBlue
).ReadUChar( nGreen
).ReadUChar( nRed
);
497 for ( sal_uInt16 i
= 0; i
< ( ( nRunCount
& 0x7f ) + 1 ); i
++ )
499 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, nGreen
, nBlue
) );
502 if ( nXCount
== mpFileHeader
->nImageWidth
)
509 if( nYCount
>= mpFileHeader
->nImageHeight
)
516 for ( sal_uInt16 i
= 0; i
< ( ( nRunCount
& 0x7f ) + 1 ); i
++ )
518 m_rTGA
.ReadUChar( nBlue
).ReadUChar( nGreen
).ReadUChar( nRed
);
521 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, nGreen
, nBlue
) );
524 if ( nXCount
== mpFileHeader
->nImageWidth
)
531 if( nYCount
>= mpFileHeader
->nImageHeight
)
539 // 16 bit true color encoding
541 while ( nYCount
< mpFileHeader
->nImageHeight
)
543 m_rTGA
.ReadUChar( nRunCount
);
546 if ( nRunCount
& 0x80 ) // a run length packet
548 m_rTGA
.ReadUInt16( nRGB16
);
551 nRed
= static_cast<sal_uInt8
>( nRGB16
>> 7 ) & 0xf8;
552 nGreen
= static_cast<sal_uInt8
>( nRGB16
>> 2 ) & 0xf8;
553 nBlue
= static_cast<sal_uInt8
>( nRGB16
<< 3 ) & 0xf8;
554 for ( sal_uInt16 i
= 0; i
< ( ( nRunCount
& 0x7f ) + 1 ); i
++ )
556 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, nGreen
, nBlue
) );
559 if ( nXCount
== mpFileHeader
->nImageWidth
)
566 if( nYCount
>= mpFileHeader
->nImageHeight
)
573 for ( sal_uInt16 i
= 0; i
< ( ( nRunCount
& 0x7f ) + 1 ); i
++ )
575 m_rTGA
.ReadUInt16( nRGB16
);
578 nRed
= static_cast<sal_uInt8
>( nRGB16
>> 7 ) & 0xf8;
579 nGreen
= static_cast<sal_uInt8
>( nRGB16
>> 2 ) & 0xf8;
580 nBlue
= static_cast<sal_uInt8
>( nRGB16
<< 3 ) & 0xf8;
581 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, nGreen
, nBlue
) );
584 if ( nXCount
== mpFileHeader
->nImageWidth
)
591 if( nYCount
>= mpFileHeader
->nImageHeight
)
606 for ( nYCount
= 0; nYCount
< mpFileHeader
->nImageHeight
; nYCount
++, nY
+= nYAdd
)
617 for (;nXCount
< mpFileHeader
->nImageWidth
; nXCount
++, nX
+= nXAdd
)
619 m_rTGA
.ReadUInt16( nRGB16
);
622 if ( nRGB16
>= mpFileHeader
->nColorMapLength
)
624 nRed
= static_cast<sal_uInt8
>( mpColorMap
[ nRGB16
] >> 16 );
625 nGreen
= static_cast<sal_uInt8
>( mpColorMap
[ nRGB16
] >> 8 );
626 nBlue
= static_cast<sal_uInt8
>( mpColorMap
[ nRGB16
] );
627 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, nGreen
, nBlue
) );
633 for (;nXCount
< mpFileHeader
->nImageWidth
; nXCount
++, nX
+= nXAdd
)
635 m_rTGA
.ReadUChar( nDummy
);
638 if ( nDummy
>= mpFileHeader
->nColorMapLength
)
640 mpBitmap
->SetPixel( nY
, nX
, Color(ColorTransparency
, nDummy
) );
654 for (;nXCount
< mpFileHeader
->nImageWidth
; nXCount
++, nX
+= nXAdd
)
656 m_rTGA
.ReadUChar( nBlue
).ReadUChar( nGreen
).ReadUChar( nRed
).ReadUChar( nDummy
);
659 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, nGreen
, nBlue
) );
666 for (;nXCount
< mpFileHeader
->nImageWidth
; nXCount
++, nX
+= nXAdd
)
668 m_rTGA
.ReadUChar( nBlue
).ReadUChar( nGreen
).ReadUChar( nRed
);
671 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, nGreen
, nBlue
) );
677 for (;nXCount
< mpFileHeader
->nImageWidth
; nXCount
++, nX
+= nXAdd
)
679 m_rTGA
.ReadUInt16( nRGB16
);
682 nRed
= static_cast<sal_uInt8
>( nRGB16
>> 7 ) & 0xf8;
683 nGreen
= static_cast<sal_uInt8
>( nRGB16
>> 2 ) & 0xf8;
684 nBlue
= static_cast<sal_uInt8
>( nRGB16
<< 3 ) & 0xf8;
685 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, nGreen
, nBlue
) );
698 bool TGAReader::ImplReadPalette()
700 if ( mbIndexing
) // read the colormap
702 sal_uInt16 nColors
= mpFileHeader
->nColorMapLength
;
704 if ( !nColors
) // colors == 0 ? -> we will build a grayscale palette
706 if ( mpFileHeader
->nPixelDepth
!= 8 )
709 mpFileHeader
->nColorMapLength
= 256;
710 mpFileHeader
->nColorMapEntrySize
= 0x3f; // patch for the following switch routine
712 mpColorMap
.reset( new sal_uInt32
[ nColors
] ); // we will always index dwords
714 switch( mpFileHeader
->nColorMapEntrySize
)
718 for (sal_uInt32 i
= 0; i
< nColors
; ++i
)
720 mpColorMap
[ i
] = ( i
<< 16 ) + ( i
<< 8 ) + i
;
726 for (sal_uInt16 i
= 0; i
< nColors
; i
++)
728 m_rTGA
.ReadUInt32(mpColorMap
[i
]);
734 for ( sal_uInt16 i
= 0; i
< nColors
; i
++ )
739 m_rTGA
.ReadUChar(nBlue
).ReadUChar(nGreen
).ReadUChar(nRed
);
740 mpColorMap
[i
] = (nRed
<< 16) | (nGreen
<< 8) | nBlue
;
748 for ( sal_uInt16 i
= 0; i
< nColors
; i
++ )
751 m_rTGA
.ReadUInt16( nTemp
);
752 if ( !m_rTGA
.good() )
754 mpColorMap
[ i
] = ( ( nTemp
& 0x7c00 ) << 9 ) + ( ( nTemp
& 0x01e0 ) << 6 ) +
755 ( ( nTemp
& 0x1f ) << 3 );
763 if ( mnDestBitDepth
<= 8 )
765 sal_uInt16 nDestColors
= ( 1 << mnDestBitDepth
);
766 if ( nColors
> nDestColors
)
769 mvPalette
.resize( nColors
);
770 for ( sal_uInt16 i
= 0; i
< nColors
; i
++ )
772 mvPalette
[i
] = Color( static_cast<sal_uInt8
>( mpColorMap
[ i
] >> 16 ),
773 static_cast<sal_uInt8
>( mpColorMap
[ i
] >> 8 ), static_cast<sal_uInt8
>(mpColorMap
[ i
] ) );
781 //================== GraphicImport - the exported function ================
783 bool ImportTgaGraphic(SvStream
& rStream
, Graphic
& rGraphic
)
785 TGAReader
aTGAReader(rStream
);
787 return aTGAReader
.ReadTGA(rGraphic
);
790 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */