LibreOffice Module vcl (master)  1
ipsd.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
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>
28 #include <memory>
29 #include <filter/PsdReader.hxx>
30 
31 
32 class FilterConfigItem;
33 
34 //============================ PSDReader ==================================
35 
36 #define PSD_BITMAP 0
37 #define PSD_GRAYSCALE 1
38 #define PSD_INDEXED 2
39 #define PSD_RGB 3
40 #define PSD_CMYK 4
41 #define PSD_MULTICHANNEL 7
42 #define PSD_DUOTONE 8
43 #define PSD_LAB 9
44 
45 namespace {
46 
47 struct PSDFileHeader
48 {
49  sal_uInt32 nSignature;
50  sal_uInt16 nVersion;
51  sal_uInt32 nPad1;
52  sal_uInt16 nPad2;
53  sal_uInt16 nChannels;
54  sal_uInt32 nRows;
55  sal_uInt32 nColumns;
56  sal_uInt16 nDepth;
57  sal_uInt16 nMode;
58 };
59 
60 class PSDReader {
61 
62 private:
63 
64  SvStream& m_rPSD; // the PSD file to be read in
65  std::unique_ptr<PSDFileHeader>
66  mpFileHeader;
67 
68  sal_uInt32 mnXResFixed;
69  sal_uInt32 mnYResFixed;
70 
71  bool mbStatus;
72  bool mbTransparent;
73 
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[]>
79  mpPalette;
80 
81  bool ImplReadBody();
82  bool ImplReadHeader();
83 
84 public:
85  explicit PSDReader(SvStream &rStream);
86  bool ReadPSD(Graphic & rGraphic);
87 };
88 
89 }
90 
91 //=================== Methods of PSDReader ==============================
92 
93 PSDReader::PSDReader(SvStream &rStream)
94  : m_rPSD(rStream)
95  , mnXResFixed(0)
96  , mnYResFixed(0)
97  , mbStatus(true)
98  , mbTransparent(false)
99  , mnDestBitDepth(0)
100  , mbCompression(false)
101 {
102 }
103 
104 bool PSDReader::ReadPSD(Graphic & rGraphic )
105 {
106  if (m_rPSD.GetError())
107  return false;
108 
109  m_rPSD.SetEndian( SvStreamEndian::BIG );
110 
111  // read header:
112 
113  if ( !ImplReadHeader() )
114  return false;
115 
116  if (mbStatus)
117  {
118  sal_uInt32 nResult;
119  if (o3tl::checked_multiply(mpFileHeader->nColumns, mpFileHeader->nRows, nResult) || nResult > SAL_MAX_INT32/2/3)
120  return false;
121  }
122 
123  Size aBitmapSize( mpFileHeader->nColumns, mpFileHeader->nRows );
124  mpBitmap.reset( new vcl::bitmap::RawBitmap( aBitmapSize, mbTransparent ? 32 : 24 ) );
125  if ( mpPalette && mbStatus )
126  {
127  mvPalette.resize( 256 );
128  for ( sal_uInt16 i = 0; i < 256; i++ )
129  {
130  mvPalette[i] = Color( mpPalette[ i ], mpPalette[ i + 256 ], mpPalette[ i + 512 ] );
131  }
132  }
133 
134  if ((mnDestBitDepth == 1 || mnDestBitDepth == 8) && mvPalette.empty())
135  {
136  SAL_WARN("vcl", "no palette, but bit depth is " << mnDestBitDepth);
137  mbStatus = false;
138  return mbStatus;
139  }
140 
141  // read bitmap data
142  if ( mbStatus && ImplReadBody() )
143  {
144  rGraphic = Graphic( vcl::bitmap::CreateFromData( std::move(*mpBitmap) ) );
145 
146  if ( mnXResFixed && mnYResFixed )
147  {
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 ) );
154  }
155  }
156  else
157  mbStatus = false;
158  return mbStatus;
159 }
160 
161 
162 bool PSDReader::ImplReadHeader()
163 {
164  mpFileHeader.reset( new PSDFileHeader );
165 
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 );
167 
168  if (!m_rPSD.good())
169  return false;
170 
171  if ( ( mpFileHeader->nSignature != 0x38425053 ) || ( mpFileHeader->nVersion != 1 ) )
172  return false;
173 
174  if ( mpFileHeader->nRows == 0 || mpFileHeader->nColumns == 0 )
175  return false;
176 
177  if ( ( mpFileHeader->nRows > 30000 ) || ( mpFileHeader->nColumns > 30000 ) )
178  return false;
179 
180  sal_uInt16 nDepth = mpFileHeader->nDepth;
181  if (!( ( nDepth == 1 ) || ( nDepth == 8 ) || ( nDepth == 16 ) ) )
182  return false;
183 
184  mnDestBitDepth = ( nDepth == 16 ) ? 8 : nDepth;
185 
186  sal_uInt32 nColorLength(0);
187  m_rPSD.ReadUInt32( nColorLength );
188  if ( mpFileHeader->nMode == PSD_CMYK )
189  {
190  switch ( mpFileHeader->nChannels )
191  {
192  case 5 :
193  mbTransparent = true;
194  [[fallthrough]];
195  case 4 :
196  mnDestBitDepth = 24;
197  break;
198  default :
199  return false;
200  }
201  }
202  else switch ( mpFileHeader->nChannels )
203  {
204  case 2 :
205  mbTransparent = true;
206  break;
207  case 1 :
208  break;
209  case 4 :
210  mbTransparent = true;
211  [[fallthrough]];
212  case 3 :
213  mnDestBitDepth = 24;
214  break;
215  default:
216  return false;
217  }
218 
219  switch ( mpFileHeader->nMode )
220  {
221  case PSD_BITMAP :
222  {
223  if ( nColorLength || ( nDepth != 1 ) )
224  return false;
225  }
226  break;
227 
228  case PSD_INDEXED :
229  {
230  if ( nColorLength != 768 ) // we need the color map
231  return false;
232  mpPalette.reset( new sal_uInt8[ 768 ] );
233  m_rPSD.ReadBytes(mpPalette.get(), 768);
234  }
235  break;
236 
237  case PSD_DUOTONE : // we'll handle the duotone color like a normal grayscale picture
238  m_rPSD.SeekRel( nColorLength );
239  nColorLength = 0;
240  [[fallthrough]];
241  case PSD_GRAYSCALE :
242  {
243  if ( nColorLength )
244  return false;
245  mpPalette.reset( new sal_uInt8[ 768 ] );
246  for ( sal_uInt16 i = 0; i < 256; i++ )
247  {
248  mpPalette[ i ] = mpPalette[ i + 256 ] = mpPalette[ i + 512 ] = static_cast<sal_uInt8>(i);
249  }
250  }
251  break;
252 
253  case PSD_CMYK :
254  case PSD_RGB :
255  case PSD_MULTICHANNEL :
256  case PSD_LAB :
257  {
258  if ( nColorLength ) // color table is not supported by the other graphic modes
259  return false;
260  }
261  break;
262 
263  default:
264  return false;
265  }
266  sal_uInt32 nResourceLength(0);
267  m_rPSD.ReadUInt32(nResourceLength);
268  if (nResourceLength > m_rPSD.remainingSize())
269  return false;
270  sal_uInt32 nLayerPos = m_rPSD.Tell() + nResourceLength;
271 
272  // this is a loop over the resource entries to get the resolution info
273  while( m_rPSD.Tell() < nLayerPos )
274  {
275  sal_uInt32 nType(0);
276  sal_uInt16 nUniqueID(0);
277  sal_uInt8 n8(0);
278  m_rPSD.ReadUInt32(nType).ReadUInt16(nUniqueID).ReadUChar(n8);
279  if (nType != 0x3842494d)
280  break;
281  sal_uInt32 nPStringLen = n8;
282  if ( ! ( nPStringLen & 1 ) )
283  nPStringLen++;
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_uInt32 nCurrentPos = m_rPSD.Tell();
290  if (nCurrentPos > nLayerPos || nResEntryLen > (nLayerPos - nCurrentPos)) // check if size
291  break; // is possible
292  switch( nUniqueID )
293  {
294  case 0x3ed : // UID for the resolution info
295  {
296  sal_Int16 nUnit;
297 
298  m_rPSD.ReadUInt32( mnXResFixed ).ReadInt16( nUnit ).ReadInt16( nUnit )
299  .ReadUInt32( mnYResFixed ).ReadInt16( nUnit ).ReadInt16( nUnit );
300  }
301  break;
302  }
303  m_rPSD.Seek( nCurrentPos + nResEntryLen ); // set the stream to the next
304  } // resource entry
305  m_rPSD.Seek( nLayerPos );
306  sal_uInt32 nLayerMaskLength(0);
307  m_rPSD.ReadUInt32( nLayerMaskLength );
308  m_rPSD.SeekRel( nLayerMaskLength );
309 
310  sal_uInt16 nCompression(0);
311  m_rPSD.ReadUInt16(nCompression);
312  if ( nCompression == 0 )
313  {
314  mbCompression = false;
315  }
316  else if ( nCompression == 1 )
317  {
318  m_rPSD.SeekRel( ( mpFileHeader->nRows * mpFileHeader->nChannels ) << 1 );
319  mbCompression = true;
320  }
321  else
322  return false;
323 
324  return true;
325 }
326 
327 namespace
328 {
329  const Color& SanitizePaletteIndex(std::vector<Color> const & rvPalette, sal_uInt8 nIndex)
330  {
331  if (nIndex >= rvPalette.size())
332  {
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;
338  }
339  return rvPalette[nIndex];
340  }
341 }
342 
343 bool PSDReader::ImplReadBody()
344 {
345  sal_uInt32 nX, nY;
346  signed char nRunCount = 0;
347  sal_uInt8 nDat = 0, nDummy, nRed, nGreen, nBlue;
348  BitmapColor aBitmapColor;
349  nX = nY = 0;
350 
351  switch ( mnDestBitDepth )
352  {
353  case 1 :
354  {
355  signed char nBitCount = -1;
356  while (nY < mpFileHeader->nRows && m_rPSD.good())
357  {
358  if ( nBitCount == -1 )
359  {
360  if ( mbCompression ) // else nRunCount = 0 -> so we use only single raw packets
361  {
362  char nTmp(0);
363  m_rPSD.ReadChar(nTmp);
364  nRunCount = nTmp;
365  }
366  }
367  if ( nRunCount & 0x80 ) // a run length packet
368  {
369  const sal_uInt16 nCount = -nRunCount + 1;
370  for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
371  {
372  if ( nBitCount == -1 ) // bits left in nDat?
373  {
374  m_rPSD.ReadUChar( nDat );
375  nDat ^= 0xff;
376  nBitCount = 7;
377  }
378  mpBitmap->SetPixel(nY, nX, SanitizePaletteIndex(mvPalette, nDat >> nBitCount--));
379  if ( ++nX == mpFileHeader->nColumns )
380  {
381  nX = 0;
382  nY++;
383  nBitCount = -1;
384  if ( nY == mpFileHeader->nRows )
385  break;
386  }
387  }
388  }
389  else // a raw packet
390  {
391  const sal_uInt16 nCount = (nRunCount & 0x7f) + 1;
392  for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
393  {
394  if ( nBitCount == -1 ) // bits left in nDat ?
395  {
396  m_rPSD.ReadUChar( nDat );
397  nDat ^= 0xff;
398  nBitCount = 7;
399  }
400  mpBitmap->SetPixel(nY, nX, SanitizePaletteIndex(mvPalette, nDat >> nBitCount--));
401  if ( ++nX == mpFileHeader->nColumns )
402  {
403  nX = 0;
404  nY++;
405  nBitCount = -1;
406  if ( nY == mpFileHeader->nRows )
407  break;
408  }
409  }
410  }
411  }
412  }
413  break;
414 
415  case 8 :
416  {
417  while (nY < mpFileHeader->nRows && m_rPSD.good())
418  {
419  if ( mbCompression ) // else nRunCount = 0 -> so we use only single raw packets
420  {
421  char nTmp(0);
422  m_rPSD.ReadChar(nTmp);
423  nRunCount = nTmp;
424  }
425 
426  if ( nRunCount & 0x80 ) // a run length packet
427  {
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)
433  {
434  mpBitmap->SetPixel(nY, nX, SanitizePaletteIndex(mvPalette, nDat));
435  if ( ++nX == mpFileHeader->nColumns )
436  {
437  nX = 0;
438  nY++;
439  if ( nY == mpFileHeader->nRows )
440  break;
441  }
442  }
443  }
444  else // a raw packet
445  {
446  const sal_uInt16 nCount = (nRunCount & 0x7f) + 1;
447  for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
448  {
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 )
454  {
455  nX = 0;
456  nY++;
457  if ( nY == mpFileHeader->nRows )
458  break;
459  }
460  }
461  }
462  }
463  }
464  break;
465 
466  case 24 :
467  {
468 
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
471 
472  while (nY < mpFileHeader->nRows && m_rPSD.good())
473  {
474  if ( mbCompression ) // else nRunCount = 0 -> so we use only single raw packets
475  {
476  char nTmp(0);
477  m_rPSD.ReadChar(nTmp);
478  nRunCount = nTmp;
479  }
480 
481  if ( nRunCount & 0x80 ) // a run length packet
482  {
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)
488  {
489  mpBitmap->SetPixel( nY, nX, Color( nRed, sal_uInt8(0), sal_uInt8(0) ) );
490  if ( ++nX == mpFileHeader->nColumns )
491  {
492  nX = 0;
493  nY++;
494  if ( nY == mpFileHeader->nRows )
495  break;
496  }
497  }
498  }
499  else // a raw packet
500  {
501  const sal_uInt16 nCount = (nRunCount & 0x7f) + 1;
502  for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
503  {
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 )
509  {
510  nX = 0;
511  nY++;
512  if ( nY == mpFileHeader->nRows )
513  break;
514  }
515  }
516  }
517  }
518  nY = 0;
519  while (nY < mpFileHeader->nRows && m_rPSD.good())
520  {
521  if ( mbCompression )
522  {
523  char nTmp(0);
524  m_rPSD.ReadChar(nTmp);
525  nRunCount = nTmp;
526  }
527 
528  if ( nRunCount & 0x80 ) // a run length packet
529  {
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)
535  {
536  aBitmapColor = mpBitmap->GetPixel( nY, nX );
537  mpBitmap->SetPixel( nY, nX, Color( aBitmapColor.GetRed(), nGreen, aBitmapColor.GetBlue() ) );
538  if ( ++nX == mpFileHeader->nColumns )
539  {
540  nX = 0;
541  nY++;
542  if ( nY == mpFileHeader->nRows )
543  break;
544  }
545  }
546  }
547  else // a raw packet
548  {
549  const sal_uInt16 nCount = (nRunCount & 0x7f) + 1;
550  for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
551  {
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 )
558  {
559  nX = 0;
560  nY++;
561  if ( nY == mpFileHeader->nRows )
562  break;
563  }
564  }
565  }
566  }
567  nY = 0;
568  while (nY < mpFileHeader->nRows && m_rPSD.good())
569  {
570  if ( mbCompression )
571  {
572  char nTmp(0);
573  m_rPSD.ReadChar(nTmp);
574  nRunCount = nTmp;
575  }
576 
577  if ( nRunCount & 0x80 ) // a run length packet
578  {
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)
584  {
585  aBitmapColor = mpBitmap->GetPixel( nY, nX );
586  mpBitmap->SetPixel( nY, nX, Color( aBitmapColor.GetRed(), aBitmapColor.GetGreen(), nBlue ) );
587  if ( ++nX == mpFileHeader->nColumns )
588  {
589  nX = 0;
590  nY++;
591  if ( nY == mpFileHeader->nRows )
592  break;
593  }
594  }
595  }
596  else // a raw packet
597  {
598  const sal_uInt16 nCount = (nRunCount & 0x7f) + 1;
599  for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
600  {
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 )
607  {
608  nX = 0;
609  nY++;
610  if ( nY == mpFileHeader->nRows )
611  break;
612  }
613  }
614  }
615  }
616  if (mpFileHeader->nMode == PSD_CMYK && m_rPSD.good())
617  {
618  sal_uInt32 nBlack, nBlackMax = 0;
619  std::vector<sal_uInt8> aBlack(mpFileHeader->nRows * mpFileHeader->nColumns, 0);
620  nY = 0;
621  while (nY < mpFileHeader->nRows && m_rPSD.good())
622  {
623  if ( mbCompression ) // else nRunCount = 0 -> so we use only single raw packets
624  {
625  char nTmp(0);
626  m_rPSD.ReadChar(nTmp);
627  nRunCount = nTmp;
628  }
629 
630  if ( nRunCount & 0x80 ) // a run length packet
631  {
632  m_rPSD.ReadUChar( nDat );
633 
634  if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
635  m_rPSD.ReadUChar( nDummy );
636 
637  for ( sal_uInt16 i = 0; i < ( -nRunCount + 1 ); i++ )
638  {
639  nBlack = mpBitmap->GetPixel( nY, nX ).GetRed() + nDat;
640  if ( nBlack > nBlackMax )
641  nBlackMax = nBlack;
642  nBlack = mpBitmap->GetPixel( nY, nX ).GetGreen() + nDat;
643  if ( nBlack > nBlackMax )
644  nBlackMax = nBlack;
645  nBlack = mpBitmap->GetPixel( nY, nX ).GetBlue() + nDat;
646  if ( nBlack > nBlackMax )
647  nBlackMax = nBlack;
648  aBlack[ nX + nY * mpFileHeader->nColumns ] = nDat ^ 0xff;
649  if ( ++nX == mpFileHeader->nColumns )
650  {
651  nX = 0;
652  nY++;
653  if ( nY == mpFileHeader->nRows )
654  break;
655  }
656  }
657  }
658  else // a raw packet
659  {
660  for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
661  {
662  m_rPSD.ReadUChar( nDat );
663 
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 )
668  nBlackMax = nBlack;
669  nBlack = mpBitmap->GetPixel( nY, nX ).GetGreen() + nDat;
670  if ( nBlack > nBlackMax )
671  nBlackMax = nBlack;
672  nBlack = mpBitmap->GetPixel( nY, nX ).GetBlue() + nDat;
673  if ( nBlack > nBlackMax )
674  nBlackMax = nBlack;
675  aBlack[ nX + nY * mpFileHeader->nColumns ] = nDat ^ 0xff;
676  if ( ++nX == mpFileHeader->nColumns )
677  {
678  nX = 0;
679  nY++;
680  if ( nY == mpFileHeader->nRows )
681  break;
682  }
683  }
684  }
685  }
686 
687  for ( nY = 0; nY < mpFileHeader->nRows; nY++ )
688  {
689  for ( nX = 0; nX < mpFileHeader->nColumns; nX++ )
690  {
691  sal_Int32 nDAT = aBlack[ nX + nY * mpFileHeader->nColumns ] * ( nBlackMax - 256 ) / 0x1ff;
692 
693  aBitmapColor = mpBitmap->GetPixel( nY, nX );
694  sal_uInt8 cR = static_cast<sal_uInt8>(MinMax( aBitmapColor.GetRed() - nDAT, 0, 255L ));
695  sal_uInt8 cG = static_cast<sal_uInt8>(MinMax( aBitmapColor.GetGreen() - nDAT, 0, 255L ));
696  sal_uInt8 cB = static_cast<sal_uInt8>(MinMax( aBitmapColor.GetBlue() - nDAT, 0, 255L ));
697  mpBitmap->SetPixel( nY, nX, Color( cR, cG, cB ) );
698  }
699  }
700  }
701  }
702  break;
703  }
704 
705  if (mbTransparent && m_rPSD.good())
706  {
707  // the psd is 24 or 8 bit grafix + alphachannel
708 
709  nY = nX = 0;
710  while ( nY < mpFileHeader->nRows )
711  {
712  if ( mbCompression ) // else nRunCount = 0 -> so we use only single raw packets
713  {
714  char nTmp(0);
715  m_rPSD.ReadChar(nTmp);
716  nRunCount = nTmp;
717  }
718 
719  if ( nRunCount & 0x80 ) // a run length packet
720  {
721  m_rPSD.ReadUChar( nDat );
722  if ( nDat )
723  nDat = 0;
724  else
725  nDat = 1;
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++ )
729  {
730  mpBitmap->SetAlpha(nY, nX, nDat ? 0 : 255);
731  if ( ++nX == mpFileHeader->nColumns )
732  {
733  nX = 0;
734  nY++;
735  if ( nY == mpFileHeader->nRows )
736  break;
737  }
738  }
739  }
740  else // a raw packet
741  {
742  for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
743  {
744  m_rPSD.ReadUChar( nDat );
745  if ( nDat )
746  nDat = 0;
747  else
748  nDat = 1;
749  if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
750  m_rPSD.ReadUChar( nDummy );
751  mpBitmap->SetAlpha(nY, nX, nDat ? 0 : 255);
752  if ( ++nX == mpFileHeader->nColumns )
753  {
754  nX = 0;
755  nY++;
756  if ( nY == mpFileHeader->nRows )
757  break;
758  }
759  }
760  }
761  }
762  }
763 
764  return m_rPSD.good();
765 }
766 
767 //================== GraphicImport - the exported function ================
768 
769 bool ImportPsdGraphic(SvStream& rStream, Graphic& rGraphic)
770 {
771  PSDReader aPSDReader(rStream);
772  return aPSDReader.ReadPSD(rGraphic);
773 }
774 
775 
776 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_uInt8 GetRed() const
SAL_WARN_UNUSED_RESULT Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
Definition: map.cxx:1564
void SetPrefMapMode(const MapMode &rPrefMapMode)
Definition: graph.cxx:379
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)
FPDF_BITMAP mpBitmap
#define PSD_GRAYSCALE
Definition: ipsd.cxx:37
int nCount
sal_uInt8 GetBlue() const
bool ImportPsdGraphic(SvStream &rStream, Graphic &rGraphic)
Definition: ipsd.cxx:769
#define PSD_DUOTONE
Definition: ipsd.cxx:42
int i
#define PSD_BITMAP
Definition: ipsd.cxx:36
std::enable_if< std::is_signed< T >::value, bool >::type checked_multiply(T a, T b, T &result)
sal_Int16 nVersion
Intended to be used to feed into CreateFromData to create a BitmapEx.
Definition: RawBitmap.hxx:21
short nBitCount
Definition: ipict.cxx:80
sal_uInt8 GetGreen() const
#define PSD_INDEXED
Definition: ipsd.cxx:38
#define SAL_WARN_IF(condition, area, stream)
unsigned char sal_uInt8
QPRO_FUNC_TYPE nType
BitmapEx CreateFromData(sal_uInt8 const *pData, sal_Int32 nWidth, sal_Int32 nHeight, sal_Int32 nStride, vcl::PixelFormat ePixelFormat, bool bReversColors, bool bReverseAlpha)
Copy block of image data into the bitmap.
void SetPrefSize(const Size &rPrefSize)
Definition: graph.cxx:368
#define SAL_WARN(area, stream)
#define PSD_RGB
Definition: ipsd.cxx:39
#define PSD_LAB
Definition: ipsd.cxx:43
#define PSD_CMYK
Definition: ipsd.cxx:40
#define PSD_MULTICHANNEL
Definition: ipsd.cxx:41