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 <vcl/outdev.hxx>
24 #include <sal/log.hxx>
25 #include <tools/fract.hxx>
26 #include <tools/helpers.hxx>
27 #include <tools/stream.hxx>
30 class FilterConfigItem
;
32 //============================ PSDReader ==================================
35 #define PSD_GRAYSCALE 1
39 #define PSD_MULTICHANNEL 7
47 sal_uInt32 nSignature
;
62 SvStream
& m_rPSD
; // the PSD file to be read in
63 std::unique_ptr
<PSDFileHeader
>
66 sal_uInt32 mnXResFixed
;
67 sal_uInt32 mnYResFixed
;
72 std::unique_ptr
<vcl::bitmap::RawBitmap
> mpBitmap
;
73 std::vector
<Color
> mvPalette
;
74 sal_uInt16 mnDestBitDepth
;
75 bool mbCompression
; // RLE decoding
76 std::unique_ptr
<sal_uInt8
[]>
80 bool ImplReadHeader();
83 explicit PSDReader(SvStream
&rStream
);
84 bool ReadPSD(Graphic
& rGraphic
);
89 //=================== Methods of PSDReader ==============================
91 PSDReader::PSDReader(SvStream
&rStream
)
96 , mbTransparent(false)
98 , mbCompression(false)
102 bool PSDReader::ReadPSD(Graphic
& rGraphic
)
104 if (m_rPSD
.GetError())
107 m_rPSD
.SetEndian( SvStreamEndian::BIG
);
111 if ( !ImplReadHeader() )
117 if (o3tl::checked_multiply(mpFileHeader
->nColumns
, mpFileHeader
->nRows
, nResult
) || nResult
> SAL_MAX_INT32
/2/3)
121 Size
aBitmapSize( mpFileHeader
->nColumns
, mpFileHeader
->nRows
);
122 mpBitmap
.reset( new vcl::bitmap::RawBitmap( aBitmapSize
, mbTransparent
? 32 : 24 ) );
123 if ( mpPalette
&& mbStatus
)
125 mvPalette
.resize( 256 );
126 for ( sal_uInt16 i
= 0; i
< 256; i
++ )
128 mvPalette
[i
] = Color( mpPalette
[ i
], mpPalette
[ i
+ 256 ], mpPalette
[ i
+ 512 ] );
132 if ((mnDestBitDepth
== 1 || mnDestBitDepth
== 8) && mvPalette
.empty())
134 SAL_WARN("vcl", "no palette, but bit depth is " << mnDestBitDepth
);
140 if ( mbStatus
&& ImplReadBody() )
142 rGraphic
= Graphic( vcl::bitmap::CreateFromData( std::move(*mpBitmap
) ) );
144 if ( mnXResFixed
&& mnYResFixed
)
146 Fraction
aFractX( 1, mnXResFixed
>> 16 );
147 Fraction
aFractY( 1, mnYResFixed
>> 16 );
148 MapMode
aMapMode( MapUnit::MapInch
, Point(), aFractX
, aFractY
);
149 Size aPrefSize
= OutputDevice::LogicToLogic(aBitmapSize
, aMapMode
, MapMode(MapUnit::Map100thMM
));
150 rGraphic
.SetPrefSize( aPrefSize
);
151 rGraphic
.SetPrefMapMode( MapMode( MapUnit::Map100thMM
) );
160 bool PSDReader::ImplReadHeader()
162 mpFileHeader
.reset( new PSDFileHeader
);
164 m_rPSD
.ReadUInt32( mpFileHeader
->nSignature
).ReadUInt16( mpFileHeader
->nVersion
).ReadUInt32( mpFileHeader
->nPad1
). ReadUInt16( mpFileHeader
->nPad2
).ReadUInt16( mpFileHeader
->nChannels
).ReadUInt32( mpFileHeader
->nRows
). ReadUInt32( mpFileHeader
->nColumns
).ReadUInt16( mpFileHeader
->nDepth
).ReadUInt16( mpFileHeader
->nMode
);
166 if ( ( mpFileHeader
->nSignature
!= 0x38425053 ) || ( mpFileHeader
->nVersion
!= 1 ) )
169 if ( mpFileHeader
->nRows
== 0 || mpFileHeader
->nColumns
== 0 )
172 if ( ( mpFileHeader
->nRows
> 30000 ) || ( mpFileHeader
->nColumns
> 30000 ) )
175 sal_uInt16 nDepth
= mpFileHeader
->nDepth
;
176 if (!( ( nDepth
== 1 ) || ( nDepth
== 8 ) || ( nDepth
== 16 ) ) )
179 mnDestBitDepth
= ( nDepth
== 16 ) ? 8 : nDepth
;
181 sal_uInt32
nColorLength(0);
182 m_rPSD
.ReadUInt32( nColorLength
);
183 if ( mpFileHeader
->nMode
== PSD_CMYK
)
185 switch ( mpFileHeader
->nChannels
)
188 mbTransparent
= true;
197 else switch ( mpFileHeader
->nChannels
)
200 mbTransparent
= true;
205 mbTransparent
= true;
214 switch ( mpFileHeader
->nMode
)
218 if ( nColorLength
|| ( nDepth
!= 1 ) )
225 if ( nColorLength
!= 768 ) // we need the color map
227 mpPalette
.reset( new sal_uInt8
[ 768 ] );
228 m_rPSD
.ReadBytes(mpPalette
.get(), 768);
232 case PSD_DUOTONE
: // we'll handle the duotone color like a normal grayscale picture
233 m_rPSD
.SeekRel( nColorLength
);
240 mpPalette
.reset( new sal_uInt8
[ 768 ] );
241 for ( sal_uInt16 i
= 0; i
< 256; i
++ )
243 mpPalette
[ i
] = mpPalette
[ i
+ 256 ] = mpPalette
[ i
+ 512 ] = static_cast<sal_uInt8
>(i
);
250 case PSD_MULTICHANNEL
:
253 if ( nColorLength
) // color table is not supported by the other graphic modes
261 sal_uInt32
nResourceLength(0);
262 m_rPSD
.ReadUInt32(nResourceLength
);
263 if (nResourceLength
> m_rPSD
.remainingSize())
265 sal_uInt32 nLayerPos
= m_rPSD
.Tell() + nResourceLength
;
267 // this is a loop over the resource entries to get the resolution info
268 while( m_rPSD
.Tell() < nLayerPos
)
271 sal_uInt16
nUniqueID(0);
273 m_rPSD
.ReadUInt32(nType
).ReadUInt16(nUniqueID
).ReadUChar(n8
);
274 if (nType
!= 0x3842494d)
276 sal_uInt32 nPStringLen
= n8
;
277 if ( ! ( nPStringLen
& 1 ) )
279 m_rPSD
.SeekRel( nPStringLen
); // skipping the pstring
280 sal_uInt32
nResEntryLen(0);
281 m_rPSD
.ReadUInt32( nResEntryLen
);
282 if ( nResEntryLen
& 1 )
283 nResEntryLen
++; // the resource entries are padded
284 sal_uInt32 nCurrentPos
= m_rPSD
.Tell();
285 if (nCurrentPos
> nLayerPos
|| nResEntryLen
> (nLayerPos
- nCurrentPos
)) // check if size
286 break; // is possible
289 case 0x3ed : // UID for the resolution info
293 m_rPSD
.ReadUInt32( mnXResFixed
).ReadInt16( nUnit
).ReadInt16( nUnit
)
294 .ReadUInt32( mnYResFixed
).ReadInt16( nUnit
).ReadInt16( nUnit
);
298 m_rPSD
.Seek( nCurrentPos
+ nResEntryLen
); // set the stream to the next
300 m_rPSD
.Seek( nLayerPos
);
301 sal_uInt32
nLayerMaskLength(0);
302 m_rPSD
.ReadUInt32( nLayerMaskLength
);
303 m_rPSD
.SeekRel( nLayerMaskLength
);
305 sal_uInt16
nCompression(0);
306 m_rPSD
.ReadUInt16(nCompression
);
307 if ( nCompression
== 0 )
309 mbCompression
= false;
311 else if ( nCompression
== 1 )
313 m_rPSD
.SeekRel( ( mpFileHeader
->nRows
* mpFileHeader
->nChannels
) << 1 );
314 mbCompression
= true;
324 const Color
& SanitizePaletteIndex(std::vector
<Color
> const & rvPalette
, sal_uInt8 nIndex
)
326 if (nIndex
>= rvPalette
.size())
328 auto nSanitizedIndex
= nIndex
% rvPalette
.size();
329 SAL_WARN_IF(nIndex
!= nSanitizedIndex
, "filter.tga", "invalid colormap index: "
330 << static_cast<unsigned int>(nIndex
) << ", colormap len is: "
331 << rvPalette
.size());
332 nIndex
= nSanitizedIndex
;
334 return rvPalette
[nIndex
];
338 bool PSDReader::ImplReadBody()
341 signed char nRunCount
= 0;
342 sal_uInt8 nDat
= 0, nDummy
, nRed
, nGreen
, nBlue
;
343 BitmapColor aBitmapColor
;
346 switch ( mnDestBitDepth
)
350 signed char nBitCount
= -1;
351 while (nY
< mpFileHeader
->nRows
&& m_rPSD
.good())
353 if ( nBitCount
== -1 )
355 if ( mbCompression
) // else nRunCount = 0 -> so we use only single raw packets
358 m_rPSD
.ReadChar(nTmp
);
362 if ( nRunCount
& 0x80 ) // a run length packet
364 const sal_uInt16 nCount
= -nRunCount
+ 1;
365 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
367 if ( nBitCount
== -1 ) // bits left in nDat?
369 m_rPSD
.ReadUChar( nDat
);
373 mpBitmap
->SetPixel(nY
, nX
, SanitizePaletteIndex(mvPalette
, nDat
>> nBitCount
--));
374 if ( ++nX
== mpFileHeader
->nColumns
)
379 if ( nY
== mpFileHeader
->nRows
)
386 const sal_uInt16 nCount
= (nRunCount
& 0x7f) + 1;
387 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
389 if ( nBitCount
== -1 ) // bits left in nDat ?
391 m_rPSD
.ReadUChar( nDat
);
395 mpBitmap
->SetPixel(nY
, nX
, SanitizePaletteIndex(mvPalette
, nDat
>> nBitCount
--));
396 if ( ++nX
== mpFileHeader
->nColumns
)
401 if ( nY
== mpFileHeader
->nRows
)
412 while (nY
< mpFileHeader
->nRows
&& m_rPSD
.good())
414 if ( mbCompression
) // else nRunCount = 0 -> so we use only single raw packets
417 m_rPSD
.ReadChar(nTmp
);
421 if ( nRunCount
& 0x80 ) // a run length packet
423 m_rPSD
.ReadUChar( nDat
);
424 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
425 m_rPSD
.ReadUChar( nDummy
);
426 const sal_uInt16 nCount
= -nRunCount
+ 1;
427 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
429 mpBitmap
->SetPixel(nY
, nX
, SanitizePaletteIndex(mvPalette
, nDat
));
430 if ( ++nX
== mpFileHeader
->nColumns
)
434 if ( nY
== mpFileHeader
->nRows
)
441 const sal_uInt16 nCount
= (nRunCount
& 0x7f) + 1;
442 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
444 m_rPSD
.ReadUChar( nDat
);
445 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
446 m_rPSD
.ReadUChar( nDummy
);
447 mpBitmap
->SetPixel(nY
, nX
, SanitizePaletteIndex(mvPalette
, nDat
));
448 if ( ++nX
== mpFileHeader
->nColumns
)
452 if ( nY
== mpFileHeader
->nRows
)
464 // the psd format is in plain order (RRRR GGGG BBBB) so we have to set each pixel three times
465 // maybe the format is CCCC MMMM YYYY KKKK
467 while (nY
< mpFileHeader
->nRows
&& m_rPSD
.good())
469 if ( mbCompression
) // else nRunCount = 0 -> so we use only single raw packets
472 m_rPSD
.ReadChar(nTmp
);
476 if ( nRunCount
& 0x80 ) // a run length packet
478 m_rPSD
.ReadUChar( nRed
);
479 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
480 m_rPSD
.ReadUChar( nDummy
);
481 const sal_uInt16 nCount
= -nRunCount
+ 1;
482 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
484 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, sal_uInt8(0), sal_uInt8(0) ) );
485 if ( ++nX
== mpFileHeader
->nColumns
)
489 if ( nY
== mpFileHeader
->nRows
)
496 const sal_uInt16 nCount
= (nRunCount
& 0x7f) + 1;
497 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
499 m_rPSD
.ReadUChar( nRed
);
500 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
501 m_rPSD
.ReadUChar( nDummy
);
502 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, sal_uInt8(0), sal_uInt8(0) ) );
503 if ( ++nX
== mpFileHeader
->nColumns
)
507 if ( nY
== mpFileHeader
->nRows
)
514 while (nY
< mpFileHeader
->nRows
&& m_rPSD
.good())
519 m_rPSD
.ReadChar(nTmp
);
523 if ( nRunCount
& 0x80 ) // a run length packet
525 m_rPSD
.ReadUChar( nGreen
);
526 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
527 m_rPSD
.ReadUChar( nDummy
);
528 const sal_uInt16 nCount
= -nRunCount
+ 1;
529 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
531 aBitmapColor
= mpBitmap
->GetPixel( nY
, nX
);
532 mpBitmap
->SetPixel( nY
, nX
, Color( aBitmapColor
.GetRed(), nGreen
, aBitmapColor
.GetBlue() ) );
533 if ( ++nX
== mpFileHeader
->nColumns
)
537 if ( nY
== mpFileHeader
->nRows
)
544 const sal_uInt16 nCount
= (nRunCount
& 0x7f) + 1;
545 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
547 m_rPSD
.ReadUChar( nGreen
);
548 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
549 m_rPSD
.ReadUChar( nDummy
);
550 aBitmapColor
= mpBitmap
->GetPixel( nY
, nX
);
551 mpBitmap
->SetPixel( nY
, nX
, Color( aBitmapColor
.GetRed(), nGreen
, aBitmapColor
.GetBlue() ) );
552 if ( ++nX
== mpFileHeader
->nColumns
)
556 if ( nY
== mpFileHeader
->nRows
)
563 while (nY
< mpFileHeader
->nRows
&& m_rPSD
.good())
568 m_rPSD
.ReadChar(nTmp
);
572 if ( nRunCount
& 0x80 ) // a run length packet
574 m_rPSD
.ReadUChar( nBlue
);
575 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
576 m_rPSD
.ReadUChar( nDummy
);
577 const sal_uInt16 nCount
= -nRunCount
+ 1;
578 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
580 aBitmapColor
= mpBitmap
->GetPixel( nY
, nX
);
581 mpBitmap
->SetPixel( nY
, nX
, Color( aBitmapColor
.GetRed(), aBitmapColor
.GetGreen(), nBlue
) );
582 if ( ++nX
== mpFileHeader
->nColumns
)
586 if ( nY
== mpFileHeader
->nRows
)
593 const sal_uInt16 nCount
= (nRunCount
& 0x7f) + 1;
594 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
596 m_rPSD
.ReadUChar( nBlue
);
597 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
598 m_rPSD
.ReadUChar( nDummy
);
599 aBitmapColor
= mpBitmap
->GetPixel( nY
, nX
);
600 mpBitmap
->SetPixel( nY
, nX
, Color( aBitmapColor
.GetRed(), aBitmapColor
.GetGreen(), nBlue
) );
601 if ( ++nX
== mpFileHeader
->nColumns
)
605 if ( nY
== mpFileHeader
->nRows
)
611 if (mpFileHeader
->nMode
== PSD_CMYK
&& m_rPSD
.good())
613 sal_uInt32 nBlack
, nBlackMax
= 0;
614 std::vector
<sal_uInt8
> aBlack(mpFileHeader
->nRows
* mpFileHeader
->nColumns
, 0);
616 while (nY
< mpFileHeader
->nRows
&& m_rPSD
.good())
618 if ( mbCompression
) // else nRunCount = 0 -> so we use only single raw packets
621 m_rPSD
.ReadChar(nTmp
);
625 if ( nRunCount
& 0x80 ) // a run length packet
627 m_rPSD
.ReadUChar( nDat
);
629 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
630 m_rPSD
.ReadUChar( nDummy
);
632 for ( sal_uInt16 i
= 0; i
< ( -nRunCount
+ 1 ); i
++ )
634 nBlack
= mpBitmap
->GetPixel( nY
, nX
).GetRed() + nDat
;
635 if ( nBlack
> nBlackMax
)
637 nBlack
= mpBitmap
->GetPixel( nY
, nX
).GetGreen() + nDat
;
638 if ( nBlack
> nBlackMax
)
640 nBlack
= mpBitmap
->GetPixel( nY
, nX
).GetBlue() + nDat
;
641 if ( nBlack
> nBlackMax
)
643 aBlack
[ nX
+ nY
* mpFileHeader
->nColumns
] = nDat
^ 0xff;
644 if ( ++nX
== mpFileHeader
->nColumns
)
648 if ( nY
== mpFileHeader
->nRows
)
655 for ( sal_uInt16 i
= 0; i
< ( ( nRunCount
& 0x7f ) + 1 ); i
++ )
657 m_rPSD
.ReadUChar( nDat
);
659 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
660 m_rPSD
.ReadUChar( nDummy
);
661 nBlack
= mpBitmap
->GetPixel( nY
, nX
).GetRed() + nDat
;
662 if ( nBlack
> nBlackMax
)
664 nBlack
= mpBitmap
->GetPixel( nY
, nX
).GetGreen() + nDat
;
665 if ( nBlack
> nBlackMax
)
667 nBlack
= mpBitmap
->GetPixel( nY
, nX
).GetBlue() + nDat
;
668 if ( nBlack
> nBlackMax
)
670 aBlack
[ nX
+ nY
* mpFileHeader
->nColumns
] = nDat
^ 0xff;
671 if ( ++nX
== mpFileHeader
->nColumns
)
675 if ( nY
== mpFileHeader
->nRows
)
682 for ( nY
= 0; nY
< mpFileHeader
->nRows
; nY
++ )
684 for ( nX
= 0; nX
< mpFileHeader
->nColumns
; nX
++ )
686 sal_Int32 nDAT
= aBlack
[ nX
+ nY
* mpFileHeader
->nColumns
] * ( nBlackMax
- 256 ) / 0x1ff;
688 aBitmapColor
= mpBitmap
->GetPixel( nY
, nX
);
689 sal_uInt8 cR
= static_cast<sal_uInt8
>(MinMax( aBitmapColor
.GetRed() - nDAT
, 0, 255L ));
690 sal_uInt8 cG
= static_cast<sal_uInt8
>(MinMax( aBitmapColor
.GetGreen() - nDAT
, 0, 255L ));
691 sal_uInt8 cB
= static_cast<sal_uInt8
>(MinMax( aBitmapColor
.GetBlue() - nDAT
, 0, 255L ));
692 mpBitmap
->SetPixel( nY
, nX
, Color( cR
, cG
, cB
) );
700 if (mbTransparent
&& m_rPSD
.good())
702 // the psd is 24 or 8 bit grafix + alphachannel
705 while ( nY
< mpFileHeader
->nRows
)
707 if ( mbCompression
) // else nRunCount = 0 -> so we use only single raw packets
710 m_rPSD
.ReadChar(nTmp
);
714 if ( nRunCount
& 0x80 ) // a run length packet
716 m_rPSD
.ReadUChar( nDat
);
721 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
722 m_rPSD
.ReadUChar( nDummy
);
723 for ( sal_uInt16 i
= 0; i
< ( -nRunCount
+ 1 ); i
++ )
725 mpBitmap
->SetAlpha(nY
, nX
, nDat
? 0 : 255);
726 if ( ++nX
== mpFileHeader
->nColumns
)
730 if ( nY
== mpFileHeader
->nRows
)
737 for ( sal_uInt16 i
= 0; i
< ( ( nRunCount
& 0x7f ) + 1 ); i
++ )
739 m_rPSD
.ReadUChar( nDat
);
744 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
745 m_rPSD
.ReadUChar( nDummy
);
746 mpBitmap
->SetAlpha(nY
, nX
, nDat
? 0 : 255);
747 if ( ++nX
== mpFileHeader
->nColumns
)
751 if ( nY
== mpFileHeader
->nRows
)
759 return m_rPSD
.good();
762 //================== GraphicImport - the exported function ================
764 extern "C" SAL_DLLPUBLIC_EXPORT
bool
765 ipdGraphicImport( SvStream
& rStream
, Graphic
& rGraphic
, FilterConfigItem
* )
767 PSDReader
aPSDReader(rStream
);
769 return aPSDReader
.ReadPSD(rGraphic
);
773 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */