Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / unx / generic / print / bitmap_gfx.cxx
blobf58e77d0ecaeecdd23fd93b9927078e106da1ed2
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 <array>
21 #include <memory>
22 #include "psputil.hxx"
24 #include <unx/printergfx.hxx>
26 namespace psp {
28 const sal_uInt32 nLineLength = 80;
29 const sal_uInt32 nBufferSize = 16384;
33 * Bitmap compression / Hex encoding / Ascii85 Encoding
37 PrinterBmp::~PrinterBmp()
41 /* virtual base class */
43 class ByteEncoder
45 private:
47 public:
49 virtual void EncodeByte (sal_uInt8 nByte) = 0;
50 virtual ~ByteEncoder () = 0;
53 ByteEncoder::~ByteEncoder()
57 /* HexEncoder */
59 class HexEncoder : public ByteEncoder
61 private:
63 osl::File* const mpFile;
64 sal_uInt32 mnColumn;
65 sal_uInt32 mnOffset;
66 OStringBuffer mpFileBuffer;
68 public:
70 explicit HexEncoder (osl::File* pFile);
71 virtual ~HexEncoder () override;
72 void WriteAscii (sal_uInt8 nByte);
73 virtual void EncodeByte (sal_uInt8 nByte) override;
74 void FlushLine ();
77 HexEncoder::HexEncoder (osl::File* pFile) :
78 mpFile (pFile),
79 mnColumn (0),
80 mnOffset (0)
83 HexEncoder::~HexEncoder ()
85 FlushLine ();
86 if (mnColumn > 0)
87 WritePS (mpFile, "\n");
90 void
91 HexEncoder::WriteAscii (sal_uInt8 nByte)
93 sal_uInt32 nOff = psp::getHexValueOf (nByte, mpFileBuffer);
94 mnColumn += nOff;
95 mnOffset += nOff;
97 if (mnColumn >= nLineLength)
99 mnOffset += psp::appendStr ("\n", mpFileBuffer);
100 mnColumn = 0;
102 if (mnOffset >= nBufferSize)
103 FlushLine ();
106 void
107 HexEncoder::EncodeByte (sal_uInt8 nByte)
109 WriteAscii (nByte);
112 void
113 HexEncoder::FlushLine ()
115 if (mnOffset > 0)
117 WritePS (mpFile, mpFileBuffer.makeStringAndClear());
118 mnOffset = 0;
122 /* Ascii85 encoder, is abi compatible with HexEncoder but writes a ~> to
123 indicate end of data EOD */
125 class Ascii85Encoder : public ByteEncoder
127 private:
129 osl::File* const mpFile;
130 sal_uInt32 mnByte;
131 sal_uInt8 mpByteBuffer[4];
133 sal_uInt32 mnColumn;
134 sal_uInt32 mnOffset;
135 OStringBuffer mpFileBuffer;
137 inline void PutByte (sal_uInt8 nByte);
138 inline void PutEOD ();
139 void ConvertToAscii85 ();
140 void FlushLine ();
142 public:
144 explicit Ascii85Encoder (osl::File* pFile);
145 virtual ~Ascii85Encoder () override;
146 virtual void EncodeByte (sal_uInt8 nByte) override;
147 void WriteAscii (sal_uInt8 nByte);
150 Ascii85Encoder::Ascii85Encoder (osl::File* pFile) :
151 mpFile (pFile),
152 mnByte (0),
153 mnColumn (0),
154 mnOffset (0)
157 inline void
158 Ascii85Encoder::PutByte (sal_uInt8 nByte)
160 mpByteBuffer [mnByte++] = nByte;
163 inline void
164 Ascii85Encoder::PutEOD ()
166 WritePS (mpFile, "~>\n");
169 void
170 Ascii85Encoder::ConvertToAscii85 ()
172 // Add (4 - mnByte) zero padding bytes:
173 if (mnByte < 4)
174 std::memset (mpByteBuffer + mnByte, 0, (4 - mnByte) * sizeof(sal_uInt8));
176 sal_uInt32 nByteValue = mpByteBuffer[0] * 256 * 256 * 256
177 + mpByteBuffer[1] * 256 * 256
178 + mpByteBuffer[2] * 256
179 + mpByteBuffer[3];
181 if (nByteValue == 0 && mnByte == 4)
183 /* special case of 4 Bytes in row */
184 mpFileBuffer.append('z');
186 mnOffset += 1;
187 mnColumn += 1;
189 else
191 /* real ascii85 encoding */
193 // Of the up to 5 characters to be generated, do not generate the last (4 - mnByte) ones
194 // that correspond to the (4 - mnByte) zero padding bytes added to the input:
196 auto const pos = mpFileBuffer.getLength();
197 if (mnByte == 4) {
198 mpFileBuffer.insert(pos, char((nByteValue % 85) + 33));
200 nByteValue /= 85;
201 if (mnByte >= 3) {
202 mpFileBuffer.insert(pos, char((nByteValue % 85) + 33));
204 nByteValue /= 85;
205 if (mnByte >= 2) {
206 mpFileBuffer.insert(pos, char((nByteValue % 85) + 33));
208 nByteValue /= 85;
209 mpFileBuffer.insert(pos, char((nByteValue % 85) + 33));
210 nByteValue /= 85;
211 mpFileBuffer.insert(pos, char((nByteValue % 85) + 33));
213 mnColumn += (mnByte + 1);
214 mnOffset += (mnByte + 1);
216 /* insert a newline if necessary */
217 if (mnColumn > nLineLength)
219 sal_uInt32 nEolOff = mnColumn - nLineLength;
220 auto const posNl = pos + (mnByte + 1) - nEolOff;
222 mpFileBuffer.insert(posNl, '\n');
224 mnOffset++;
225 mnColumn = nEolOff;
229 mnByte = 0;
232 void
233 Ascii85Encoder::WriteAscii (sal_uInt8 nByte)
235 PutByte (nByte);
236 if (mnByte == 4)
237 ConvertToAscii85 ();
239 if (mnColumn >= nLineLength)
241 mnOffset += psp::appendStr ("\n", mpFileBuffer);
242 mnColumn = 0;
244 if (mnOffset >= nBufferSize)
245 FlushLine ();
248 void
249 Ascii85Encoder::EncodeByte (sal_uInt8 nByte)
251 WriteAscii (nByte);
254 void
255 Ascii85Encoder::FlushLine ()
257 if (mnOffset > 0)
259 WritePS (mpFile, mpFileBuffer.makeStringAndClear());
260 mnOffset = 0;
264 Ascii85Encoder::~Ascii85Encoder ()
266 if (mnByte > 0)
267 ConvertToAscii85 ();
268 if (mnOffset > 0)
269 FlushLine ();
270 PutEOD ();
273 /* LZW encoder */
275 class LZWEncoder : public Ascii85Encoder
277 private:
279 struct LZWCTreeNode
281 LZWCTreeNode* mpBrother; // next node with same parent
282 LZWCTreeNode* mpFirstChild; // first son
283 sal_uInt16 mnCode; // code for the string
284 sal_uInt16 mnValue; // pixelvalue
287 std::array<LZWCTreeNode, 4096>
288 mpTable; // LZW compression data
289 LZWCTreeNode* mpPrefix; // the compression is as same as the TIFF compression
290 static constexpr sal_uInt16 gnDataSize = 8;
291 static constexpr sal_uInt16 gnClearCode = 1 << gnDataSize;
292 static constexpr sal_uInt16 gnEOICode = gnClearCode + 1;
293 sal_uInt16 mnTableSize;
294 sal_uInt16 mnCodeSize;
295 sal_uInt32 mnOffset;
296 sal_uInt32 mdwShift;
298 void WriteBits (sal_uInt16 nCode, sal_uInt16 nCodeLen);
300 public:
302 explicit LZWEncoder (osl::File* pOutputFile);
303 virtual ~LZWEncoder () override;
305 virtual void EncodeByte (sal_uInt8 nByte) override;
308 LZWEncoder::LZWEncoder(osl::File* pOutputFile) :
309 Ascii85Encoder (pOutputFile),
310 mpPrefix(nullptr),
311 mnTableSize(gnEOICode + 1),
312 mnCodeSize(gnDataSize + 1),
313 mnOffset(32), // free bits in dwShift
314 mdwShift(0)
316 for (sal_uInt32 i = 0; i < 4096; i++)
318 mpTable[i].mpBrother = nullptr;
319 mpTable[i].mpFirstChild = nullptr;
320 mpTable[i].mnCode = i;
321 mpTable[i].mnValue = static_cast<sal_uInt8>(mpTable[i].mnCode);
324 WriteBits( gnClearCode, mnCodeSize );
327 LZWEncoder::~LZWEncoder()
329 if (mpPrefix)
330 WriteBits (mpPrefix->mnCode, mnCodeSize);
332 WriteBits (gnEOICode, mnCodeSize);
335 void
336 LZWEncoder::WriteBits (sal_uInt16 nCode, sal_uInt16 nCodeLen)
338 mdwShift |= (nCode << (mnOffset - nCodeLen));
339 mnOffset -= nCodeLen;
340 while (mnOffset < 24)
342 WriteAscii (static_cast<sal_uInt8>(mdwShift >> 24));
343 mdwShift <<= 8;
344 mnOffset += 8;
346 if (nCode == 257 && mnOffset != 32)
347 WriteAscii (static_cast<sal_uInt8>(mdwShift >> 24));
350 void
351 LZWEncoder::EncodeByte (sal_uInt8 nByte )
353 LZWCTreeNode* p;
354 sal_uInt16 i;
355 sal_uInt8 nV;
357 if (!mpPrefix)
359 mpPrefix = mpTable.data() + nByte;
361 else
363 nV = nByte;
364 for (p = mpPrefix->mpFirstChild; p != nullptr; p = p->mpBrother)
366 if (p->mnValue == nV)
367 break;
370 if (p != nullptr)
372 mpPrefix = p;
374 else
376 WriteBits (mpPrefix->mnCode, mnCodeSize);
378 if (mnTableSize == 409)
380 WriteBits (gnClearCode, mnCodeSize);
382 for (i = 0; i < gnClearCode; i++)
383 mpTable[i].mpFirstChild = nullptr;
385 mnCodeSize = gnDataSize + 1;
386 mnTableSize = gnEOICode + 1;
388 else
390 if(mnTableSize == static_cast<sal_uInt16>((1 << mnCodeSize) - 1))
391 mnCodeSize++;
393 p = mpTable.data() + (mnTableSize++);
394 p->mpBrother = mpPrefix->mpFirstChild;
395 mpPrefix->mpFirstChild = p;
396 p->mnValue = nV;
397 p->mpFirstChild = nullptr;
400 mpPrefix = mpTable.data() + nV;
407 * bitmap handling routines
411 void
412 PrinterGfx::DrawBitmap (const tools::Rectangle& rDest, const tools::Rectangle& rSrc,
413 const PrinterBmp& rBitmap)
415 double fScaleX = static_cast<double>(rDest.GetWidth());
416 double fScaleY = static_cast<double>(rDest.GetHeight());
417 if(rSrc.GetWidth() > 0)
419 fScaleX = static_cast<double>(rDest.GetWidth()) / static_cast<double>(rSrc.GetWidth());
421 if(rSrc.GetHeight() > 0)
423 fScaleY = static_cast<double>(rDest.GetHeight()) / static_cast<double>(rSrc.GetHeight());
425 PSGSave ();
426 PSTranslate (rDest.BottomLeft());
427 PSScale (fScaleX, fScaleY);
429 if (mnPSLevel >= 2)
431 if (rBitmap.GetDepth() == 1)
433 DrawPS2MonoImage (rBitmap, rSrc);
435 else
436 if (rBitmap.GetDepth() == 8 && mbColor)
438 // if the palette is larger than the image itself print it as a truecolor
439 // image to save diskspace. This is important for printing transparent
440 // bitmaps that are disassembled into small pieces
441 sal_Int32 nImageSz = rSrc.GetWidth() * rSrc.GetHeight();
442 sal_Int32 nPaletteSz = rBitmap.GetPaletteEntryCount();
443 if ((nImageSz < nPaletteSz) || (nImageSz < 24) )
444 DrawPS2TrueColorImage (rBitmap, rSrc);
445 else
446 DrawPS2PaletteImage (rBitmap, rSrc);
448 else
449 if (rBitmap.GetDepth() == 24 && mbColor)
451 DrawPS2TrueColorImage (rBitmap, rSrc);
453 else
455 DrawPS2GrayImage (rBitmap, rSrc);
458 else
460 DrawPS1GrayImage (rBitmap, rSrc);
463 PSGRestore ();
468 * Implementation: PS Level 1
472 void
473 PrinterGfx::DrawPS1GrayImage (const PrinterBmp& rBitmap, const tools::Rectangle& rArea)
475 sal_uInt32 nWidth = rArea.GetWidth();
476 sal_uInt32 nHeight = rArea.GetHeight();
478 OStringBuffer pGrayImage;
480 // image header
481 psp::getValueOf (nWidth, pGrayImage);
482 psp::appendStr (" ", pGrayImage);
483 psp::getValueOf (nHeight, pGrayImage);
484 psp::appendStr (" 8 ", pGrayImage);
485 psp::appendStr ("[ 1 0 0 1 0 ", pGrayImage);
486 psp::getValueOf (nHeight, pGrayImage);
487 psp::appendStr ("]", pGrayImage);
488 psp::appendStr (" {currentfile ", pGrayImage);
489 psp::getValueOf (nWidth, pGrayImage);
490 psp::appendStr (" string readhexstring pop}\n", pGrayImage);
491 psp::appendStr ("image\n", pGrayImage);
493 WritePS (mpPageBody, pGrayImage.makeStringAndClear());
495 // image body
496 std::unique_ptr<HexEncoder> xEncoder(new HexEncoder (mpPageBody));
498 for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
500 for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
502 unsigned char nByte = rBitmap.GetPixelGray (nRow, nColumn);
503 xEncoder->EncodeByte (nByte);
507 xEncoder.reset();
509 WritePS (mpPageBody, "\n");
514 * Implementation: PS Level 2
518 void
519 PrinterGfx::writePS2ImageHeader (const tools::Rectangle& rArea, psp::ImageType nType)
521 OStringBuffer pImage;
523 sal_Int32 nDictType = 0;
524 switch (nType)
526 case psp::ImageType::TrueColorImage: nDictType = 0; break;
527 case psp::ImageType::PaletteImage: nDictType = 1; break;
528 case psp::ImageType::GrayScaleImage: nDictType = 2; break;
529 case psp::ImageType::MonochromeImage: nDictType = 3; break;
530 default: break;
533 psp::getValueOf (rArea.GetWidth(), pImage);
534 psp::appendStr (" ", pImage);
535 psp::getValueOf (rArea.GetHeight(), pImage);
536 psp::appendStr (" ", pImage);
537 psp::getValueOf (nDictType, pImage);
538 psp::appendStr (" ", pImage);
539 psp::getValueOf (sal_Int32(1), pImage); // nCompressType
540 psp::appendStr (" psp_imagedict image\n", pImage);
542 WritePS (mpPageBody, pImage.makeStringAndClear());
545 void
546 PrinterGfx::writePS2Colorspace(const PrinterBmp& rBitmap, psp::ImageType nType)
548 switch (nType)
550 case psp::ImageType::GrayScaleImage:
552 WritePS (mpPageBody, "/DeviceGray setcolorspace\n");
553 break;
555 case psp::ImageType::TrueColorImage:
557 WritePS (mpPageBody, "/DeviceRGB setcolorspace\n");
558 break;
560 case psp::ImageType::MonochromeImage:
561 case psp::ImageType::PaletteImage:
564 OStringBuffer pImage;
566 const sal_uInt32 nSize = rBitmap.GetPaletteEntryCount();
568 psp::appendStr ("[/Indexed /DeviceRGB ", pImage);
569 psp::getValueOf (nSize - 1, pImage);
570 psp::appendStr ("\npsp_lzwstring\n", pImage);
571 WritePS (mpPageBody, pImage.makeStringAndClear());
573 std::unique_ptr<ByteEncoder> xEncoder(new LZWEncoder(mpPageBody));
574 for (sal_uInt32 i = 0; i < nSize; i++)
576 PrinterColor aColor = rBitmap.GetPaletteColor(i);
578 xEncoder->EncodeByte (aColor.GetRed());
579 xEncoder->EncodeByte (aColor.GetGreen());
580 xEncoder->EncodeByte (aColor.GetBlue());
582 xEncoder.reset();
584 WritePS (mpPageBody, "pop ] setcolorspace\n");
586 break;
587 default: break;
591 void
592 PrinterGfx::DrawPS2GrayImage (const PrinterBmp& rBitmap, const tools::Rectangle& rArea)
594 writePS2Colorspace(rBitmap, psp::ImageType::GrayScaleImage);
595 writePS2ImageHeader(rArea, psp::ImageType::GrayScaleImage);
597 std::unique_ptr<ByteEncoder> xEncoder(new LZWEncoder(mpPageBody));
599 for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
601 for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
603 unsigned char nByte = rBitmap.GetPixelGray (nRow, nColumn);
604 xEncoder->EncodeByte (nByte);
609 void
610 PrinterGfx::DrawPS2MonoImage (const PrinterBmp& rBitmap, const tools::Rectangle& rArea)
612 writePS2Colorspace(rBitmap, psp::ImageType::MonochromeImage);
613 writePS2ImageHeader(rArea, psp::ImageType::MonochromeImage);
615 std::unique_ptr<ByteEncoder> xEncoder(new LZWEncoder(mpPageBody));
617 for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
619 long nBitPos = 0;
620 unsigned char nByte = 0;
622 for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
624 unsigned char nBit = rBitmap.GetPixelIdx (nRow, nColumn);
625 nByte |= nBit << (7 - nBitPos);
627 if (++nBitPos == 8)
629 xEncoder->EncodeByte (nByte);
630 nBitPos = 0;
631 nByte = 0;
634 // keep the row byte aligned
635 if (nBitPos != 0)
636 xEncoder->EncodeByte (nByte);
640 void
641 PrinterGfx::DrawPS2PaletteImage (const PrinterBmp& rBitmap, const tools::Rectangle& rArea)
643 writePS2Colorspace(rBitmap, psp::ImageType::PaletteImage);
644 writePS2ImageHeader(rArea, psp::ImageType::PaletteImage);
646 std::unique_ptr<ByteEncoder> xEncoder(new LZWEncoder(mpPageBody));
648 for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
650 for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
652 unsigned char nByte = rBitmap.GetPixelIdx (nRow, nColumn);
653 xEncoder->EncodeByte (nByte);
658 void
659 PrinterGfx::DrawPS2TrueColorImage (const PrinterBmp& rBitmap, const tools::Rectangle& rArea)
661 writePS2Colorspace(rBitmap, psp::ImageType::TrueColorImage);
662 writePS2ImageHeader(rArea, psp::ImageType::TrueColorImage);
664 std::unique_ptr<ByteEncoder> xEncoder(new LZWEncoder(mpPageBody));
666 for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
668 for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
670 PrinterColor aColor = rBitmap.GetPixelRGB (nRow, nColumn);
671 xEncoder->EncodeByte (aColor.GetRed());
672 xEncoder->EncodeByte (aColor.GetGreen());
673 xEncoder->EncodeByte (aColor.GetBlue());
678 } /* namespace psp */
680 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */