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>
29 #include <filter/PsdReader.hxx>
32 class FilterConfigItem
;
34 //============================ PSDReader ==================================
37 #define PSD_GRAYSCALE 1
41 #define PSD_MULTICHANNEL 7
49 sal_uInt32 nSignature
;
64 SvStream
& m_rPSD
; // the PSD file to be read in
65 std::unique_ptr
<PSDFileHeader
>
68 sal_uInt32 mnXResFixed
;
69 sal_uInt32 mnYResFixed
;
74 std::unique_ptr
<vcl::bitmap::RawBitmap
> mpBitmap
;
75 std::vector
<Color
> mvPalette
;
76 sal_uInt16 mnDestBitDepth
;
77 bool mbCompression
; // RLE decoding
78 std::unique_ptr
<sal_uInt8
[]>
82 bool ImplReadHeader();
85 explicit PSDReader(SvStream
&rStream
);
86 bool ReadPSD(Graphic
& rGraphic
);
91 //=================== Methods of PSDReader ==============================
93 PSDReader::PSDReader(SvStream
&rStream
)
98 , mbTransparent(false)
100 , mbCompression(false)
104 bool PSDReader::ReadPSD(Graphic
& rGraphic
)
106 if (m_rPSD
.GetError())
109 m_rPSD
.SetEndian( SvStreamEndian::BIG
);
113 if ( !ImplReadHeader() )
119 if (o3tl::checked_multiply(mpFileHeader
->nColumns
, mpFileHeader
->nRows
, nResult
) || nResult
> SAL_MAX_INT32
/2/3)
123 Size
aBitmapSize( mpFileHeader
->nColumns
, mpFileHeader
->nRows
);
124 mpBitmap
.reset( new vcl::bitmap::RawBitmap( aBitmapSize
, mbTransparent
? 32 : 24 ) );
125 if ( mpPalette
&& mbStatus
)
127 mvPalette
.resize( 256 );
128 for ( sal_uInt16 i
= 0; i
< 256; i
++ )
130 mvPalette
[i
] = Color( mpPalette
[ i
], mpPalette
[ i
+ 256 ], mpPalette
[ i
+ 512 ] );
134 if ((mnDestBitDepth
== 1 || mnDestBitDepth
== 8) && mvPalette
.empty())
136 SAL_WARN("vcl", "no palette, but bit depth is " << mnDestBitDepth
);
142 if ( mbStatus
&& ImplReadBody() )
144 rGraphic
= Graphic( vcl::bitmap::CreateFromData( std::move(*mpBitmap
) ) );
146 if ( mnXResFixed
&& mnYResFixed
)
148 Fraction
aFractX( 1, mnXResFixed
>> 16 );
149 Fraction
aFractY( 1, mnYResFixed
>> 16 );
150 MapMode
aMapMode( MapUnit::MapInch
, Point(), aFractX
, aFractY
);
151 Size aPrefSize
= OutputDevice::LogicToLogic(aBitmapSize
, aMapMode
, MapMode(MapUnit::Map100thMM
));
152 rGraphic
.SetPrefSize( aPrefSize
);
153 rGraphic
.SetPrefMapMode( MapMode( MapUnit::Map100thMM
) );
162 bool PSDReader::ImplReadHeader()
164 mpFileHeader
.reset( new PSDFileHeader
);
166 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
);
171 if ( ( mpFileHeader
->nSignature
!= 0x38425053 ) || ( mpFileHeader
->nVersion
!= 1 ) )
174 if ( mpFileHeader
->nRows
== 0 || mpFileHeader
->nColumns
== 0 )
177 if ( ( mpFileHeader
->nRows
> 30000 ) || ( mpFileHeader
->nColumns
> 30000 ) )
180 sal_uInt16 nDepth
= mpFileHeader
->nDepth
;
181 if (!( ( nDepth
== 1 ) || ( nDepth
== 8 ) || ( nDepth
== 16 ) ) )
184 mnDestBitDepth
= ( nDepth
== 16 ) ? 8 : nDepth
;
186 sal_uInt32
nColorLength(0);
187 m_rPSD
.ReadUInt32( nColorLength
);
188 if ( mpFileHeader
->nMode
== PSD_CMYK
)
190 switch ( mpFileHeader
->nChannels
)
193 mbTransparent
= true;
202 else switch ( mpFileHeader
->nChannels
)
205 mbTransparent
= true;
210 mbTransparent
= true;
219 switch ( mpFileHeader
->nMode
)
223 if ( nColorLength
|| ( nDepth
!= 1 ) )
230 if ( nColorLength
!= 768 ) // we need the color map
232 mpPalette
.reset( new sal_uInt8
[ 768 ] );
233 m_rPSD
.ReadBytes(mpPalette
.get(), 768);
237 case PSD_DUOTONE
: // we'll handle the duotone color like a normal grayscale picture
238 m_rPSD
.SeekRel( nColorLength
);
245 mpPalette
.reset( new sal_uInt8
[ 768 ] );
246 for ( sal_uInt16 i
= 0; i
< 256; i
++ )
248 mpPalette
[ i
] = mpPalette
[ i
+ 256 ] = mpPalette
[ i
+ 512 ] = static_cast<sal_uInt8
>(i
);
255 case PSD_MULTICHANNEL
:
258 if ( nColorLength
) // color table is not supported by the other graphic modes
266 sal_uInt32
nResourceLength(0);
267 m_rPSD
.ReadUInt32(nResourceLength
);
268 if (nResourceLength
> m_rPSD
.remainingSize())
270 sal_uInt64 nLayerPos
= m_rPSD
.Tell() + nResourceLength
;
272 // this is a loop over the resource entries to get the resolution info
273 while( m_rPSD
.Tell() < nLayerPos
)
276 sal_uInt16
nUniqueID(0);
278 m_rPSD
.ReadUInt32(nType
).ReadUInt16(nUniqueID
).ReadUChar(n8
);
279 if (nType
!= 0x3842494d)
281 sal_uInt32 nPStringLen
= n8
;
282 if ( ! ( nPStringLen
& 1 ) )
284 m_rPSD
.SeekRel( nPStringLen
); // skipping the pstring
285 sal_uInt32
nResEntryLen(0);
286 m_rPSD
.ReadUInt32( nResEntryLen
);
287 if ( nResEntryLen
& 1 )
288 nResEntryLen
++; // the resource entries are padded
289 sal_uInt64 nCurrentPos
= m_rPSD
.Tell();
290 if (nCurrentPos
> nLayerPos
|| nResEntryLen
> (nLayerPos
- nCurrentPos
)) // check if size
291 break; // is possible
294 case 0x3ed : // UID for the resolution info
298 m_rPSD
.ReadUInt32( mnXResFixed
).ReadInt16( nUnit
).ReadInt16( nUnit
)
299 .ReadUInt32( mnYResFixed
).ReadInt16( nUnit
).ReadInt16( nUnit
);
303 m_rPSD
.Seek( nCurrentPos
+ nResEntryLen
); // set the stream to the next
305 m_rPSD
.Seek( nLayerPos
);
306 sal_uInt32
nLayerMaskLength(0);
307 m_rPSD
.ReadUInt32( nLayerMaskLength
);
308 m_rPSD
.SeekRel( nLayerMaskLength
);
310 sal_uInt16
nCompression(0);
311 m_rPSD
.ReadUInt16(nCompression
);
312 if ( nCompression
== 0 )
314 mbCompression
= false;
316 else if ( nCompression
== 1 )
318 m_rPSD
.SeekRel( ( mpFileHeader
->nRows
* mpFileHeader
->nChannels
) << 1 );
319 mbCompression
= true;
329 const Color
& SanitizePaletteIndex(std::vector
<Color
> const & rvPalette
, sal_uInt8 nIndex
)
331 if (nIndex
>= rvPalette
.size())
333 auto nSanitizedIndex
= nIndex
% rvPalette
.size();
334 SAL_WARN_IF(nIndex
!= nSanitizedIndex
, "filter.psd", "invalid colormap index: "
335 << static_cast<unsigned int>(nIndex
) << ", colormap len is: "
336 << rvPalette
.size());
337 nIndex
= nSanitizedIndex
;
339 return rvPalette
[nIndex
];
343 bool PSDReader::ImplReadBody()
346 signed char nRunCount
= 0;
347 sal_uInt8 nDat
= 0, nDummy
, nRed
, nGreen
, nBlue
;
348 BitmapColor aBitmapColor
;
351 switch ( mnDestBitDepth
)
355 signed char nBitCount
= -1;
356 while (nY
< mpFileHeader
->nRows
&& m_rPSD
.good())
358 if ( nBitCount
== -1 )
360 if ( mbCompression
) // else nRunCount = 0 -> so we use only single raw packets
363 m_rPSD
.ReadChar(nTmp
);
367 if ( nRunCount
& 0x80 ) // a run length packet
369 const sal_uInt16 nCount
= -nRunCount
+ 1;
370 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
372 if ( nBitCount
== -1 ) // bits left in nDat?
374 m_rPSD
.ReadUChar( nDat
);
378 mpBitmap
->SetPixel(nY
, nX
, SanitizePaletteIndex(mvPalette
, nDat
>> nBitCount
--));
379 if ( ++nX
== mpFileHeader
->nColumns
)
384 if ( nY
== mpFileHeader
->nRows
)
391 const sal_uInt16 nCount
= (nRunCount
& 0x7f) + 1;
392 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
394 if ( nBitCount
== -1 ) // bits left in nDat ?
396 m_rPSD
.ReadUChar( nDat
);
400 mpBitmap
->SetPixel(nY
, nX
, SanitizePaletteIndex(mvPalette
, nDat
>> nBitCount
--));
401 if ( ++nX
== mpFileHeader
->nColumns
)
406 if ( nY
== mpFileHeader
->nRows
)
417 while (nY
< mpFileHeader
->nRows
&& m_rPSD
.good())
419 if ( mbCompression
) // else nRunCount = 0 -> so we use only single raw packets
422 m_rPSD
.ReadChar(nTmp
);
426 if ( nRunCount
& 0x80 ) // a run length packet
428 m_rPSD
.ReadUChar( nDat
);
429 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
430 m_rPSD
.ReadUChar( nDummy
);
431 const sal_uInt16 nCount
= -nRunCount
+ 1;
432 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
434 mpBitmap
->SetPixel(nY
, nX
, SanitizePaletteIndex(mvPalette
, nDat
));
435 if ( ++nX
== mpFileHeader
->nColumns
)
439 if ( nY
== mpFileHeader
->nRows
)
446 const sal_uInt16 nCount
= (nRunCount
& 0x7f) + 1;
447 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
449 m_rPSD
.ReadUChar( nDat
);
450 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
451 m_rPSD
.ReadUChar( nDummy
);
452 mpBitmap
->SetPixel(nY
, nX
, SanitizePaletteIndex(mvPalette
, nDat
));
453 if ( ++nX
== mpFileHeader
->nColumns
)
457 if ( nY
== mpFileHeader
->nRows
)
469 // the psd format is in plain order (RRRR GGGG BBBB) so we have to set each pixel three times
470 // maybe the format is CCCC MMMM YYYY KKKK
472 while (nY
< mpFileHeader
->nRows
&& m_rPSD
.good())
474 if ( mbCompression
) // else nRunCount = 0 -> so we use only single raw packets
477 m_rPSD
.ReadChar(nTmp
);
481 if ( nRunCount
& 0x80 ) // a run length packet
483 m_rPSD
.ReadUChar( nRed
);
484 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
485 m_rPSD
.ReadUChar( nDummy
);
486 const sal_uInt16 nCount
= -nRunCount
+ 1;
487 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
489 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, sal_uInt8(0), sal_uInt8(0) ) );
490 if ( ++nX
== mpFileHeader
->nColumns
)
494 if ( nY
== mpFileHeader
->nRows
)
501 const sal_uInt16 nCount
= (nRunCount
& 0x7f) + 1;
502 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
504 m_rPSD
.ReadUChar( nRed
);
505 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
506 m_rPSD
.ReadUChar( nDummy
);
507 mpBitmap
->SetPixel( nY
, nX
, Color( nRed
, sal_uInt8(0), sal_uInt8(0) ) );
508 if ( ++nX
== mpFileHeader
->nColumns
)
512 if ( nY
== mpFileHeader
->nRows
)
519 while (nY
< mpFileHeader
->nRows
&& m_rPSD
.good())
524 m_rPSD
.ReadChar(nTmp
);
528 if ( nRunCount
& 0x80 ) // a run length packet
530 m_rPSD
.ReadUChar( nGreen
);
531 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
532 m_rPSD
.ReadUChar( nDummy
);
533 const sal_uInt16 nCount
= -nRunCount
+ 1;
534 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
536 aBitmapColor
= mpBitmap
->GetPixel( nY
, nX
);
537 mpBitmap
->SetPixel( nY
, nX
, Color( aBitmapColor
.GetRed(), nGreen
, aBitmapColor
.GetBlue() ) );
538 if ( ++nX
== mpFileHeader
->nColumns
)
542 if ( nY
== mpFileHeader
->nRows
)
549 const sal_uInt16 nCount
= (nRunCount
& 0x7f) + 1;
550 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
552 m_rPSD
.ReadUChar( nGreen
);
553 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
554 m_rPSD
.ReadUChar( nDummy
);
555 aBitmapColor
= mpBitmap
->GetPixel( nY
, nX
);
556 mpBitmap
->SetPixel( nY
, nX
, Color( aBitmapColor
.GetRed(), nGreen
, aBitmapColor
.GetBlue() ) );
557 if ( ++nX
== mpFileHeader
->nColumns
)
561 if ( nY
== mpFileHeader
->nRows
)
568 while (nY
< mpFileHeader
->nRows
&& m_rPSD
.good())
573 m_rPSD
.ReadChar(nTmp
);
577 if ( nRunCount
& 0x80 ) // a run length packet
579 m_rPSD
.ReadUChar( nBlue
);
580 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
581 m_rPSD
.ReadUChar( nDummy
);
582 const sal_uInt16 nCount
= -nRunCount
+ 1;
583 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
585 aBitmapColor
= mpBitmap
->GetPixel( nY
, nX
);
586 mpBitmap
->SetPixel( nY
, nX
, Color( aBitmapColor
.GetRed(), aBitmapColor
.GetGreen(), nBlue
) );
587 if ( ++nX
== mpFileHeader
->nColumns
)
591 if ( nY
== mpFileHeader
->nRows
)
598 const sal_uInt16 nCount
= (nRunCount
& 0x7f) + 1;
599 for (sal_uInt16 i
= 0; i
< nCount
&& m_rPSD
.good(); ++i
)
601 m_rPSD
.ReadUChar( nBlue
);
602 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
603 m_rPSD
.ReadUChar( nDummy
);
604 aBitmapColor
= mpBitmap
->GetPixel( nY
, nX
);
605 mpBitmap
->SetPixel( nY
, nX
, Color( aBitmapColor
.GetRed(), aBitmapColor
.GetGreen(), nBlue
) );
606 if ( ++nX
== mpFileHeader
->nColumns
)
610 if ( nY
== mpFileHeader
->nRows
)
616 if (mpFileHeader
->nMode
== PSD_CMYK
&& m_rPSD
.good())
618 sal_uInt32 nBlack
, nBlackMax
= 0;
619 std::vector
<sal_uInt8
> aBlack(mpFileHeader
->nRows
* mpFileHeader
->nColumns
, 0);
621 while (nY
< mpFileHeader
->nRows
&& m_rPSD
.good())
623 if ( mbCompression
) // else nRunCount = 0 -> so we use only single raw packets
626 m_rPSD
.ReadChar(nTmp
);
630 if ( nRunCount
& 0x80 ) // a run length packet
632 m_rPSD
.ReadUChar( nDat
);
634 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
635 m_rPSD
.ReadUChar( nDummy
);
637 for ( sal_uInt16 i
= 0; i
< ( -nRunCount
+ 1 ); i
++ )
639 nBlack
= mpBitmap
->GetPixel( nY
, nX
).GetRed() + nDat
;
640 if ( nBlack
> nBlackMax
)
642 nBlack
= mpBitmap
->GetPixel( nY
, nX
).GetGreen() + nDat
;
643 if ( nBlack
> nBlackMax
)
645 nBlack
= mpBitmap
->GetPixel( nY
, nX
).GetBlue() + nDat
;
646 if ( nBlack
> nBlackMax
)
648 aBlack
[ nX
+ nY
* mpFileHeader
->nColumns
] = nDat
^ 0xff;
649 if ( ++nX
== mpFileHeader
->nColumns
)
653 if ( nY
== mpFileHeader
->nRows
)
660 for ( sal_uInt16 i
= 0; i
< ( ( nRunCount
& 0x7f ) + 1 ); i
++ )
662 m_rPSD
.ReadUChar( nDat
);
664 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
665 m_rPSD
.ReadUChar( nDummy
);
666 nBlack
= mpBitmap
->GetPixel( nY
, nX
).GetRed() + nDat
;
667 if ( nBlack
> nBlackMax
)
669 nBlack
= mpBitmap
->GetPixel( nY
, nX
).GetGreen() + nDat
;
670 if ( nBlack
> nBlackMax
)
672 nBlack
= mpBitmap
->GetPixel( nY
, nX
).GetBlue() + nDat
;
673 if ( nBlack
> nBlackMax
)
675 aBlack
[ nX
+ nY
* mpFileHeader
->nColumns
] = nDat
^ 0xff;
676 if ( ++nX
== mpFileHeader
->nColumns
)
680 if ( nY
== mpFileHeader
->nRows
)
687 for ( nY
= 0; nY
< mpFileHeader
->nRows
; nY
++ )
689 for ( nX
= 0; nX
< mpFileHeader
->nColumns
; nX
++ )
691 sal_Int32 nDAT
= aBlack
[ nX
+ nY
* mpFileHeader
->nColumns
] * ( nBlackMax
- 256 ) / 0x1ff;
693 aBitmapColor
= mpBitmap
->GetPixel( nY
, nX
);
694 sal_uInt8 cR
= static_cast<sal_uInt8
>(std::clamp( aBitmapColor
.GetRed() - nDAT
, sal_Int32(0), sal_Int32(255) ));
695 sal_uInt8 cG
= static_cast<sal_uInt8
>(std::clamp( aBitmapColor
.GetGreen() - nDAT
, sal_Int32(0), sal_Int32(255) ));
696 sal_uInt8 cB
= static_cast<sal_uInt8
>(std::clamp( aBitmapColor
.GetBlue() - nDAT
, sal_Int32(0), sal_Int32(255) ));
697 mpBitmap
->SetPixel( nY
, nX
, Color( cR
, cG
, cB
) );
705 if (mbTransparent
&& m_rPSD
.good())
707 // the psd is 24 or 8 bit grafix + alpha channel
710 while ( nY
< mpFileHeader
->nRows
)
712 if ( mbCompression
) // else nRunCount = 0 -> so we use only single raw packets
715 m_rPSD
.ReadChar(nTmp
);
719 if ( nRunCount
& 0x80 ) // a run length packet
721 m_rPSD
.ReadUChar( nDat
);
726 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
727 m_rPSD
.ReadUChar( nDummy
);
728 for ( sal_uInt16 i
= 0; i
< ( -nRunCount
+ 1 ); i
++ )
730 mpBitmap
->SetAlpha(nY
, nX
, nDat
? 255 : 0);
731 if ( ++nX
== mpFileHeader
->nColumns
)
735 if ( nY
== mpFileHeader
->nRows
)
742 for ( sal_uInt16 i
= 0; i
< ( ( nRunCount
& 0x7f ) + 1 ); i
++ )
744 m_rPSD
.ReadUChar( nDat
);
749 if ( mpFileHeader
->nDepth
== 16 ) // 16 bit depth is to be skipped
750 m_rPSD
.ReadUChar( nDummy
);
751 mpBitmap
->SetAlpha(nY
, nX
, nDat
? 255 : 0);
752 if ( ++nX
== mpFileHeader
->nColumns
)
756 if ( nY
== mpFileHeader
->nRows
)
764 return m_rPSD
.good();
767 //================== GraphicImport - the exported function ================
769 bool ImportPsdGraphic(SvStream
& rStream
, Graphic
& rGraphic
)
771 PSDReader
aPSDReader(rStream
);
772 return aPSDReader
.ReadPSD(rGraphic
);
776 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */