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 .
20 #include "swfwriter.hxx"
21 #include <vcl/virdev.hxx>
22 #include <basegfx/matrix/b2dhommatrixtools.hxx>
23 #include <tools/debug.hxx>
27 using namespace ::swf
;
28 using namespace ::com::sun::star::uno
;
29 using namespace ::com::sun::star::io
;
32 static sal_uInt16
getMaxBitsUnsigned( sal_uInt32 nValue
)
46 sal_uInt16
getMaxBitsSigned( sal_Int32 nValue
)
51 return getMaxBitsUnsigned( static_cast< sal_uInt32
>(nValue
) ) + 1;
55 BitStream::BitStream()
62 void BitStream::writeUB( sal_uInt32 nValue
, sal_uInt16 nBits
)
66 mnCurrentByte
|= nValue
<< (32 - nBits
) >> (32 - mnBitPos
);
68 if ( nBits
> mnBitPos
)
70 nBits
= nBits
- mnBitPos
;
75 mnBitPos
= sal::static_int_cast
<sal_uInt8
>( mnBitPos
- nBits
);
85 void BitStream::writeSB( sal_Int32 nValue
, sal_uInt16 nBits
)
87 writeUB( static_cast< sal_uInt32
>(nValue
), nBits
);
91 void BitStream::writeFB( sal_uInt32 nValue
, sal_uInt16 nBits
)
93 writeUB( nValue
, nBits
);
101 maData
.push_back( mnCurrentByte
);
108 void BitStream::writeTo( SvStream
& out
)
112 for (auto const& data
: maData
)
114 out
.WriteUChar(data
);
119 sal_uInt32
BitStream::getOffset() const
121 return maData
.size();
125 Tag::Tag( sal_uInt8 nTagId
)
131 void Tag::write( SvStream
&out
)
133 sal_uInt32 nSz
= TellEnd();
134 Seek( STREAM_SEEK_TO_BEGIN
);
136 if( mnTagId
!= 0xff )
138 bool bLarge
= nSz
> 62;
140 sal_uInt16 nCode
= ( mnTagId
<< 6 ) | ( bLarge
? 0x3f : uInt16_(nSz
) );
142 out
.WriteUChar( nCode
);
143 out
.WriteUChar( nCode
>> 8 );
147 sal_uInt32 nTmp
= nSz
;
149 out
.WriteUChar( nTmp
);
151 out
.WriteUChar( nTmp
);
153 out
.WriteUChar( nTmp
);
155 out
.WriteUChar( nTmp
);
159 out
.WriteBytes( GetData(), nSz
);
164 void Tag::addI32( sal_Int32 nValue
)
166 addUI32( static_cast<sal_uInt32
>( nValue
) );
171 void Tag::addUI32( sal_uInt32 nValue
)
173 WriteUInt32( nValue
);
178 void Tag::addI16( sal_Int16 nValue
)
180 addUI16( static_cast<sal_uInt16
>( nValue
) );
185 void Tag::addUI16( sal_uInt16 nValue
)
187 WriteUChar( nValue
);
188 WriteUChar( nValue
>> 8 );
192 void Tag::addUI8( sal_uInt8 nValue
)
194 WriteUChar( nValue
);
198 void Tag::addBits( BitStream
& rIn
)
200 rIn
.writeTo( *this );
204 void Tag::addRGBA( const Color
& rColor
)
206 addUI8( rColor
.GetRed() );
207 addUI8( rColor
.GetGreen() );
208 addUI8( rColor
.GetBlue() );
209 addUI8( 0xff - rColor
.GetTransparency() );
213 void Tag::addRGB( const Color
& rColor
)
215 addUI8( rColor
.GetRed() );
216 addUI8( rColor
.GetGreen() );
217 addUI8( rColor
.GetBlue() );
221 void Tag::addRect( const tools::Rectangle
& rRect
)
223 writeRect( *this, rRect
);
227 void Tag::writeRect( SvStream
& rOut
, const tools::Rectangle
& rRect
)
231 sal_Int32 minX
, minY
, maxX
, maxY
;
233 if( rRect
.Left() < rRect
.Right() )
236 maxX
= rRect
.Right();
241 minX
= rRect
.Right();
245 if( rRect
.Top() < rRect
.Bottom() )
248 maxY
= rRect
.Bottom();
253 minY
= rRect
.Bottom();
256 // AS: Figure out the maximum number of bits required to represent any of the
257 // rectangle coordinates. Since minX or minY could be negative, they could
258 // actually require more bits than maxX or maxY.
259 // AS: Christian, can they be negative, or is that a wasted check?
260 // CL: I think so, f.e. for shapes that have the top and/or left edge outside
262 sal_uInt8 nBits1
= sal::static_int_cast
<sal_uInt8
>( std::max( getMaxBitsSigned( minX
), getMaxBitsSigned( minY
) ) );
263 sal_uInt8 nBits2
= sal::static_int_cast
<sal_uInt8
>( std::max( getMaxBitsSigned( maxX
), getMaxBitsSigned( maxY
) ) );
264 sal_uInt8 nBitsMax
= std::max( nBits1
, nBits2
);
266 aBits
.writeUB( nBitsMax
, 5 );
267 aBits
.writeSB( minX
, nBitsMax
);
268 aBits
.writeSB( maxX
, nBitsMax
);
269 aBits
.writeSB( minY
, nBitsMax
);
270 aBits
.writeSB( maxY
, nBitsMax
);
272 aBits
.writeTo( rOut
);
276 void Tag::addMatrix( const ::basegfx::B2DHomMatrix
& rMatrix
) // #i73264#
278 writeMatrix( *this, rMatrix
);
282 void Tag::writeMatrix( SvStream
& rOut
, const ::basegfx::B2DHomMatrix
& rMatrix
) // #i73264#
287 const bool bHasScale
= rMatrix
.get(0, 0) != 1.0 || rMatrix
.get(1, 1) != 1.0;
289 aBits
.writeUB( int(bHasScale
), 1 );
293 sal_uInt8 nScaleBits
= 31;
295 aBits
.writeUB( nScaleBits
, 5 );
296 aBits
.writeFB( getFixed( rMatrix
.get(0, 0) ), nScaleBits
); // Scale X
297 aBits
.writeFB( getFixed( rMatrix
.get(1, 1) ), nScaleBits
); // Scale Y
300 const bool bHasRotate
= rMatrix
.get(0, 1) != 0.0 || rMatrix
.get(1, 0) != 0.0;
302 aBits
.writeUB( int(bHasRotate
), 1 );
306 sal_uInt8 nRotateBits
= 31;
308 aBits
.writeUB( nRotateBits
, 5 );
309 aBits
.writeFB( getFixed( rMatrix
.get(0, 1) ), nRotateBits
); // RotateSkew0
310 aBits
.writeFB( getFixed( rMatrix
.get(1, 0) ), nRotateBits
); // RotateSkew1
313 sal_uInt8 nTranslateBits
= 16;
315 aBits
.writeUB( nTranslateBits
, 5 );
316 aBits
.writeSB( static_cast<sal_Int16
>(rMatrix
.get(0, 2)), nTranslateBits
); // Translate X
317 aBits
.writeSB( static_cast<sal_Int16
>(rMatrix
.get(1, 2)), nTranslateBits
); // Translate Y
319 aBits
.writeTo( rOut
);
323 void Tag::addStream( SvStream
& rIn
)
325 (*this).WriteStream( rIn
);
329 Sprite::Sprite( sal_uInt16 nId
)
330 : mnId( nId
), mnFrames(0)
340 void Sprite::write( SvStream
& out
)
343 for (auto const& tag
: maTags
)
351 Tag
aTag( TAG_DEFINESPRITE
);
352 aTag
.addUI16( mnId
);
353 aTag
.addUI16( uInt16_( mnFrames
) );
354 aTag
.addStream( aTmp
);
359 void Sprite::addTag( std::unique_ptr
<Tag
> pNewTag
)
361 if( pNewTag
->getTagId() == TAG_SHOWFRAME
)
364 maTags
.push_back( std::move(pNewTag
) );
368 sal_uInt32
swf::getFixed( double fValue
)
370 sal_Int16 nUpper
= static_cast<sal_Int16
>(floor(fValue
));
371 sal_uInt16 nLower
= static_cast<sal_uInt16
>((fValue
- floor(fValue
))*0x10000);
373 sal_uInt32 temp
= static_cast<sal_Int32
>(nUpper
)<<16;
380 /** constructs a new flash font for the given VCL Font */
381 FlashFont::FlashFont( const vcl::Font
& rFont
, sal_uInt16 nId
)
382 : maFont( rFont
), mnNextIndex(0), mnId( nId
)
387 FlashFont::~FlashFont()
392 /** gets the glyph id for the given character. The glyphs are created on demand */
393 sal_uInt16
FlashFont::getGlyph( sal_uInt16 nChar
, VirtualDevice
* pVDev
)
395 // see if we already created a glyph for this character
396 std::map
<sal_uInt16
, sal_uInt16
>::iterator
aIter( maGlyphIndex
.find(nChar
) );
397 if( aIter
!= maGlyphIndex
.end() )
399 return aIter
->second
;
402 // if not, we create one now
404 maGlyphIndex
[nChar
] = mnNextIndex
;
406 vcl::Font
aOldFont( pVDev
->GetFont() );
407 vcl::Font
aNewFont( aOldFont
);
408 aNewFont
.SetAlignment( ALIGN_BASELINE
);
409 pVDev
->SetFont( aNewFont
);
410 aOldFont
.SetOrientation(0);
412 // let the virtual device convert the character to polygons
413 tools::PolyPolygon aPolyPoly
;
414 pVDev
->GetTextOutline( aPolyPoly
, OUString(sal_Unicode(nChar
)) );
416 maGlyphOffsets
.push_back( uInt16_( maGlyphData
.getOffset() ) );
418 // Number of fill and line index bits set to 1
419 maGlyphData
.writeUB( 0x11, 8 );
421 const sal_uInt16 nCount
= aPolyPoly
.Count();
423 for( i
= 0; i
< nCount
; i
++ )
425 tools::Polygon
& rPoly
= aPolyPoly
[ i
];
427 const sal_uInt16 nSize
= rPoly
.GetSize();
430 // convert polygon to flash EM_SQUARE (1024x1024)
431 for( n
= 0; n
< nSize
; n
++ )
433 Point
aPoint( rPoly
[n
] );
434 aPoint
.setX( static_cast<long>((double(aPoint
.X()) * 1024.0 ) / double(aOldFont
.GetFontHeight())) );
435 aPoint
.setY( static_cast<long>((double(aPoint
.Y()) * 1024.0 ) / double(aOldFont
.GetFontHeight())) );
438 Writer::Impl_addPolygon( maGlyphData
, rPoly
, true );
441 Writer::Impl_addEndShapeRecord( maGlyphData
);
445 pVDev
->SetFont( aOldFont
);
447 return mnNextIndex
++;
451 void FlashFont::write( SvStream
& out
)
453 Tag
aTag( TAG_DEFINEFONT
);
455 aTag
.addUI16( mnId
);
457 sal_uInt16 nGlyphs
= uInt16_( maGlyphOffsets
.size() );
458 sal_uInt16 nOffset
= nGlyphs
* sizeof( sal_uInt16
);
460 for (auto const& glyphOffset
: maGlyphOffsets
)
461 aTag
.addUI16( nOffset
+ glyphOffset
);
463 aTag
.addBits( maGlyphData
);
469 /** this c'tor creates a solid fill style */
470 FillStyle::FillStyle( const Color
& rSolidColor
)
473 , maColor(rSolidColor
)
478 /** this c'tor creates a tiled or clipped bitmap fill style */
479 FillStyle::FillStyle( sal_uInt16 nBitmapId
, bool bClipped
, const ::basegfx::B2DHomMatrix
& rMatrix
) // #i73264#
480 : meType( bClipped
? clipped_bitmap
: tiled_bitmap
),
482 mnBitmapId( nBitmapId
)
487 static FillStyle::FillStyleType
Impl_getFillStyleType( const Gradient
& rGradient
)
489 switch( rGradient
.GetStyle() )
491 case GradientStyle::Elliptical
:
492 case GradientStyle::Radial
:
493 return FillStyle::radial_gradient
;
494 // case GradientStyle::Axial:
495 // case GradientStyle::Square:
496 // case GradientStyle::Rect:
497 // case GradientStyle::Linear:
499 return FillStyle::linear_gradient
;
504 /** this c'tor creates a linear or radial gradient fill style */
505 FillStyle::FillStyle( const tools::Rectangle
& rBoundRect
, const Gradient
& rGradient
)
506 : meType(Impl_getFillStyleType(rGradient
))
508 , maGradient(rGradient
)
509 , maBoundRect(rBoundRect
)
514 void FillStyle::addTo( Tag
* pTag
) const
516 pTag
->addUI8( sal::static_int_cast
<sal_uInt8
>( meType
) );
520 pTag
->addRGBA( maColor
);
522 case linear_gradient
:
523 case radial_gradient
:
524 Impl_addGradient( pTag
);
528 pTag
->addUI16( mnBitmapId
);
529 pTag
->addMatrix( maMatrix
);
540 GradRecord( sal_uInt8 nRatio
, const Color
& rColor
) : mnRatio( nRatio
), maColor( rColor
) {}
543 // TODO: better emulation of our gradients
544 void FillStyle::Impl_addGradient( Tag
* pTag
) const
546 std::vector
< struct GradRecord
> aGradientRecords
;
547 basegfx::B2DHomMatrix
m(basegfx::utils::createRotateB2DHomMatrix((maGradient
.GetAngle() - 900) * F_PI1800
));
549 switch( maGradient
.GetStyle() )
551 case GradientStyle::Elliptical
:
552 case GradientStyle::Radial
:
554 aGradientRecords
.emplace_back( 0x00, maGradient
.GetEndColor() );
555 aGradientRecords
.emplace_back( 0xff, maGradient
.GetStartColor() );
557 double tx
= ( maGradient
.GetOfsX() * 32768.0 ) / 100.0;
558 double ty
= ( maGradient
.GetOfsY() * 32768.0 ) / 100.0;
559 double scalex
= static_cast<double>(maBoundRect
.GetWidth()) / 32768.0;
560 double scaley
= static_cast<double>(maBoundRect
.GetHeight()) / 32768.0;
564 if( scalex
> scaley
)
566 double scale_move
= scaley
/ scalex
;
568 m
.translate( tx
, scale_move
* ty
);
571 m
.scale( scalex
, scalex
);
575 double scale_move
= scalex
/ scaley
;
577 m
.translate( scale_move
* tx
, ty
);
580 m
.scale( scaley
, scaley
);
585 case GradientStyle::Axial
:
587 aGradientRecords
.emplace_back( 0x00, maGradient
.GetEndColor() );
588 aGradientRecords
.emplace_back( 0x80, maGradient
.GetStartColor() );
589 aGradientRecords
.emplace_back( 0xff, maGradient
.GetEndColor() );
590 double scalex
= static_cast<double>(maBoundRect
.GetWidth()) / 32768.0;
591 double scaley
= static_cast<double>(maBoundRect
.GetHeight()) / 32768.0;
592 m
.translate( 32768.0 / 2.0, 32768.0 / 2.0 );
593 m
.scale( scalex
, scaley
);
596 case GradientStyle::Square
:
597 case GradientStyle::Rect
:
598 case GradientStyle::Linear
:
600 aGradientRecords
.emplace_back( 0x00, maGradient
.GetStartColor() );
601 aGradientRecords
.emplace_back( 0xff, maGradient
.GetEndColor() );
602 double scalex
= static_cast<double>(maBoundRect
.GetWidth()) / 32768.0;
603 double scaley
= static_cast<double>(maBoundRect
.GetHeight()) / 32768.0;
605 m
.scale( scalex
, scaley
);
607 m
.translate( maBoundRect
.GetWidth() / 2.0, maBoundRect
.GetHeight() / 2.0 );
610 case GradientStyle::FORCE_EQUAL_SIZE
: break;
613 m
.translate( maBoundRect
.Left(), maBoundRect
.Top() );
615 pTag
->addMatrix( m
);
617 DBG_ASSERT( aGradientRecords
.size() < 8, "Illegal FlashGradient!" );
619 pTag
->addUI8( static_cast<sal_uInt8
>( aGradientRecords
.size() ) );
621 for (auto const& gradientRecord
: aGradientRecords
)
623 pTag
->addUI8( gradientRecord
.mnRatio
);
624 pTag
->addRGBA( gradientRecord
.maColor
);
628 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */