tdf#130857 qt weld: Implement QtInstanceWidget::get_text_height
[LibreOffice.git] / vcl / source / filter / ixpm / xpmread.cxx
blob6c9d226a3f32971d6b17bf0fcbd509ba501885f9
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 <filter/XpmReader.hxx>
22 #include <vcl/graph.hxx>
23 #include <tools/stream.hxx>
25 #include <vcl/BitmapWriteAccess.hxx>
27 #include "rgbtable.hxx"
29 #include <cstring>
30 #include <array>
31 #include <map>
33 #define XPMTEMPBUFSIZE 0x00008000
34 #define XPMSTRINGBUF 0x00008000
36 #define XPMIDENTIFIER 0x00000001 // mnIdentifier includes one of the six phases
37 #define XPMDEFINITION 0x00000002 // the XPM format consists of
38 #define XPMVALUES 0x00000003
39 #define XPMCOLORS 0x00000004
40 #define XPMPIXELS 0x00000005
41 #define XPMEXTENSIONS 0x00000006
42 #define XPMENDEXT 0x00000007
44 #define XPMREMARK 0x00000001 // defines used by mnStatus
45 #define XPMDOUBLE 0x00000002
46 #define XPMSTRING 0x00000004
47 #define XPMFINISHED 0x00000008
49 namespace {
51 enum ReadState
53 XPMREAD_OK,
54 XPMREAD_ERROR
59 class BitmapWriteAccess;
60 class Graphic;
62 namespace
65 class XPMReader
67 private:
68 SvStream& mrStream;
69 Bitmap maBitmap;
70 BitmapScopedWriteAccess mpWriterAccess;
71 Bitmap maMaskBitmap;
72 BitmapScopedWriteAccess mpMaskWriterAccess;
73 sal_uInt64 mnLastPos;
75 tools::Long mnWidth = 0;
76 tools::Long mnHeight = 0;
77 sal_uLong mnColors = 0;
78 sal_uInt32 mnCpp = 0; // characters per pix
79 bool mbTransparent = false;
80 bool mbStatus = true;
81 sal_uLong mnStatus = 0;
82 sal_uLong mnIdentifier = XPMIDENTIFIER;
83 sal_uInt8 mcThisByte = 0;
84 sal_uInt8 mcLastByte = 0;
85 sal_uLong mnTempAvail = 0;
86 sal_uInt8* mpTempBuf = nullptr;
87 sal_uInt8* mpTempPtr = nullptr;
88 // each key is ( mnCpp )Byte(s)-> ASCII entry assigned to the colour
89 // each colordata is
90 // 1 Byte -> 0xFF if colour is transparent
91 // 3 Bytes -> RGB value of the colour
92 typedef std::array<sal_uInt8, 4> ColorData;
93 typedef std::map<OString, ColorData> ColorMap;
94 ColorMap maColMap;
95 sal_uLong mnStringSize = 0;
96 sal_uInt8* mpStringBuf = nullptr;
97 sal_uLong mnParaSize = 0;
98 sal_uInt8* mpPara = nullptr;
100 bool ImplGetString();
101 bool ImplGetColor();
102 bool ImplGetScanLine( sal_uLong );
103 bool ImplGetColSub(ColorData &rDest);
104 bool ImplGetColKey( sal_uInt8 );
105 void ImplGetRGBHex(ColorData &rDest, sal_uLong);
106 bool ImplGetPara( sal_uLong numb );
107 static bool ImplCompare(sal_uInt8 const *, sal_uInt8 const *, sal_uLong);
108 sal_uLong ImplGetULONG( sal_uLong nPara );
110 public:
111 explicit XPMReader(SvStream& rStream);
113 ReadState ReadXPM(BitmapEx& rBitmapEx);
118 XPMReader::XPMReader(SvStream& rStream)
119 : mrStream(rStream)
120 , mnLastPos(rStream.Tell())
124 ReadState XPMReader::ReadXPM(BitmapEx& rBitmapEx)
126 if (!mrStream.good())
127 return XPMREAD_ERROR;
129 ReadState eReadState = XPMREAD_ERROR;
131 mrStream.Seek( mnLastPos );
132 mbStatus = true;
134 if ( mbStatus )
136 mpStringBuf = new sal_uInt8 [ XPMSTRINGBUF ];
137 mpTempBuf = new sal_uInt8 [ XPMTEMPBUFSIZE ];
139 mbStatus = ImplGetString();
140 if ( mbStatus )
142 mnIdentifier = XPMVALUES; // fetch Bitmap information
143 mnWidth = ImplGetULONG( 0 );
144 mnHeight = ImplGetULONG( 1 );
145 mnColors = ImplGetULONG( 2 );
146 mnCpp = ImplGetULONG( 3 );
148 if ( mnColors > ( SAL_MAX_UINT32 / ( 4 + mnCpp ) ) )
149 mbStatus = false;
150 if ( ( mnWidth * mnCpp ) >= XPMSTRINGBUF )
151 mbStatus = false;
152 //xpms are a minimum of one character (one byte) per pixel, so if the file isn't
153 //even that long, it's not all there
154 if (mrStream.remainingSize() + mnTempAvail < static_cast<sal_uInt64>(mnWidth) * mnHeight)
155 mbStatus = false;
156 if ( mbStatus && mnWidth && mnHeight && mnColors && mnCpp )
158 mnIdentifier = XPMCOLORS;
160 for (sal_uLong i = 0; i < mnColors; ++i)
162 if (!ImplGetColor())
164 mbStatus = false;
165 break;
169 if ( mbStatus )
171 // create a 24bit graphic when more as 256 colours present
172 auto ePixelFormat = vcl::PixelFormat::INVALID;
173 if ( mnColors > 256 )
174 ePixelFormat = vcl::PixelFormat::N24_BPP;
175 else
176 ePixelFormat = vcl::PixelFormat::N8_BPP;
178 maBitmap = Bitmap(Size(mnWidth, mnHeight), ePixelFormat);
179 mpWriterAccess = maBitmap;
181 // mbTransparent is TRUE if at least one colour is transparent
182 if ( mbTransparent )
184 maMaskBitmap = Bitmap(Size(mnWidth, mnHeight), vcl::PixelFormat::N8_BPP, &Bitmap::GetGreyPalette(256));
185 mpMaskWriterAccess = maMaskBitmap;
186 if (!mpMaskWriterAccess)
187 mbStatus = false;
189 if (mpWriterAccess && mbStatus)
191 if (mnColors <= 256) // palette is only needed by using less than 257
192 { // colors
193 sal_uInt8 i = 0;
194 for (auto& elem : maColMap)
196 mpWriterAccess->SetPaletteColor(i, Color(elem.second[1], elem.second[2], elem.second[3]));
197 //reuse map entry, overwrite color with palette index
198 elem.second[1] = i;
199 i++;
203 // now we get the bitmap data
204 mnIdentifier = XPMPIXELS;
205 for (tools::Long i = 0; i < mnHeight; ++i)
207 if ( !ImplGetScanLine( i ) )
209 mbStatus = false;
210 break;
213 mnIdentifier = XPMEXTENSIONS;
218 delete[] mpStringBuf;
219 delete[] mpTempBuf;
222 if( mbStatus )
224 mpWriterAccess.reset();
225 if (mpMaskWriterAccess)
227 mpMaskWriterAccess.reset();
228 rBitmapEx = BitmapEx(maBitmap, maMaskBitmap);
230 else
232 rBitmapEx = BitmapEx(maBitmap);
234 eReadState = XPMREAD_OK;
236 else
238 mpMaskWriterAccess.reset();
239 mpWriterAccess.reset();
241 return eReadState;
244 // ImplGetColor returns various colour values,
245 // returns TRUE if various colours could be assigned
246 bool XPMReader::ImplGetColor()
248 sal_uInt8* pString = mpStringBuf;
249 if (!ImplGetString())
250 return false;
252 if (mnStringSize < mnCpp)
253 return false;
255 OString aKey(reinterpret_cast<char*>(pString), mnCpp);
256 ColorData aValue{0};
257 bool bStatus = ImplGetColSub(aValue);
258 if (bStatus)
260 maColMap[aKey] = aValue;
262 return bStatus;
265 // ImpGetScanLine reads the string mpBufSize and writes the pixel in the
266 // Bitmap. Parameter nY is the horizontal position.
267 bool XPMReader::ImplGetScanLine( sal_uLong nY )
269 bool bStatus = ImplGetString();
270 sal_uInt8* pString = mpStringBuf;
271 BitmapColor aWhite;
272 BitmapColor aBlack;
274 if ( bStatus )
276 if (mpMaskWriterAccess)
278 aWhite = mpMaskWriterAccess->GetBestMatchingColor( COL_WHITE );
279 aBlack = mpMaskWriterAccess->GetBestMatchingColor( COL_BLACK );
281 if (mnStringSize != (sal_uLong(mnWidth) * mnCpp))
282 bStatus = false;
283 else
285 Scanline pScanline = mpWriterAccess->GetScanline(nY);
286 Scanline pMaskScanline = mpMaskWriterAccess ? mpMaskWriterAccess->GetScanline(nY) : nullptr;
287 for (tools::Long i = 0; i < mnWidth; ++i)
289 OString aKey(reinterpret_cast<char*>(pString), mnCpp);
290 auto it = maColMap.find(aKey);
291 if (it != maColMap.end())
293 if (mnColors > 256)
294 mpWriterAccess->SetPixelOnData(pScanline, i, Color(it->second[1], it->second[2], it->second[3]));
295 else
296 mpWriterAccess->SetPixelOnData(pScanline, i, BitmapColor(it->second[1]));
297 if (pMaskScanline)
298 mpMaskWriterAccess->SetPixelOnData(pMaskScanline, i, it->second[0] ? aWhite : aBlack);
300 pString += mnCpp;
304 return bStatus;
307 // tries to determine a colour value from mpStringBuf
308 // if a colour was found the RGB value is written a pDest[1]..pDest[2]
309 // pDest[0] contains 0xFF if the colour is transparent otherwise 0
311 bool XPMReader::ImplGetColSub(ColorData& rDest)
313 unsigned char cTransparent[] = "None";
315 bool bColStatus = false;
317 if ( ImplGetColKey( 'c' ) || ImplGetColKey( 'm' ) || ImplGetColKey( 'g' ) )
319 // hexentry for RGB or HSV color ?
320 if (*mpPara == '#')
322 rDest[0] = 0;
323 bColStatus = true;
324 switch ( mnParaSize )
326 case 25 :
327 ImplGetRGBHex(rDest, 6);
328 break;
329 case 13 :
330 ImplGetRGBHex(rDest, 2);
331 break;
332 case 7 :
333 ImplGetRGBHex(rDest, 0);
334 break;
335 default:
336 bColStatus = false;
337 break;
340 // maybe pixel is transparent
341 else if ( ImplCompare( &cTransparent[0], mpPara, 4 ))
343 rDest[0] = 0xff;
344 bColStatus = true;
345 mbTransparent = true;
347 // last we will try to get the colorname
348 else if ( mnParaSize > 2 ) // name must enlarge the minimum size
350 sal_uLong i = 0;
351 while ( true )
353 if ( pRGBTable[ i ].name == nullptr )
354 break;
355 if ( std::strlen(pRGBTable[i].name) > mnParaSize &&
356 pRGBTable[ i ].name[ mnParaSize ] == 0 )
358 if ( ImplCompare ( reinterpret_cast<unsigned char const *>(pRGBTable[ i ].name),
359 mpPara, mnParaSize ) )
361 bColStatus = true;
362 rDest[0] = 0;
363 rDest[1] = pRGBTable[i].red;
364 rDest[2] = pRGBTable[i].green;
365 rDest[3] = pRGBTable[i].blue;
366 break;
369 i++;
373 return bColStatus;
376 // ImplGetColKey searches string mpStringBuf for a parameter 'nKey'
377 // and returns a boolean. (if TRUE mpPara and mnParaSize will be set)
379 bool XPMReader::ImplGetColKey( sal_uInt8 nKey )
381 sal_uInt8 nTemp, nPrev = ' ';
383 if (mnStringSize < mnCpp + 1)
384 return false;
386 mpPara = mpStringBuf + mnCpp + 1;
387 mnParaSize = 0;
389 while ( *mpPara != 0 )
391 if ( *mpPara == nKey )
393 nTemp = *( mpPara + 1 );
394 if ( nTemp == ' ' || nTemp == 0x09 )
396 if ( nPrev == ' ' || nPrev == 0x09 )
397 break;
400 nPrev = *mpPara;
401 mpPara++;
403 if ( *mpPara )
405 mpPara++;
406 while ( (*mpPara == ' ') || (*mpPara == 0x09) )
408 mpPara++;
410 if ( *mpPara != 0 )
412 while ( *(mpPara+mnParaSize) != ' ' && *(mpPara+mnParaSize) != 0x09 &&
413 *(mpPara+mnParaSize) != 0 )
415 mnParaSize++;
419 return mnParaSize != 0;
422 // ImplGetRGBHex translates the ASCII-Hexadecimalvalue belonging to mpPara
423 // in a RGB value and writes this to rDest
424 // below formats should be contained in mpPara:
425 // if nAdd = 0 : '#12ab12' -> RGB = 0x12, 0xab, 0x12
426 // 2 : '#1234abcd1234' " " " "
427 // 6 : '#12345678abcdefab12345678' " " " "
429 void XPMReader::ImplGetRGBHex(ColorData& rDest, sal_uLong nAdd)
431 sal_uInt8* pPtr = mpPara+1;
433 for (sal_uLong i = 1; i < 4; ++i)
435 sal_uInt8 nHex = (*pPtr++) - '0';
436 if ( nHex > 9 )
437 nHex = ((nHex - 'A' + '0') & 7) + 10;
439 sal_uInt8 nTemp = (*pPtr++) - '0';
440 if ( nTemp > 9 )
441 nTemp = ((nTemp - 'A' + '0') & 7) + 10;
442 nHex = ( nHex << 4 ) + nTemp;
444 pPtr += nAdd;
445 rDest[i] = nHex;
449 // ImplGetUlong returns the value of a up to 6-digit long ASCII-decimal number.
451 sal_uLong XPMReader::ImplGetULONG( sal_uLong nPara )
453 if ( ImplGetPara ( nPara ) )
455 sal_uLong nRetValue = 0;
456 sal_uInt8* pPtr = mpPara;
458 if ( ( mnParaSize > 6 ) || ( mnParaSize == 0 ) ) return 0;
459 for ( sal_uLong i = 0; i < mnParaSize; i++ )
461 sal_uInt8 j = (*pPtr++) - 48;
462 if ( j > 9 ) return 0; // ascii is invalid
463 nRetValue*=10;
464 nRetValue+=j;
466 return nRetValue;
468 else return 0;
471 bool XPMReader::ImplCompare(sal_uInt8 const * pSource, sal_uInt8 const * pDest, sal_uLong nSize)
473 for (sal_uLong i = 0; i < nSize; ++i)
475 if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
477 return false;
480 return true;
483 // ImplGetPara tries to retrieve nNumb (0...x) parameters from mpStringBuf.
484 // Parameters are separated by spaces or tabs.
485 // If a parameter was found then the return value is TRUE and mpPara + mnParaSize
486 // are set.
488 bool XPMReader::ImplGetPara ( sal_uLong nNumb )
490 sal_uInt8 nByte;
491 sal_uLong nSize = 0;
492 sal_uInt8* pPtr = mpStringBuf;
493 sal_uLong nCount = 0;
495 if ( ( *pPtr != ' ' ) && ( *pPtr != 0x09 ) )
497 mpPara = pPtr;
498 mnParaSize = 0;
499 nCount = 0;
501 else
503 mpPara = nullptr;
504 nCount = 0xffffffff;
507 while ( nSize < mnStringSize )
509 nByte = *pPtr;
511 if ( mpPara )
513 if ( ( nByte == ' ' ) || ( nByte == 0x09 ) )
515 if ( nCount == nNumb )
516 break;
517 else
518 mpPara = nullptr;
520 else
521 mnParaSize++;
523 else
525 if ( ( nByte != ' ' ) && ( nByte != 0x09 ) )
527 mpPara = pPtr;
528 mnParaSize = 1;
529 nCount++;
532 nSize++;
533 pPtr++;
535 return ( ( nCount == nNumb ) && mpPara );
538 // The next string is read and stored in mpStringBuf (terminated with 0);
539 // mnStringSize contains the size of the string read.
540 // Comments like '//' and '/*...*/' are skipped.
542 bool XPMReader::ImplGetString()
544 sal_uInt8 const sID[] = "/* XPM */";
545 sal_uInt8* pString = mpStringBuf;
547 mnStringSize = 0;
548 mpStringBuf[0] = 0;
550 while( mbStatus && ( mnStatus != XPMFINISHED ) )
552 if ( mnTempAvail == 0 )
554 mnTempAvail = mrStream.ReadBytes( mpTempBuf, XPMTEMPBUFSIZE );
555 if ( mnTempAvail == 0 )
556 break;
558 mpTempPtr = mpTempBuf;
560 if ( mnIdentifier == XPMIDENTIFIER )
562 if ( mnTempAvail <= 50 )
564 mbStatus = false; // file is too short to be a correct XPM format
565 break;
567 for ( int i = 0; i < 9; i++ ) // searching for "/* XPM */"
568 if ( *mpTempPtr++ != sID[i] )
570 mbStatus = false;
571 break;
573 mnTempAvail-=9;
574 mnIdentifier++;
577 mcLastByte = mcThisByte;
578 mcThisByte = *mpTempPtr++;
579 mnTempAvail--;
581 if ( mnStatus & XPMDOUBLE )
583 if ( mcThisByte == 0x0a )
584 mnStatus &=~XPMDOUBLE;
585 continue;
587 if ( mnStatus & XPMREMARK )
589 if ( ( mcThisByte == '/' ) && ( mcLastByte == '*' ) )
590 mnStatus &=~XPMREMARK;
591 continue;
593 if ( mnStatus & XPMSTRING ) // characters in string
595 if ( mcThisByte == '"' )
597 mnStatus &=~XPMSTRING; // end of parameter by eol
598 break;
600 if ( mnStringSize >= ( XPMSTRINGBUF - 1 ) )
602 mbStatus = false;
603 break;
605 *pString++ = mcThisByte;
606 pString[0] = 0;
607 mnStringSize++;
608 continue;
610 else
611 { // characters beside string
612 switch ( mcThisByte )
614 case '*' :
615 if ( mcLastByte == '/' ) mnStatus |= XPMREMARK;
616 break;
617 case '/' :
618 if ( mcLastByte == '/' ) mnStatus |= XPMDOUBLE;
619 break;
620 case '"' : mnStatus |= XPMSTRING;
621 break;
622 case '{' :
623 if ( mnIdentifier == XPMDEFINITION )
624 mnIdentifier++;
625 break;
626 case '}' :
627 if ( mnIdentifier == XPMENDEXT )
628 mnStatus = XPMFINISHED;
629 break;
633 return mbStatus;
637 VCL_DLLPUBLIC bool ImportXPM(SvStream& rStream, Graphic& rGraphic)
639 XPMReader aXPMReader(rStream);
641 BitmapEx aBitmapEx;
642 ReadState eReadState = aXPMReader.ReadXPM(aBitmapEx);
644 if (eReadState == XPMREAD_ERROR)
645 return false;
646 rGraphic = Graphic(aBitmapEx);
647 return true;
650 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */