1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
21 #include <sal/log.hxx>
22 #include <comphelper/configuration.hxx>
23 #include <vcl/BitmapTools.hxx>
27 #include "elements.hxx"
31 Color
BMCOL(sal_uInt32 _col
) {
32 return Color( static_cast<sal_Int8
>(_col
>> 16 ), static_cast<sal_Int8
>( _col
>> 8 ), static_cast<sal_Int8
>(_col
) );
37 CGMBitmap::CGMBitmap(CGM
& rCGM
)
39 , pCGMBitmapDescriptor(new CGMBitmapDescriptor
)
41 ImplGetBitmap( *pCGMBitmapDescriptor
);
44 CGMBitmap::~CGMBitmap()
50 bool isLegalBitsPerPixel(sal_uInt32 nBitsPerPixel
)
52 switch (nBitsPerPixel
)
67 void CGMBitmap::ImplGetBitmap( CGMBitmapDescriptor
& rDesc
)
69 rDesc
.mbStatus
= true;
71 if (!ImplGetDimensions(rDesc
) || !rDesc
.mpBuf
)
74 if (!isLegalBitsPerPixel(rDesc
.mnDstBitsPerPixel
))
76 rDesc
.mbStatus
= false;
84 vcl::bitmap::RawBitmap
aBitmap( Size( rDesc
.mnX
, rDesc
.mnY
), 24 );
86 // the picture may either be read from left to right or right to left, from top to bottom ...
88 tools::Long nxCount
= rDesc
.mnX
+ 1; // +1 because we are using prefix decreasing
89 tools::Long nyCount
= rDesc
.mnY
+ 1;
90 tools::Long nx
, ny
, nxC
;
92 switch ( rDesc
.mnDstBitsPerPixel
) {
94 std::vector
<Color
> palette(2);
95 if ( rDesc
.mnLocalColorPrecision
== 1 )
96 palette
= ImplGeneratePalette( rDesc
);
98 palette
[0] = BMCOL( mpCGM
->pElement
->nBackGroundColor
);
99 palette
[1] = ( mpCGM
->pElement
->nAspectSourceFlags
& ASF_FILLINTERIORSTYLE
)
100 ? BMCOL( mpCGM
->pElement
->pFillBundle
->GetColor() )
101 : BMCOL( mpCGM
->pElement
->aFillBundle
.GetColor() );
103 for (ny
= 0; rDesc
.mbStatus
&& --nyCount
; ny
++, rDesc
.mpBuf
+= rDesc
.mnScanSize
) {
105 for ( nx
= 0; --nxC
; nx
++ ) {
106 // this is not fast, but a one bit/pixel format is rarely used
107 const sal_uInt8
* pPos
= rDesc
.mpBuf
+ (nx
>> 3);
108 if (pPos
>= rDesc
.mpEndBuf
)
110 SAL_WARN("filter.icgm", "buffer is too small");
111 rDesc
.mbStatus
= false;
114 sal_uInt8 colorIndex
= static_cast<sal_uInt8
>((*pPos
>> ((nx
& 7)^7))) & 1;
115 aBitmap
.SetPixel(ny
, nx
, palette
[colorIndex
]);
122 auto palette
= ImplGeneratePalette( rDesc
);
123 for (ny
= 0; rDesc
.mbStatus
&& --nyCount
; ny
++, rDesc
.mpBuf
+= rDesc
.mnScanSize
) {
125 for ( nx
= 0; --nxC
; nx
++ ) {
126 // this is not fast, but a two bits/pixel format is rarely used
127 const sal_uInt8
* pPos
= rDesc
.mpBuf
+ (nx
>> 2);
128 if (pPos
>= rDesc
.mpEndBuf
)
130 SAL_WARN("filter.icgm", "buffer is too small");
131 rDesc
.mbStatus
= false;
134 aBitmap
.SetPixel(ny
, nx
, palette
[static_cast<sal_uInt8
>( (*pPos
>> (((nx
& 3)^3) << 1))) & 3]);
141 auto palette
= ImplGeneratePalette( rDesc
);
142 for (ny
= 0; rDesc
.mbStatus
&& --nyCount
; ny
++, rDesc
.mpBuf
+= rDesc
.mnScanSize
) {
144 sal_uInt8
* pTemp
= rDesc
.mpBuf
;
145 for ( nx
= 0; --nxC
; nx
++ ) {
147 if (pTemp
>= rDesc
.mpEndBuf
)
149 SAL_WARN("filter.icgm", "buffer is too small");
150 rDesc
.mbStatus
= false;
154 sal_uInt8 nDat
= *pTemp
++;
156 aBitmap
.SetPixel(ny
, nx
, palette
[static_cast<sal_uInt8
>(nDat
>> 4)]);
159 aBitmap
.SetPixel(ny
, nx
, palette
[static_cast<sal_uInt8
>(nDat
& 15)]);
168 auto palette
= ImplGeneratePalette( rDesc
);
169 for (ny
= 0; rDesc
.mbStatus
&& --nyCount
; ny
++, rDesc
.mpBuf
+= rDesc
.mnScanSize
) {
170 sal_uInt8
* pTemp
= rDesc
.mpBuf
;
172 for ( nx
= 0; --nxC
; nx
++ ) {
174 if (pTemp
>= rDesc
.mpEndBuf
)
176 SAL_WARN("filter.icgm", "buffer is too small");
177 rDesc
.mbStatus
= false;
181 aBitmap
.SetPixel(ny
, nx
, palette
[*(pTemp
++)]);
189 for (ny
= 0; rDesc
.mbStatus
&& --nyCount
; ny
++, rDesc
.mpBuf
+= rDesc
.mnScanSize
) {
190 sal_uInt8
* pTemp
= rDesc
.mpBuf
;
192 for ( nx
= 0; --nxC
; nx
++ ) {
194 if (pTemp
+ 2 >= rDesc
.mpEndBuf
)
196 SAL_WARN("filter.icgm", "buffer is too small");
197 rDesc
.mbStatus
= false;
201 aBitmapColor
.SetRed( *pTemp
++ );
202 aBitmapColor
.SetGreen( *pTemp
++ );
203 aBitmapColor
.SetBlue( *pTemp
++ );
204 aBitmap
.SetPixel(ny
, nx
, aBitmapColor
);
211 if ( rDesc
.mbStatus
)
212 rDesc
.mxBitmap
= vcl::bitmap::CreateFromData(std::move(aBitmap
));
215 double nX
= rDesc
.mnR
.X
- rDesc
.mnQ
.X
;
216 double nY
= rDesc
.mnR
.Y
- rDesc
.mnQ
.Y
;
218 rDesc
.mndy
= std::hypot(nX
, nY
);
220 nX
= rDesc
.mnR
.X
- rDesc
.mnP
.X
;
221 nY
= rDesc
.mnR
.Y
- rDesc
.mnP
.Y
;
223 rDesc
.mndx
= std::hypot(nX
, nY
);
225 nX
= rDesc
.mnR
.X
- rDesc
.mnP
.X
;
226 nY
= rDesc
.mnR
.Y
- rDesc
.mnP
.Y
;
228 double fSqrt
= std::hypot(nX
, nY
);
229 rDesc
.mnOrientation
= fSqrt
!= 0.0 ? basegfx::rad2deg(acos(nX
/ fSqrt
)) : 0.0;
231 rDesc
.mnOrientation
= 360 - rDesc
.mnOrientation
;
233 nX
= rDesc
.mnQ
.X
- rDesc
.mnR
.X
;
234 nY
= rDesc
.mnQ
.Y
- rDesc
.mnR
.Y
;
236 double fAngle
= basegfx::deg2rad( 360 - rDesc
.mnOrientation
);
237 double fSin
= sin(fAngle
);
238 double fCos
= cos(fAngle
);
239 nX
= fCos
* nX
+ fSin
* nY
;
240 nY
= -( fSin
* nX
- fCos
* nY
);
242 fSqrt
= std::hypot(nX
, nY
);
243 fAngle
= fSqrt
!= 0.0 ? basegfx::rad2deg(acos(nX
/ fSqrt
)) : 0.0;
245 fAngle
= 360 - fAngle
;
247 if ( fAngle
> 180 ) { // is the picture build upwards or downwards ?
248 rDesc
.mnOrigin
= rDesc
.mnP
;
250 rDesc
.mbVMirror
= true;
251 rDesc
.mnOrigin
= rDesc
.mnP
;
252 rDesc
.mnOrigin
.X
+= rDesc
.mnQ
.X
- rDesc
.mnR
.X
;
253 rDesc
.mnOrigin
.Y
+= rDesc
.mnQ
.Y
- rDesc
.mnR
.Y
;
256 catch (const std::bad_alloc
&)
258 rDesc
.mbStatus
= false;
262 std::vector
<Color
> CGMBitmap::ImplGeneratePalette( CGMBitmapDescriptor
const & rDesc
)
264 sal_uInt16 nColors
= sal::static_int_cast
< sal_uInt16
>(
265 1 << rDesc
.mnDstBitsPerPixel
);
266 std::vector
<Color
> palette( nColors
);
267 for ( sal_uInt16 i
= 0; i
< nColors
; i
++ )
269 palette
[i
] = BMCOL( mpCGM
->pElement
->aLatestColorTable
[ i
] );
275 bool CGMBitmap::ImplGetDimensions( CGMBitmapDescriptor
& rDesc
)
277 mpCGM
->ImplGetPoint( rDesc
.mnP
); // parallelogram p < - > r
278 mpCGM
->ImplGetPoint( rDesc
.mnQ
); // |
279 mpCGM
->ImplGetPoint( rDesc
.mnR
); // q
280 sal_uInt32 nPrecision
= mpCGM
->pElement
->nIntegerPrecision
;
281 rDesc
.mnX
= mpCGM
->ImplGetUI( nPrecision
);
282 rDesc
.mnY
= mpCGM
->ImplGetUI( nPrecision
);
283 rDesc
.mnLocalColorPrecision
= mpCGM
->ImplGetI( nPrecision
);
284 rDesc
.mnScanSize
= 0;
285 switch( rDesc
.mnLocalColorPrecision
)
287 case tools::Long(0x80000001) : // monochrome ( bit = 0->backgroundcolor )
288 case 0 : // bit = 1->fillcolor
289 rDesc
.mnDstBitsPerPixel
= 1;
291 case 1 : // 2 color indexed ( monochrome )
293 rDesc
.mnDstBitsPerPixel
= 1;
295 case 2 : // 4 color indexed
297 rDesc
.mnDstBitsPerPixel
= 2;
299 case 4 : // 16 color indexed
301 rDesc
.mnDstBitsPerPixel
= 4;
303 case 8 : // 256 color indexed
305 rDesc
.mnDstBitsPerPixel
= 8;
306 rDesc
.mnScanSize
= rDesc
.mnX
;
310 rDesc
.mbStatus
= false;
312 case 24 : // 24 bit directColor ( 8 bits each component )
314 rDesc
.mnDstBitsPerPixel
= 24;
318 rDesc
.mbStatus
= false;
322 // mnCompressionMode == 0 : CCOMP_RUNLENGTH
323 // == 1 : CCOMP_PACKED ( no compression. each row starts on a 4 byte boundary )
324 if ( ( rDesc
.mnCompressionMode
= mpCGM
->ImplGetUI16() ) != 1 )
325 rDesc
.mbStatus
= false;
327 if (!rDesc
.mnX
|| !rDesc
.mnY
)
329 rDesc
.mbStatus
= false;
333 sal_uInt32 nHeaderSize
= 2 + 3 * nPrecision
+ 3 * mpCGM
->ImplGetPointSize();
335 sal_uInt32 nWidthBits
;
336 if (o3tl::checked_multiply(rDesc
.mnX
, rDesc
.mnDstBitsPerPixel
, nWidthBits
))
338 rDesc
.mbStatus
= false;
342 rDesc
.mnScanSize
= (nWidthBits
+ 7) >> 3;
344 sal_uInt32 nScanSize
;
345 nScanSize
= rDesc
.mnScanSize
;
346 if ( ( nScanSize
* rDesc
.mnY
+ nHeaderSize
) != mpCGM
->mnElementSize
) // try a scansize without dw alignment
348 nScanSize
= ( rDesc
.mnScanSize
+ 1 ) & ~1;
349 if ( ( nScanSize
* rDesc
.mnY
+ nHeaderSize
) != mpCGM
->mnElementSize
) // then we'll try word alignment
351 nScanSize
= ( rDesc
.mnScanSize
+ 3 ) & ~3;
352 if ( ( nScanSize
* rDesc
.mnY
+ nHeaderSize
) != mpCGM
->mnElementSize
) // and last we'll try dword alignment
354 nScanSize
= ( rDesc
.mnScanSize
+ 1 ) & ~1; // and LAST BUT NOT LEAST we'll try word alignment without aligning the last line
355 if ( ( nScanSize
* ( rDesc
.mnY
- 1 ) + rDesc
.mnScanSize
+ nHeaderSize
) != mpCGM
->mnElementSize
)
357 nScanSize
= ( rDesc
.mnScanSize
+ 3 ) & ~3;
358 if ( ( nScanSize
* ( rDesc
.mnY
- 1 ) + rDesc
.mnScanSize
+ nHeaderSize
) != mpCGM
->mnElementSize
)
360 mpCGM
->mnParaSize
= 0; // this format is corrupt
361 rDesc
.mbStatus
= false;
367 rDesc
.mnScanSize
= nScanSize
;
368 if ( rDesc
.mbStatus
)
370 rDesc
.mpBuf
= mpCGM
->mpSource
+ mpCGM
->mnParaSize
; // mpBuf now points to the first scanline
371 rDesc
.mpEndBuf
= mpCGM
->mpEndValidSource
;
372 mpCGM
->mnParaSize
+= rDesc
.mnScanSize
* rDesc
.mnY
;
374 return rDesc
.mbStatus
;
378 void CGMBitmap::ImplInsert( CGMBitmapDescriptor
const & rSource
, CGMBitmapDescriptor
& rDest
)
380 ++mpCGM
->mnBitmapInserts
;
381 static const bool bFuzzing
= comphelper::IsFuzzing();
384 if (rDest
.mxBitmap
.GetSizePixel().Height() + rSource
.mnY
> SAL_MAX_UINT16
)
386 SAL_WARN("filter.icgm", "bitmap would expand too much");
387 rDest
.mbStatus
= false;
390 if (mpCGM
->mnBitmapInserts
> 1024)
392 SAL_WARN("filter.icgm", "too many inserts");
393 rDest
.mbStatus
= false;
397 rDest
.mxBitmap
.Expand( 0, rSource
.mnY
);
398 rDest
.mxBitmap
.CopyPixel( tools::Rectangle( Point( 0, rDest
.mnY
), Size( rSource
.mnX
, rSource
.mnY
) ),
399 tools::Rectangle( Point( 0, 0 ), Size( rSource
.mnX
, rSource
.mnY
) ), rSource
.mxBitmap
);
401 if ( ( rSource
.mnR
.Y
== rDest
.mnQ
.Y
) && ( rSource
.mnR
.X
== rDest
.mnQ
.X
) )
402 { // Insert on Bottom
403 if ( mpCGM
->mnVDCYmul
== -1 )
404 rDest
.mnOrigin
= rSource
.mnOrigin
; // new origin
405 FloatPoint aFloatPoint
;
406 aFloatPoint
.X
= rSource
.mnQ
.X
- rSource
.mnR
.X
;
407 aFloatPoint
.Y
= rSource
.mnQ
.Y
- rSource
.mnR
.Y
;
408 rDest
.mnQ
.X
+= aFloatPoint
.X
;
409 rDest
.mnQ
.Y
+= aFloatPoint
.Y
;
410 rDest
.mnP
= rSource
.mnP
;
411 rDest
.mnR
= rSource
.mnR
;
415 if ( mpCGM
->mnVDCYmul
== 1 )
416 rDest
.mnOrigin
= rSource
.mnOrigin
; // new origin
417 rDest
.mnP
= rSource
.mnP
;
418 rDest
.mnR
= rSource
.mnR
;
420 rDest
.mnY
+= rSource
.mnY
;
421 rDest
.mndy
+= rSource
.mndy
;
424 std::unique_ptr
<CGMBitmap
> CGMBitmap::GetNext()
426 std::unique_ptr
<CGMBitmap
> xCGMTempBitmap
;
427 if (!pCGMBitmapDescriptor
->mxBitmap
.IsEmpty() && pCGMBitmapDescriptor
->mbStatus
)
429 xCGMTempBitmap
.reset(new CGMBitmap(*mpCGM
));
430 if ( ( static_cast<tools::Long
>(xCGMTempBitmap
->pCGMBitmapDescriptor
->mnOrientation
) == static_cast<tools::Long
>(pCGMBitmapDescriptor
->mnOrientation
) ) &&
431 ( ( ( xCGMTempBitmap
->pCGMBitmapDescriptor
->mnR
.X
== pCGMBitmapDescriptor
->mnQ
.X
) &&
432 ( xCGMTempBitmap
->pCGMBitmapDescriptor
->mnR
.Y
== pCGMBitmapDescriptor
->mnQ
.Y
) ) ||
433 ( ( xCGMTempBitmap
->pCGMBitmapDescriptor
->mnQ
.X
== pCGMBitmapDescriptor
->mnR
.X
) &&
434 ( xCGMTempBitmap
->pCGMBitmapDescriptor
->mnQ
.Y
== pCGMBitmapDescriptor
->mnR
.Y
) ) ) )
436 ImplInsert( *(xCGMTempBitmap
->pCGMBitmapDescriptor
), *pCGMBitmapDescriptor
);
437 xCGMTempBitmap
.reset();
438 return xCGMTempBitmap
;
441 pCGMBitmapDescriptor
.swap(xCGMTempBitmap
->pCGMBitmapDescriptor
);
443 return xCGMTempBitmap
;
446 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */