Bump version to 6.4-15
[LibreOffice.git] / filter / source / flash / swfwriter2.cxx
blob428c807c50ca485210d4a7501b86e5ae3f1461b4
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 "swfwriter.hxx"
21 #include <vcl/virdev.hxx>
22 #include <basegfx/matrix/b2dhommatrixtools.hxx>
23 #include <tools/debug.hxx>
25 #include <math.h>
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 )
34 sal_uInt16 nBits = 0;
36 while( nValue )
38 nBits++;
39 nValue >>= 1;
42 return nBits;
46 sal_uInt16 getMaxBitsSigned( sal_Int32 nValue )
48 if( nValue < 0 )
49 nValue *= -1;
51 return getMaxBitsUnsigned( static_cast< sal_uInt32 >(nValue) ) + 1;
55 BitStream::BitStream()
57 mnBitPos = 8;
58 mnCurrentByte = 0;
62 void BitStream::writeUB( sal_uInt32 nValue, sal_uInt16 nBits )
64 while( nBits != 0 )
66 mnCurrentByte |= nValue << (32 - nBits) >> (32 - mnBitPos);
68 if ( nBits > mnBitPos )
70 nBits = nBits - mnBitPos;
71 mnBitPos = 0;
73 else
75 mnBitPos = sal::static_int_cast<sal_uInt8>( mnBitPos - nBits );
76 nBits = 0;
79 if( 0 == mnBitPos )
80 pad();
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 );
97 void BitStream::pad()
99 if( 8 != mnBitPos )
101 maData.push_back( mnCurrentByte );
102 mnCurrentByte = 0;
103 mnBitPos = 8;
108 void BitStream::writeTo( SvStream& out )
110 pad();
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 )
127 mnTagId = 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 );
145 if( bLarge )
147 sal_uInt32 nTmp = nSz;
149 out.WriteUChar( nTmp );
150 nTmp >>= 8;
151 out.WriteUChar( nTmp );
152 nTmp >>= 8;
153 out.WriteUChar( nTmp );
154 nTmp >>= 8;
155 out.WriteUChar( nTmp );
159 out.WriteBytes( GetData(), nSz );
161 #if 0
164 void Tag::addI32( sal_Int32 nValue )
166 addUI32( static_cast<sal_uInt32>( nValue ) );
168 #endif
171 void Tag::addUI32( sal_uInt32 nValue )
173 WriteUInt32( nValue );
175 #if 0
178 void Tag::addI16( sal_Int16 nValue )
180 addUI16( static_cast<sal_uInt16>( nValue ) );
182 #endif
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 )
229 BitStream aBits;
231 sal_Int32 minX, minY, maxX, maxY;
233 if( rRect.Left() < rRect.Right() )
235 minX = rRect.Left();
236 maxX = rRect.Right();
238 else
240 maxX = rRect.Left();
241 minX = rRect.Right();
245 if( rRect.Top() < rRect.Bottom() )
247 minY = rRect.Top();
248 maxY = rRect.Bottom();
250 else
252 maxY = rRect.Top();
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
261 // the page origin
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#
285 BitStream aBits;
287 const bool bHasScale = rMatrix.get(0, 0) != 1.0 || rMatrix.get(1, 1) != 1.0;
289 aBits.writeUB( int(bHasScale), 1 );
291 if( bHasScale )
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 );
304 if( bHasRotate )
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)
335 Sprite::~Sprite()
340 void Sprite::write( SvStream& out )
342 SvMemoryStream aTmp;
343 for (auto const& tag : maTags)
344 tag->write( aTmp );
346 if( !mnFrames )
347 mnFrames = 1;
349 aTmp.Seek(0);
351 Tag aTag( TAG_DEFINESPRITE );
352 aTag.addUI16( mnId );
353 aTag.addUI16( uInt16_( mnFrames ) );
354 aTag.addStream( aTmp );
355 aTag.write( out );
359 void Sprite::addTag( std::unique_ptr<Tag> pNewTag )
361 if( pNewTag->getTagId() == TAG_SHOWFRAME )
362 mnFrames++;
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;
374 temp |= nLower;
376 return temp;
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();
422 sal_uInt16 i,n;
423 for( i = 0; i < nCount; i++ )
425 tools::Polygon& rPoly = aPolyPoly[ i ];
427 const sal_uInt16 nSize = rPoly.GetSize();
428 if( nSize )
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())) );
436 rPoly[n] = aPoint;
438 Writer::Impl_addPolygon( maGlyphData, rPoly, true );
441 Writer::Impl_addEndShapeRecord( maGlyphData );
443 maGlyphData.pad();
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 );
465 aTag.write( out );
469 /** this c'tor creates a solid fill style */
470 FillStyle::FillStyle( const Color& rSolidColor )
471 : meType(solid )
472 , mnBitmapId(0)
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 ),
481 maMatrix( rMatrix ),
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:
498 default:
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))
507 , mnBitmapId(0)
508 , maGradient(rGradient)
509 , maBoundRect(rBoundRect)
514 void FillStyle::addTo( Tag* pTag ) const
516 pTag->addUI8( sal::static_int_cast<sal_uInt8>( meType ) );
517 switch( meType )
519 case solid:
520 pTag->addRGBA( maColor );
521 break;
522 case linear_gradient:
523 case radial_gradient:
524 Impl_addGradient( pTag );
525 break;
526 case tiled_bitmap:
527 case clipped_bitmap:
528 pTag->addUI16( mnBitmapId );
529 pTag->addMatrix( maMatrix );
530 break;
535 struct GradRecord
537 sal_uInt8 mnRatio;
538 Color maColor;
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;
562 m.scale( 1.2, 1.2 );
564 if( scalex > scaley )
566 double scale_move = scaley / scalex;
568 m.translate( tx, scale_move * ty );
571 m.scale( scalex, scalex );
573 else
575 double scale_move = scalex / scaley;
577 m.translate( scale_move * tx, ty );
580 m.scale( scaley, scaley );
584 break;
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 );
595 break;
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 );
609 break;
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: */