update emoji autocorrect entries from po-files
[LibreOffice.git] / vcl / source / gdi / pngwrite.cxx
blobd3ee25fbeff9d28d8455afd1b53486247f89eccc
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <vcl/pngwrite.hxx>
22 #include <cmath>
23 #include <limits>
24 #include <rtl/crc.h>
25 #include <rtl/alloc.h>
26 #include <tools/zcodec.hxx>
27 #include <tools/stream.hxx>
28 #include <vcl/bmpacc.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/alpha.hxx>
31 #include <osl/endian.h>
32 #include <boost/scoped_array.hpp>
34 #define PNG_DEF_COMPRESSION 6
36 #define PNGCHUNK_IHDR 0x49484452
37 #define PNGCHUNK_PLTE 0x504c5445
38 #define PNGCHUNK_IDAT 0x49444154
39 #define PNGCHUNK_IEND 0x49454e44
40 #define PNGCHUNK_pHYs 0x70485973
41 #define PNGCHUNK_tRNS 0x74524e53
43 namespace vcl
46 class PNGWriterImpl
48 public:
50 PNGWriterImpl(const BitmapEx& BmpEx,
51 const css::uno::Sequence<css::beans::PropertyValue>* pFilterData = NULL);
53 bool Write(SvStream& rOutStream);
55 std::vector<vcl::PNGWriter::ChunkData>& GetChunks()
57 return maChunkSeq;
60 private:
62 std::vector<vcl::PNGWriter::ChunkData> maChunkSeq;
64 sal_Int32 mnCompLevel;
65 sal_Int32 mnInterlaced;
66 sal_uInt32 mnMaxChunkSize;
67 bool mbStatus;
69 BitmapReadAccess* mpAccess;
70 BitmapReadAccess* mpMaskAccess;
71 ZCodec mpZCodec;
73 sal_uInt8* mpDeflateInBuf; // as big as the size of a scanline + alphachannel + 1
74 sal_uInt8* mpPreviousScan; // as big as mpDeflateInBuf
75 sal_uInt8* mpCurrentScan;
76 sal_uLong mnDeflateInSize;
78 sal_uLong mnWidth;
79 sal_uLong mnHeight;
80 sal_uInt8 mnBitsPerPixel;
81 sal_uInt8 mnFilterType; // 0 oder 4;
82 sal_uLong mnBBP; // bytes per pixel ( needed for filtering )
83 bool mbTrueAlpha;
84 sal_uLong mnCRC;
86 void ImplWritepHYs(const BitmapEx& rBitmapEx);
87 void ImplWriteIDAT();
88 sal_uLong ImplGetFilter(sal_uLong nY, sal_uLong nXStart = 0, sal_uLong nXAdd = 1);
89 void ImplClearFirstScanline();
90 void ImplWriteTransparent();
91 bool ImplWriteHeader();
92 void ImplWritePalette();
93 void ImplOpenChunk(sal_uLong nChunkType);
94 void ImplWriteChunk(sal_uInt8 nNumb);
95 void ImplWriteChunk(sal_uInt32 nNumb);
96 void ImplWriteChunk(unsigned char* pSource, sal_uInt32 nDatSize);
99 PNGWriterImpl::PNGWriterImpl( const BitmapEx& rBmpEx,
100 const css::uno::Sequence<css::beans::PropertyValue>* pFilterData )
101 : mnCompLevel(PNG_DEF_COMPRESSION)
102 , mnInterlaced(0)
103 , mnMaxChunkSize(0)
104 , mbStatus(true)
105 , mpAccess(NULL)
106 , mpMaskAccess(NULL)
107 , mpDeflateInBuf(NULL)
108 , mpPreviousScan(NULL)
109 , mpCurrentScan(NULL)
110 , mnDeflateInSize(0)
111 , mnWidth(0)
112 , mnHeight(0)
113 , mnBitsPerPixel(0)
114 , mnFilterType(0)
115 , mnBBP(0)
116 , mbTrueAlpha(false)
117 , mnCRC(0UL)
119 if (!rBmpEx.IsEmpty())
121 Bitmap aBmp(rBmpEx.GetBitmap());
123 mnInterlaced = 0; // ( aBmp.GetSizePixel().Width() > 128 ) || ( aBmp.GetSizePixel().Height() > 128 ) ? 1 : 0; #i67236#
125 // #i67234# defaulting max chunk size to 256kb when using interlace mode
126 mnMaxChunkSize = mnInterlaced == 0 ? std::numeric_limits<sal_uInt32>::max() : 0x40000;
128 if (pFilterData)
130 sal_Int32 i = 0;
131 for (i = 0; i < pFilterData->getLength(); i++)
133 if ((*pFilterData)[i].Name == "Compression")
134 (*pFilterData)[i].Value >>= mnCompLevel;
135 else if ((*pFilterData)[i].Name == "Interlaced")
136 (*pFilterData)[i].Value >>= mnInterlaced;
137 else if ((*pFilterData)[i].Name == "MaxChunkSize")
139 sal_Int32 nVal = 0;
140 if ((*pFilterData)[i].Value >>= nVal)
141 mnMaxChunkSize = static_cast<sal_uInt32>(nVal);
145 mnBitsPerPixel = static_cast<sal_uInt8>(aBmp.GetBitCount());
147 if (rBmpEx.IsTransparent())
149 if (mnBitsPerPixel <= 8 && rBmpEx.IsAlpha())
151 aBmp.Convert( BMP_CONVERSION_24BIT );
152 mnBitsPerPixel = 24;
155 if (mnBitsPerPixel <= 8) // transparent palette
157 aBmp.Convert(BMP_CONVERSION_8BIT_TRANS);
158 aBmp.Replace(rBmpEx.GetMask(), BMP_COL_TRANS);
159 mnBitsPerPixel = 8;
160 mpAccess = aBmp.AcquireReadAccess();
161 if (mpAccess)
163 if (ImplWriteHeader())
165 ImplWritepHYs(rBmpEx);
166 ImplWritePalette();
167 ImplWriteTransparent();
168 ImplWriteIDAT();
170 Bitmap::ReleaseAccess(mpAccess);
171 mpAccess = NULL;
173 else
175 mbStatus = false;
178 else
180 mpAccess = aBmp.AcquireReadAccess(); // true RGB with alphachannel
181 if (mpAccess)
183 if ((mbTrueAlpha = rBmpEx.IsAlpha()))
185 AlphaMask aMask(rBmpEx.GetAlpha());
186 mpMaskAccess = aMask.AcquireReadAccess();
187 if (mpMaskAccess)
189 if (ImplWriteHeader())
191 ImplWritepHYs(rBmpEx);
192 ImplWriteIDAT();
194 aMask.ReleaseAccess(mpMaskAccess);
195 mpMaskAccess = NULL;
197 else
199 mbStatus = false;
202 else
204 Bitmap aMask(rBmpEx.GetMask());
205 mpMaskAccess = aMask.AcquireReadAccess();
206 if (mpMaskAccess)
208 if (ImplWriteHeader())
210 ImplWritepHYs(rBmpEx);
211 ImplWriteIDAT();
213 Bitmap::ReleaseAccess(mpMaskAccess);
214 mpMaskAccess = NULL;
216 else
218 mbStatus = false;
221 Bitmap::ReleaseAccess(mpAccess);
222 mpAccess = NULL;
224 else
226 mbStatus = false;
230 else
232 mpAccess = aBmp.AcquireReadAccess(); // palette + RGB without alphachannel
233 if (mpAccess)
235 if (ImplWriteHeader())
237 ImplWritepHYs(rBmpEx);
238 if (mpAccess->HasPalette())
239 ImplWritePalette();
241 ImplWriteIDAT();
243 Bitmap::ReleaseAccess(mpAccess);
244 mpAccess = NULL;
246 else
248 mbStatus = false;
252 if (mbStatus)
254 ImplOpenChunk(PNGCHUNK_IEND); // create an IEND chunk
259 bool PNGWriterImpl::Write(SvStream& rOStm)
261 /* png signature is always an array of 8 bytes */
262 SvStreamEndian nOldMode = rOStm.GetEndian();
263 rOStm.SetEndian(SvStreamEndian::BIG);
264 rOStm.WriteUInt32(0x89504e47);
265 rOStm.WriteUInt32(0x0d0a1a0a);
267 std::vector< vcl::PNGWriter::ChunkData >::iterator aBeg(maChunkSeq.begin());
268 std::vector< vcl::PNGWriter::ChunkData >::iterator aEnd(maChunkSeq.end());
269 while (aBeg != aEnd)
271 sal_uInt32 nType = aBeg->nType;
272 #if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
273 nType = OSL_SWAPDWORD(nType);
274 #endif
275 sal_uInt32 nCRC = rtl_crc32(0, &nType, 4);
276 sal_uInt32 nDataSize = aBeg->aData.size();
277 if (nDataSize)
278 nCRC = rtl_crc32(nCRC, &aBeg->aData[0], nDataSize);
279 rOStm.WriteUInt32(nDataSize);
280 rOStm.WriteUInt32(aBeg->nType);
281 if (nDataSize)
282 rOStm.Write(&aBeg->aData[0], nDataSize);
283 rOStm.WriteUInt32(nCRC);
284 ++aBeg;
286 rOStm.SetEndian(nOldMode);
287 return mbStatus;
291 bool PNGWriterImpl::ImplWriteHeader()
293 ImplOpenChunk(PNGCHUNK_IHDR);
294 ImplWriteChunk(sal_uInt32(mnWidth = mpAccess->Width()));
295 ImplWriteChunk(sal_uInt32(mnHeight = mpAccess->Height()));
297 if (mnWidth && mnHeight && mnBitsPerPixel && mbStatus)
299 sal_uInt8 nBitDepth = mnBitsPerPixel;
300 if (mnBitsPerPixel <= 8)
301 mnFilterType = 0;
302 else
303 mnFilterType = 4;
305 sal_uInt8 nColorType = 2; // colortype:
307 // bit 0 -> palette is used
308 if (mpAccess->HasPalette()) // bit 1 -> color is used
309 nColorType |= 1; // bit 2 -> alpha channel is used
310 else
311 nBitDepth /= 3;
313 if (mpMaskAccess)
314 nColorType |= 4;
316 ImplWriteChunk(nBitDepth);
317 ImplWriteChunk(nColorType); // colortype
318 ImplWriteChunk(static_cast<sal_uInt8>(0)); // compression type
319 ImplWriteChunk(static_cast<sal_uInt8>(0)); // filter type - is not supported in this version
320 ImplWriteChunk(static_cast<sal_uInt8>(mnInterlaced)); // interlace type
322 else
324 mbStatus = false;
326 return mbStatus;
329 void PNGWriterImpl::ImplWritePalette()
331 const sal_uLong nCount = mpAccess->GetPaletteEntryCount();
332 boost::scoped_array<sal_uInt8> pTempBuf(new sal_uInt8[nCount * 3]);
333 sal_uInt8* pTmp = pTempBuf.get();
335 ImplOpenChunk(PNGCHUNK_PLTE);
337 for ( sal_uInt16 i = 0; i < nCount; i++ )
339 const BitmapColor& rColor = mpAccess->GetPaletteColor(i);
340 *pTmp++ = rColor.GetRed();
341 *pTmp++ = rColor.GetGreen();
342 *pTmp++ = rColor.GetBlue();
344 ImplWriteChunk(pTempBuf.get(), nCount * 3);
347 void PNGWriterImpl::ImplWriteTransparent()
349 const sal_uLong nTransIndex = mpAccess->GetBestPaletteIndex(BMP_COL_TRANS);
351 ImplOpenChunk(PNGCHUNK_tRNS);
353 for (sal_uLong n = 0UL; n <= nTransIndex; n++)
355 ImplWriteChunk((nTransIndex == n) ? static_cast<sal_uInt8>(0x0) : static_cast<sal_uInt8>(0xff));
359 void PNGWriterImpl::ImplWritepHYs(const BitmapEx& rBmpEx)
361 if (rBmpEx.GetPrefMapMode() == MAP_100TH_MM)
363 Size aPrefSize(rBmpEx.GetPrefSize());
365 if (aPrefSize.Width() && aPrefSize.Height() && mnWidth && mnHeight)
367 ImplOpenChunk(PNGCHUNK_pHYs);
368 sal_uInt8 nMapUnit = 1;
369 sal_uInt32 nPrefSizeX = static_cast<sal_uInt32>(100000.0 / (static_cast<double>(aPrefSize.Width()) / mnWidth) + 0.5);
370 sal_uInt32 nPrefSizeY = static_cast<sal_uInt32>(100000.0 / (static_cast<double>(aPrefSize.Height()) / mnHeight) + 0.5);
371 ImplWriteChunk(nPrefSizeX);
372 ImplWriteChunk(nPrefSizeY);
373 ImplWriteChunk(nMapUnit);
378 void PNGWriterImpl::ImplWriteIDAT()
380 mnDeflateInSize = mnBitsPerPixel;
382 if (mpMaskAccess)
383 mnDeflateInSize += 8;
385 mnBBP = (mnDeflateInSize + 7) >> 3;
387 mnDeflateInSize = mnBBP * mnWidth + 1;
389 mpDeflateInBuf = new sal_uInt8[mnDeflateInSize];
391 if (mnFilterType) // using filter type 4 we need memory for the scanline 3 times
393 mpPreviousScan = new sal_uInt8[mnDeflateInSize];
394 mpCurrentScan = new sal_uInt8[mnDeflateInSize];
395 ImplClearFirstScanline();
397 mpZCodec.BeginCompression(mnCompLevel, true);
398 mpZCodec.SetCRC(mnCRC);
399 SvMemoryStream aOStm;
400 if (mnInterlaced == 0)
402 for (sal_uLong nY = 0; nY < mnHeight; nY++)
404 mpZCodec.Write(aOStm, mpDeflateInBuf, ImplGetFilter(nY));
407 else
409 // interlace mode
410 sal_uLong nY;
411 for (nY = 0; nY < mnHeight; nY += 8) // pass 1
413 mpZCodec.Write(aOStm, mpDeflateInBuf, ImplGetFilter(nY, 0, 8));
415 ImplClearFirstScanline();
417 for (nY = 0; nY < mnHeight; nY += 8) // pass 2
419 mpZCodec.Write(aOStm, mpDeflateInBuf, ImplGetFilter(nY, 4, 8));
421 ImplClearFirstScanline();
423 if (mnHeight >= 5) // pass 3
425 for (nY = 4; nY < mnHeight; nY += 8)
427 mpZCodec.Write(aOStm, mpDeflateInBuf, ImplGetFilter(nY, 0, 4));
429 ImplClearFirstScanline();
432 for (nY = 0; nY < mnHeight; nY += 4) // pass 4
434 mpZCodec.Write(aOStm, mpDeflateInBuf, ImplGetFilter(nY, 2, 4));
436 ImplClearFirstScanline();
438 if (mnHeight >= 3) // pass 5
440 for (nY = 2; nY < mnHeight; nY += 4)
442 mpZCodec.Write(aOStm, mpDeflateInBuf, ImplGetFilter(nY, 0, 2));
444 ImplClearFirstScanline();
447 for (nY = 0; nY < mnHeight; nY += 2) // pass 6
449 mpZCodec.Write(aOStm, mpDeflateInBuf, ImplGetFilter(nY, 1, 2));
451 ImplClearFirstScanline();
453 if (mnHeight >= 2) // pass 7
455 for (nY = 1; nY < mnHeight; nY += 2)
457 mpZCodec.Write(aOStm, mpDeflateInBuf, ImplGetFilter (nY, 0, 1));
461 mpZCodec.EndCompression();
462 mnCRC = mpZCodec.GetCRC();
464 if (mnFilterType) // using filter type 4 we need memory for the scanline 3 times
466 delete[] mpCurrentScan;
467 delete[] mpPreviousScan;
469 delete[] mpDeflateInBuf;
471 sal_uInt32 nIDATSize = aOStm.Tell();
472 sal_uInt32 nBytes, nBytesToWrite = nIDATSize;
473 while(nBytesToWrite)
475 nBytes = nBytesToWrite <= mnMaxChunkSize ? nBytesToWrite : mnMaxChunkSize;
476 ImplOpenChunk(PNGCHUNK_IDAT);
477 ImplWriteChunk(const_cast<unsigned char *>(static_cast<unsigned char const *>(aOStm.GetData())) + (nIDATSize - nBytesToWrite), nBytes);
478 nBytesToWrite -= nBytes;
482 // ImplGetFilter writes the complete Scanline (nY) - in interlace mode the parameter nXStart and nXAdd
483 // appends to the currently used pass
484 // the complete size of scanline will be returned - in interlace mode zero is possible!
486 sal_uLong PNGWriterImpl::ImplGetFilter (sal_uLong nY, sal_uLong nXStart, sal_uLong nXAdd)
488 sal_uInt8* pDest;
490 if (mnFilterType)
491 pDest = mpCurrentScan;
492 else
493 pDest = mpDeflateInBuf;
495 if (nXStart < mnWidth)
497 *pDest++ = mnFilterType; // in this version the filter type is either 0 or 4
499 if (mpAccess->HasPalette()) // alphachannel is not allowed by pictures including palette entries
501 switch (mnBitsPerPixel)
503 case 1:
505 sal_uLong nX, nXIndex;
506 for (nX = nXStart, nXIndex = 0; nX < mnWidth; nX += nXAdd, nXIndex++)
508 sal_uLong nShift = (nXIndex & 7) ^ 7;
509 if (nShift == 7)
510 *pDest = mpAccess->GetPixelIndex(nY, nX) << nShift;
511 else if (nShift == 0)
512 *pDest++ |= mpAccess->GetPixelIndex(nY, nX) << nShift;
513 else
514 *pDest |= mpAccess->GetPixelIndex(nY, nX) << nShift;
516 if ( (nXIndex & 7) != 0 )
517 pDest++; // byte is not completely used, so the bufferpointer is to correct
519 break;
521 case 4:
523 sal_uLong nX, nXIndex;
524 for (nX = nXStart, nXIndex = 0; nX < mnWidth; nX += nXAdd, nXIndex++)
526 if(nXIndex & 1)
527 *pDest++ |= mpAccess->GetPixelIndex(nY, nX);
528 else
529 *pDest = mpAccess->GetPixelIndex(nY, nX) << 4;
531 if (nXIndex & 1)
532 pDest++;
534 break;
536 case 8:
538 for (sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd)
540 *pDest++ = mpAccess->GetPixelIndex( nY, nX );
543 break;
545 default :
546 mbStatus = false;
547 break;
550 else
552 if (mpMaskAccess) // mpMaskAccess != NULL -> alphachannel is to create
554 if (mbTrueAlpha)
556 for (sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd)
558 const BitmapColor& rColor = mpAccess->GetPixel(nY, nX);
559 *pDest++ = rColor.GetRed();
560 *pDest++ = rColor.GetGreen();
561 *pDest++ = rColor.GetBlue();
562 *pDest++ = 255 - mpMaskAccess->GetPixelIndex(nY, nX);
565 else
567 const BitmapColor aTrans(mpMaskAccess->GetBestMatchingColor(Color(COL_WHITE)));
569 for (sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd)
571 const BitmapColor& rColor = mpAccess->GetPixel(nY, nX);
572 *pDest++ = rColor.GetRed();
573 *pDest++ = rColor.GetGreen();
574 *pDest++ = rColor.GetBlue();
576 if(mpMaskAccess->GetPixel(nY, nX) == aTrans)
577 *pDest++ = 0;
578 else
579 *pDest++ = 0xff;
583 else
585 for (sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd)
587 const BitmapColor& rColor = mpAccess->GetPixel(nY, nX);
588 *pDest++ = rColor.GetRed();
589 *pDest++ = rColor.GetGreen();
590 *pDest++ = rColor.GetBlue();
595 // filter type4 ( PAETH ) will be used only for 24bit graphics
596 if (mnFilterType)
598 mnDeflateInSize = pDest - mpCurrentScan;
599 pDest = mpDeflateInBuf;
600 *pDest++ = 4; // filter type
602 sal_uLong na, nb, nc;
603 long np, npa, npb, npc;
605 sal_uInt8* p1 = mpCurrentScan + 1; // Current Pixel
606 sal_uInt8* p2 = p1 - mnBBP; // left pixel
607 sal_uInt8* p3 = mpPreviousScan; // upper pixel
608 sal_uInt8* p4 = p3 - mnBBP; // upperleft Pixel;
610 while (pDest < mpDeflateInBuf + mnDeflateInSize)
612 nb = *p3++;
613 if (p2 >= mpCurrentScan + 1)
615 na = *p2;
616 nc = *p4;
618 else
620 na = nc = 0;
623 np = na + nb;
624 np -= nc;
625 npa = np - na;
626 npb = np - nb;
627 npc = np - nc;
629 if (npa < 0)
630 npa =-npa;
631 if (npb < 0)
632 npb =-npb;
633 if (npc < 0)
634 npc =-npc;
636 if (npa <= npb && npa <= npc)
637 *pDest++ = *p1++ - static_cast<sal_uInt8>(na);
638 else if ( npb <= npc )
639 *pDest++ = *p1++ - static_cast<sal_uInt8>(nb);
640 else
641 *pDest++ = *p1++ - static_cast<sal_uInt8>(nc);
643 p4++;
644 p2++;
646 for (long i = 0; i < static_cast<long>(mnDeflateInSize - 1); i++)
648 mpPreviousScan[i] = mpCurrentScan[i + 1];
651 else
653 mnDeflateInSize = pDest - mpDeflateInBuf;
655 return mnDeflateInSize;
658 void PNGWriterImpl::ImplClearFirstScanline()
660 if (mnFilterType)
661 memset(mpPreviousScan, 0, mnDeflateInSize);
664 void PNGWriterImpl::ImplOpenChunk (sal_uLong nChunkType)
666 maChunkSeq.resize(maChunkSeq.size() + 1);
667 maChunkSeq.back().nType = nChunkType;
670 void PNGWriterImpl::ImplWriteChunk (sal_uInt8 nSource)
672 maChunkSeq.back().aData.push_back(nSource);
675 void PNGWriterImpl::ImplWriteChunk (sal_uInt32 nSource)
677 vcl::PNGWriter::ChunkData& rChunkData = maChunkSeq.back();
678 rChunkData.aData.push_back(static_cast<sal_uInt8>(nSource >> 24));
679 rChunkData.aData.push_back(static_cast<sal_uInt8>(nSource >> 16));
680 rChunkData.aData.push_back(static_cast<sal_uInt8>(nSource >> 8));
681 rChunkData.aData.push_back(static_cast<sal_uInt8>(nSource));
684 void PNGWriterImpl::ImplWriteChunk (unsigned char* pSource, sal_uInt32 nDatSize)
686 if (nDatSize)
688 vcl::PNGWriter::ChunkData& rChunkData = maChunkSeq.back();
689 sal_uInt32 nSize = rChunkData.aData.size();
690 rChunkData.aData.resize(nSize + nDatSize);
691 memcpy(&rChunkData.aData[nSize], pSource, nDatSize);
695 PNGWriter::PNGWriter(const BitmapEx& rBmpEx,
696 const css::uno::Sequence<css::beans::PropertyValue>* pFilterData)
697 : mpImpl(new vcl::PNGWriterImpl(rBmpEx, pFilterData))
701 PNGWriter::~PNGWriter()
705 bool PNGWriter::Write(SvStream& rStream)
707 return mpImpl->Write(rStream);
710 std::vector<vcl::PNGWriter::ChunkData>& PNGWriter::GetChunks()
712 return mpImpl->GetChunks();
715 } // namespace vcl
717 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */