1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include "swfwriter.hxx"
21 #include <vcl/virdev.hxx>
22 #include <basegfx/matrix/b2dhommatrixtools.hxx>
23 #include <tools/debug.hxx>
24 
25 #include <math.h>
26 
27 using namespace ::swf;
28 using namespace ::com::sun::star::uno;
29 using namespace ::com::sun::star::io;
30 
31 
getMaxBitsUnsigned(sal_uInt32 nValue)32 static sal_uInt16 getMaxBitsUnsigned( sal_uInt32 nValue )
33 {
34     sal_uInt16 nBits = 0;
35 
36     while( nValue )
37     {
38         nBits++;
39         nValue >>= 1;
40     }
41 
42     return nBits;
43 }
44 
45 
getMaxBitsSigned(sal_Int32 nValue)46 sal_uInt16 getMaxBitsSigned( sal_Int32 nValue )
47 {
48     if( nValue < 0 )
49         nValue *= -1;
50 
51     return getMaxBitsUnsigned( static_cast< sal_uInt32 >(nValue) ) + 1;
52 }
53 
54 
BitStream()55 BitStream::BitStream()
56 {
57     mnBitPos = 8;
58     mnCurrentByte = 0;
59 }
60 
61 
writeUB(sal_uInt32 nValue,sal_uInt16 nBits)62 void BitStream::writeUB( sal_uInt32 nValue, sal_uInt16 nBits )
63 {
64     while( nBits != 0 )
65     {
66         mnCurrentByte |= nValue << (32 - nBits) >> (32 - mnBitPos);
67 
68         if ( nBits > mnBitPos )
69         {
70             nBits = nBits - mnBitPos;
71             mnBitPos = 0;
72         }
73         else
74         {
75             mnBitPos = sal::static_int_cast<sal_uInt8>( mnBitPos - nBits );
76             nBits = 0;
77         }
78 
79         if( 0 == mnBitPos )
80             pad();
81     }
82 }
83 
84 
writeSB(sal_Int32 nValue,sal_uInt16 nBits)85 void BitStream::writeSB( sal_Int32 nValue, sal_uInt16 nBits )
86 {
87     writeUB( static_cast< sal_uInt32 >(nValue), nBits );
88 }
89 
90 
writeFB(sal_uInt32 nValue,sal_uInt16 nBits)91 void BitStream::writeFB( sal_uInt32 nValue, sal_uInt16 nBits )
92 {
93     writeUB( nValue, nBits );
94 }
95 
96 
pad()97 void BitStream::pad()
98 {
99     if( 8 != mnBitPos )
100     {
101         maData.push_back( mnCurrentByte );
102         mnCurrentByte = 0;
103         mnBitPos = 8;
104     }
105 }
106 
107 
writeTo(SvStream & out)108 void BitStream::writeTo( SvStream& out )
109 {
110     pad();
111 
112     for (auto const& data : maData)
113     {
114         out.WriteUChar(data);
115     }
116 }
117 
118 
getOffset() const119 sal_uInt32 BitStream::getOffset() const
120 {
121     return maData.size();
122 }
123 
124 
Tag(sal_uInt8 nTagId)125 Tag::Tag( sal_uInt8 nTagId )
126 {
127     mnTagId = nTagId;
128 }
129 
130 
write(SvStream & out)131 void Tag::write( SvStream &out )
132 {
133     sal_uInt32 nSz = TellEnd();
134     Seek( STREAM_SEEK_TO_BEGIN );
135 
136     if( mnTagId != 0xff )
137     {
138         bool bLarge = nSz > 62;
139 
140         sal_uInt16 nCode = ( mnTagId << 6 ) | ( bLarge ? 0x3f : uInt16_(nSz) );
141 
142         out.WriteUChar( nCode );
143         out.WriteUChar( nCode >> 8 );
144 
145         if( bLarge )
146         {
147             sal_uInt32 nTmp = nSz;
148 
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 );
156         }
157     }
158 
159     out.WriteBytes( GetData(), nSz );
160 }
161 #if 0
162 
163 
164 void Tag::addI32( sal_Int32 nValue )
165 {
166     addUI32( static_cast<sal_uInt32>( nValue ) );
167 }
168 #endif
169 
170 
addUI32(sal_uInt32 nValue)171 void Tag::addUI32( sal_uInt32 nValue )
172 {
173     WriteUInt32( nValue );
174 }
175 #if 0
176 
177 
178 void Tag::addI16( sal_Int16 nValue )
179 {
180     addUI16( static_cast<sal_uInt16>( nValue ) );
181 }
182 #endif
183 
184 
addUI16(sal_uInt16 nValue)185 void Tag::addUI16( sal_uInt16 nValue )
186 {
187     WriteUChar( nValue );
188     WriteUChar( nValue >> 8 );
189 }
190 
191 
addUI8(sal_uInt8 nValue)192 void Tag::addUI8( sal_uInt8 nValue )
193 {
194     WriteUChar( nValue );
195 }
196 
197 
addBits(BitStream & rIn)198 void Tag::addBits( BitStream& rIn )
199 {
200     rIn.writeTo( *this );
201 }
202 
203 
addRGBA(const Color & rColor)204 void Tag::addRGBA( const Color& rColor )
205 {
206     addUI8( rColor.GetRed() );
207     addUI8( rColor.GetGreen() );
208     addUI8( rColor.GetBlue() );
209     addUI8( 0xff - rColor.GetTransparency() );
210 }
211 
212 
addRGB(const Color & rColor)213 void Tag::addRGB( const Color& rColor )
214 {
215     addUI8( rColor.GetRed() );
216     addUI8( rColor.GetGreen() );
217     addUI8( rColor.GetBlue() );
218 }
219 
220 
addRect(const tools::Rectangle & rRect)221 void Tag::addRect( const tools::Rectangle& rRect )
222 {
223     writeRect( *this, rRect );
224 }
225 
226 
writeRect(SvStream & rOut,const tools::Rectangle & rRect)227 void Tag::writeRect( SvStream& rOut, const tools::Rectangle& rRect )
228 {
229     BitStream aBits;
230 
231     sal_Int32 minX, minY, maxX, maxY;
232 
233     if( rRect.Left() < rRect.Right() )
234     {
235         minX = rRect.Left();
236         maxX = rRect.Right();
237     }
238     else
239     {
240         maxX = rRect.Left();
241         minX = rRect.Right();
242     }
243 
244 
245     if( rRect.Top() < rRect.Bottom() )
246     {
247         minY = rRect.Top();
248         maxY = rRect.Bottom();
249     }
250     else
251     {
252         maxY = rRect.Top();
253         minY = rRect.Bottom();
254     }
255 
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 );
265 
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 );
271 
272     aBits.writeTo( rOut );
273 }
274 
275 
addMatrix(const::basegfx::B2DHomMatrix & rMatrix)276 void Tag::addMatrix( const ::basegfx::B2DHomMatrix& rMatrix ) // #i73264#
277 {
278     writeMatrix( *this, rMatrix );
279 }
280 
281 
writeMatrix(SvStream & rOut,const::basegfx::B2DHomMatrix & rMatrix)282 void Tag::writeMatrix( SvStream& rOut, const ::basegfx::B2DHomMatrix& rMatrix ) // #i73264#
283 {
284 
285     BitStream aBits;
286 
287     const bool bHasScale = rMatrix.get(0, 0) != 1.0 || rMatrix.get(1, 1) != 1.0;
288 
289     aBits.writeUB( int(bHasScale), 1 );
290 
291     if( bHasScale )
292     {
293         sal_uInt8 nScaleBits = 31;
294 
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
298     }
299 
300     const bool bHasRotate = rMatrix.get(0, 1) != 0.0 || rMatrix.get(1, 0) != 0.0;
301 
302     aBits.writeUB( int(bHasRotate), 1 );
303 
304     if( bHasRotate )
305     {
306         sal_uInt8 nRotateBits = 31;
307 
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
311     }
312 
313     sal_uInt8 nTranslateBits = 16;
314 
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
318 
319     aBits.writeTo( rOut );
320 }
321 
322 
addStream(SvStream & rIn)323 void Tag::addStream( SvStream& rIn )
324 {
325     (*this).WriteStream( rIn );
326 }
327 
328 
Sprite(sal_uInt16 nId)329 Sprite::Sprite( sal_uInt16 nId )
330 : mnId( nId ), mnFrames(0)
331 {
332 }
333 
334 
~Sprite()335 Sprite::~Sprite()
336 {
337 }
338 
339 
write(SvStream & out)340 void Sprite::write( SvStream& out )
341 {
342     SvMemoryStream aTmp;
343     for (auto const& tag : maTags)
344         tag->write( aTmp );
345 
346     if( !mnFrames )
347         mnFrames = 1;
348 
349     aTmp.Seek(0);
350 
351     Tag aTag( TAG_DEFINESPRITE );
352     aTag.addUI16( mnId );
353     aTag.addUI16( uInt16_( mnFrames ) );
354     aTag.addStream( aTmp );
355     aTag.write( out );
356 }
357 
358 
addTag(std::unique_ptr<Tag> pNewTag)359 void Sprite::addTag( std::unique_ptr<Tag> pNewTag )
360 {
361     if( pNewTag->getTagId() == TAG_SHOWFRAME )
362         mnFrames++;
363 
364     maTags.push_back( std::move(pNewTag) );
365 }
366 
367 
getFixed(double fValue)368 sal_uInt32 swf::getFixed( double fValue )
369 {
370     sal_Int16 nUpper = static_cast<sal_Int16>(floor(fValue));
371     sal_uInt16 nLower = static_cast<sal_uInt16>((fValue - floor(fValue))*0x10000);
372 
373     sal_uInt32 temp = static_cast<sal_Int32>(nUpper)<<16;
374     temp |= nLower;
375 
376     return temp;
377 }
378 
379 
380 /** constructs a new flash font for the given VCL Font */
FlashFont(const vcl::Font & rFont,sal_uInt16 nId)381 FlashFont::FlashFont( const vcl::Font& rFont, sal_uInt16 nId )
382 : maFont( rFont ), mnNextIndex(0), mnId( nId )
383 {
384 }
385 
386 
~FlashFont()387 FlashFont::~FlashFont()
388 {
389 }
390 
391 
392 /** gets the glyph id for the given character. The glyphs are created on demand */
getGlyph(sal_uInt16 nChar,VirtualDevice * pVDev)393 sal_uInt16 FlashFont::getGlyph( sal_uInt16 nChar, VirtualDevice* pVDev )
394 {
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() )
398     {
399         return aIter->second;
400     }
401 
402     // if not, we create one now
403 
404     maGlyphIndex[nChar] = mnNextIndex;
405 
406     vcl::Font aOldFont( pVDev->GetFont() );
407     vcl::Font aNewFont( aOldFont );
408     aNewFont.SetAlignment( ALIGN_BASELINE );
409     pVDev->SetFont( aNewFont );
410     aOldFont.SetOrientation(0);
411 
412     // let the virtual device convert the character to polygons
413     tools::PolyPolygon aPolyPoly;
414     pVDev->GetTextOutline( aPolyPoly, OUString(sal_Unicode(nChar)) );
415 
416     maGlyphOffsets.push_back( uInt16_( maGlyphData.getOffset() ) );
417 
418     // Number of fill and line index bits set to 1
419     maGlyphData.writeUB( 0x11, 8 );
420 
421     const sal_uInt16 nCount = aPolyPoly.Count();
422     sal_uInt16 i,n;
423     for( i = 0; i < nCount; i++ )
424     {
425         tools::Polygon& rPoly = aPolyPoly[ i ];
426 
427         const sal_uInt16 nSize = rPoly.GetSize();
428         if( nSize )
429         {
430             // convert polygon to flash EM_SQUARE (1024x1024)
431             for( n = 0; n < nSize; n++ )
432             {
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;
437             }
438             Writer::Impl_addPolygon( maGlyphData, rPoly, true );
439         }
440     }
441     Writer::Impl_addEndShapeRecord( maGlyphData );
442 
443     maGlyphData.pad();
444 
445     pVDev->SetFont( aOldFont );
446 
447     return mnNextIndex++;
448 }
449 
450 
write(SvStream & out)451 void FlashFont::write( SvStream& out )
452 {
453     Tag aTag( TAG_DEFINEFONT );
454 
455     aTag.addUI16( mnId );
456 
457     sal_uInt16 nGlyphs = uInt16_( maGlyphOffsets.size() );
458     sal_uInt16 nOffset = nGlyphs * sizeof( sal_uInt16 );
459 
460     for (auto const& glyphOffset : maGlyphOffsets)
461         aTag.addUI16( nOffset + glyphOffset );
462 
463     aTag.addBits( maGlyphData );
464 
465     aTag.write( out );
466 }
467 
468 
469 /** this c'tor creates a solid fill style */
FillStyle(const Color & rSolidColor)470 FillStyle::FillStyle( const Color& rSolidColor )
471     : meType(solid )
472     , mnBitmapId(0)
473     , maColor(rSolidColor)
474 {
475 }
476 
477 
478 /** this c'tor creates a tiled or clipped bitmap fill style */
FillStyle(sal_uInt16 nBitmapId,bool bClipped,const::basegfx::B2DHomMatrix & rMatrix)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 )
483 {
484 }
485 
486 
Impl_getFillStyleType(const Gradient & rGradient)487 static FillStyle::FillStyleType Impl_getFillStyleType( const Gradient& rGradient )
488 {
489     switch( rGradient.GetStyle() )
490     {
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;
500     }
501 }
502 
503 
504 /** this c'tor creates a linear or radial gradient fill style */
FillStyle(const tools::Rectangle & rBoundRect,const Gradient & rGradient)505 FillStyle::FillStyle( const tools::Rectangle& rBoundRect, const Gradient& rGradient )
506     : meType(Impl_getFillStyleType(rGradient))
507     , mnBitmapId(0)
508     , maGradient(rGradient)
509     , maBoundRect(rBoundRect)
510 {
511 }
512 
513 
addTo(Tag * pTag) const514 void FillStyle::addTo( Tag* pTag ) const
515 {
516     pTag->addUI8( sal::static_int_cast<sal_uInt8>( meType ) );
517     switch( meType )
518     {
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;
531     }
532 }
533 
534 
535 struct GradRecord
536 {
537     sal_uInt8   mnRatio;
538     Color       maColor;
539 
GradRecordGradRecord540     GradRecord( sal_uInt8 nRatio, const Color& rColor ) : mnRatio( nRatio ), maColor( rColor ) {}
541 };
542 
543 // TODO: better emulation of our gradients
Impl_addGradient(Tag * pTag) const544 void FillStyle::Impl_addGradient( Tag* pTag ) const
545 {
546     std::vector< struct GradRecord > aGradientRecords;
547     basegfx::B2DHomMatrix m(basegfx::utils::createRotateB2DHomMatrix((maGradient.GetAngle() - 900) * F_PI1800));
548 
549     switch( maGradient.GetStyle() )
550     {
551     case GradientStyle::Elliptical:
552     case GradientStyle::Radial:
553         {
554             aGradientRecords.emplace_back( 0x00, maGradient.GetEndColor() );
555             aGradientRecords.emplace_back( 0xff, maGradient.GetStartColor() );
556 
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;
561 
562             m.scale( 1.2, 1.2 );
563 
564             if( scalex > scaley )
565             {
566                 double scale_move = scaley / scalex;
567 
568                 m.translate( tx, scale_move * ty );
569 
570 
571                 m.scale( scalex, scalex );
572             }
573             else
574             {
575                 double scale_move = scalex / scaley;
576 
577                 m.translate( scale_move * tx, ty );
578 
579 
580                 m.scale( scaley, scaley );
581             }
582 
583         }
584         break;
585     case GradientStyle::Axial:
586         {
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 );
594         }
595         break;
596     case GradientStyle::Square:
597     case GradientStyle::Rect:
598     case GradientStyle::Linear:
599         {
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;
604 
605             m.scale( scalex, scaley );
606 
607             m.translate( maBoundRect.GetWidth() / 2.0, maBoundRect.GetHeight() / 2.0 );
608         }
609         break;
610     case  GradientStyle::FORCE_EQUAL_SIZE: break;
611     }
612 
613     m.translate( maBoundRect.Left(), maBoundRect.Top() );
614 
615     pTag->addMatrix( m );
616 
617     DBG_ASSERT( aGradientRecords.size() < 8, "Illegal FlashGradient!" );
618 
619     pTag->addUI8( static_cast<sal_uInt8>( aGradientRecords.size() ) );
620 
621     for (auto const& gradientRecord : aGradientRecords)
622     {
623         pTag->addUI8( gradientRecord.mnRatio );
624         pTag->addRGBA( gradientRecord.maColor );
625     }
626 }
627 
628 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
629