37#define PSD_GRAYSCALE 1
41#define PSD_MULTICHANNEL 7
49 sal_uInt32 nSignature;
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;
78 std::unique_ptr<sal_uInt8[]>
82 bool ImplReadHeader();
85 explicit PSDReader(
SvStream &rStream);
86 bool ReadPSD(
Graphic & rGraphic);
93PSDReader::PSDReader(
SvStream &rStream)
98 , mbTransparent(false)
100 , mbCompression(false)
104bool 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 );
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() )
146 if ( mnXResFixed && mnYResFixed )
148 Fraction aFractX( 1, mnXResFixed >> 16 );
149 Fraction aFractY( 1, mnYResFixed >> 16 );
150 MapMode aMapMode( MapUnit::MapInch,
Point(), aFractX, aFractY );
162bool 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 )
233 m_rPSD.ReadBytes(mpPalette.get(), 768);
238 m_rPSD.SeekRel( nColorLength );
246 for ( sal_uInt16 i = 0;
i < 256;
i++ )
248 mpPalette[
i ] = mpPalette[
i + 256 ] = mpPalette[
i + 512 ] =
static_cast<sal_uInt8>(
i);
266 sal_uInt32 nResourceLength(0);
267 m_rPSD.ReadUInt32(nResourceLength);
268 if (nResourceLength > m_rPSD.remainingSize())
270 sal_uInt32 nLayerPos = m_rPSD.Tell() + nResourceLength;
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 );
285 sal_uInt32 nResEntryLen(0);
286 m_rPSD.ReadUInt32( nResEntryLen );
287 if ( nResEntryLen & 1 )
289 sal_uInt32 nCurrentPos = m_rPSD.Tell();
290 if (nCurrentPos > nLayerPos || nResEntryLen > (nLayerPos - nCurrentPos))
298 m_rPSD.ReadUInt32( mnXResFixed ).ReadInt16( nUnit ).ReadInt16( nUnit )
299 .ReadUInt32( mnYResFixed ).ReadInt16( nUnit ).ReadInt16( nUnit );
303 m_rPSD.Seek( nCurrentPos + nResEntryLen );
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());
343bool PSDReader::ImplReadBody()
346 signed char nRunCount = 0;
347 sal_uInt8 nDat = 0, nDummy, nRed, nGreen, nBlue;
351 switch ( mnDestBitDepth )
356 while (nY < mpFileHeader->nRows && m_rPSD.good())
363 m_rPSD.ReadChar(nTmp);
367 if ( nRunCount & 0x80 )
369 const sal_uInt16
nCount = -nRunCount + 1;
370 for (sal_uInt16 i = 0;
i <
nCount && m_rPSD.good(); ++
i)
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)
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())
422 m_rPSD.ReadChar(nTmp);
426 if ( nRunCount & 0x80 )
428 m_rPSD.ReadUChar( nDat );
429 if ( mpFileHeader->nDepth == 16 )
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 )
451 m_rPSD.ReadUChar( nDummy );
452 mpBitmap->SetPixel(nY, nX, SanitizePaletteIndex(mvPalette, nDat));
453 if ( ++nX == mpFileHeader->nColumns )
457 if ( nY == mpFileHeader->nRows )
472 while (nY < mpFileHeader->nRows && m_rPSD.good())
477 m_rPSD.ReadChar(nTmp);
481 if ( nRunCount & 0x80 )
483 m_rPSD.ReadUChar( nRed );
484 if ( mpFileHeader->nDepth == 16 )
485 m_rPSD.ReadUChar( nDummy );
486 const sal_uInt16
nCount = -nRunCount + 1;
487 for (sal_uInt16 i = 0;
i <
nCount && m_rPSD.good(); ++
i)
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 )
506 m_rPSD.ReadUChar( nDummy );
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 )
530 m_rPSD.ReadUChar( nGreen );
531 if ( mpFileHeader->nDepth == 16 )
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 );
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 )
554 m_rPSD.ReadUChar( nDummy );
555 aBitmapColor =
mpBitmap->GetPixel( nY, nX );
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 )
579 m_rPSD.ReadUChar( nBlue );
580 if ( mpFileHeader->nDepth == 16 )
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 );
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 )
603 m_rPSD.ReadUChar( nDummy );
604 aBitmapColor =
mpBitmap->GetPixel( nY, nX );
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())
626 m_rPSD.ReadChar(nTmp);
630 if ( nRunCount & 0x80 )
632 m_rPSD.ReadUChar( nDat );
634 if ( mpFileHeader->nDepth == 16 )
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 )
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 );
705 if (mbTransparent && m_rPSD.good())
710 while ( nY < mpFileHeader->nRows )
715 m_rPSD.ReadChar(nTmp);
719 if ( nRunCount & 0x80 )
721 m_rPSD.ReadUChar( nDat );
726 if ( mpFileHeader->nDepth == 16 )
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 )
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();
771 PSDReader aPSDReader(rStream);
772 return aPSDReader.ReadPSD(rGraphic);
sal_uInt8 GetBlue() const
sal_uInt8 GetGreen() const
void SetPrefMapMode(const MapMode &rPrefMapMode)
void SetPrefSize(const Size &rPrefSize)
SAL_WARN_UNUSED_RESULT Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
Intended to be used to feed into CreateFromData to create a BitmapEx.
std::enable_if< std::is_signed< T >::value||std::is_floating_point< T >::value, long >::type MinMax(T nVal, tools::Long nMin, tools::Long nMax)
bool ImportPsdGraphic(SvStream &rStream, Graphic &rGraphic)
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
std::enable_if< std::is_signed< T >::value, bool >::type checked_multiply(T a, T b, T &result)
BitmapEx CreateFromData(sal_uInt8 const *pData, sal_Int32 nWidth, sal_Int32 nHeight, sal_Int32 nStride, sal_Int8 nBitCount, bool bReversColors, bool bReverseAlpha)
Copy block of image data into the bitmap.