| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_filter.hxx" |
| #include "swfwriter.hxx" |
| #include <vcl/virdev.hxx> |
| #include <basegfx/matrix/b2dhommatrixtools.hxx> |
| |
| #include <math.h> |
| |
| using namespace ::swf; |
| using namespace ::std; |
| using namespace ::com::sun::star::uno; |
| using namespace ::com::sun::star::io; |
| using ::rtl::OUString; |
| using ::rtl::OString; |
| |
| // ----------------------------------------------------------------------------- |
| |
| sal_uInt16 getMaxBitsUnsigned( sal_uInt32 nValue ) |
| { |
| sal_uInt16 nBits = 0; |
| |
| while( nValue ) |
| { |
| nBits++; |
| nValue >>= 1; |
| } |
| |
| return nBits; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| sal_uInt16 getMaxBitsSigned( sal_Int32 nValue ) |
| { |
| if( nValue < 0 ) |
| nValue *= -1; |
| |
| return getMaxBitsUnsigned( static_cast< sal_uInt32 >(nValue) ) + 1; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| BitStream::BitStream() |
| { |
| mnBitPos = 8; |
| mnCurrentByte = 0; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void BitStream::writeUB( sal_uInt32 nValue, sal_uInt16 nBits ) |
| { |
| while( nBits != 0 ) |
| { |
| mnCurrentByte |= nValue << (32 - nBits) >> (32 - mnBitPos); |
| |
| if ( nBits > mnBitPos ) |
| { |
| nBits = nBits - mnBitPos; |
| mnBitPos = 0; |
| } |
| else |
| { |
| mnBitPos = sal::static_int_cast<sal_uInt8>( mnBitPos - nBits ); |
| nBits = 0; |
| } |
| |
| if( 0 == mnBitPos ) |
| pad(); |
| } |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void BitStream::writeSB( sal_Int32 nValue, sal_uInt16 nBits ) |
| { |
| writeUB( static_cast< sal_uInt32 >(nValue), nBits ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void BitStream::writeFB( sal_uInt32 nValue, sal_uInt16 nBits ) |
| { |
| writeUB( nValue, nBits ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void BitStream::pad() |
| { |
| if( 8 != mnBitPos ) |
| { |
| maData.push_back( mnCurrentByte ); |
| mnCurrentByte = 0; |
| mnBitPos = 8; |
| } |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void BitStream::writeTo( SvStream& out ) |
| { |
| pad(); |
| |
| vector< sal_uInt8 >::iterator aIter( maData.begin() ); |
| const vector< sal_uInt8>::iterator aEnd( maData.end() ); |
| while(aIter != aEnd) |
| { |
| out << (*aIter++); |
| } |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| sal_uInt32 BitStream::getOffset() const |
| { |
| return maData.size(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| Tag::Tag( sal_uInt8 nTagId ) |
| { |
| mnTagId = nTagId; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void Tag::write( SvStream &out ) |
| { |
| Seek( STREAM_SEEK_TO_END ); |
| sal_uInt32 nSz = Tell(); |
| Seek( STREAM_SEEK_TO_BEGIN ); |
| |
| if( mnTagId != 0xff ) |
| { |
| bool bLarge = nSz > 62; |
| |
| sal_uInt16 nCode = ( mnTagId << 6 ) | ( bLarge ? 0x3f : _uInt16(nSz) ); |
| |
| out << (sal_uInt8)nCode; |
| out << (sal_uInt8)(nCode >> 8); |
| |
| if( bLarge ) |
| { |
| sal_uInt32 nTmp = nSz; |
| |
| out << (sal_uInt8)nTmp; |
| nTmp >>= 8; |
| out << (sal_uInt8)nTmp; |
| nTmp >>= 8; |
| out << (sal_uInt8)nTmp; |
| nTmp >>= 8; |
| out << (sal_uInt8)nTmp; |
| } |
| } |
| |
| out.Write( GetData(), nSz ); |
| } |
| #if 0 |
| // ----------------------------------------------------------------------------- |
| |
| void Tag::addI32( sal_Int32 nValue ) |
| { |
| addUI32( static_cast<sal_uInt32>( nValue ) ); |
| } |
| #endif |
| // ----------------------------------------------------------------------------- |
| |
| void Tag::addUI32( sal_uInt32 nValue ) |
| { |
| *this << nValue; |
| } |
| #if 0 |
| // ----------------------------------------------------------------------------- |
| |
| void Tag::addI16( sal_Int16 nValue ) |
| { |
| addUI16( static_cast<sal_uInt16>( nValue ) ); |
| } |
| #endif |
| // ----------------------------------------------------------------------------- |
| |
| void Tag::addUI16( sal_uInt16 nValue ) |
| { |
| *this << (sal_uInt8)nValue; |
| *this << (sal_uInt8)(nValue >> 8); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void Tag::addUI8( sal_uInt8 nValue ) |
| { |
| *this << (sal_uInt8)nValue; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void Tag::addBits( BitStream& rIn ) |
| { |
| rIn.writeTo( *this ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void Tag::addRGBA( const Color& rColor ) |
| { |
| addUI8( rColor.GetRed() ); |
| addUI8( rColor.GetGreen() ); |
| addUI8( rColor.GetBlue() ); |
| addUI8( 0xff - rColor.GetTransparency() ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void Tag::addRGB( const Color& rColor ) |
| { |
| addUI8( rColor.GetRed() ); |
| addUI8( rColor.GetGreen() ); |
| addUI8( rColor.GetBlue() ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void Tag::addRect( const Rectangle& rRect ) |
| { |
| writeRect( *this, rRect ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void Tag::writeRect( SvStream& rOut, const Rectangle& rRect ) |
| { |
| BitStream aBits; |
| |
| sal_Int32 minX, minY, maxX, maxY; |
| |
| if( rRect.nLeft < rRect.nRight ) |
| { |
| minX = rRect.nLeft; maxX = rRect.nRight; |
| } |
| else |
| { |
| maxX = rRect.nLeft; minX = rRect.nRight; |
| } |
| |
| |
| if( rRect.nTop < rRect.nBottom ) |
| { |
| minY = rRect.nTop; maxY = rRect.nBottom; |
| } |
| else |
| { |
| maxY = rRect.nTop; minY = rRect.nBottom; |
| } |
| |
| // AS: Figure out the maximum nubmer of bits required to represent any of the |
| // rectangle coordinates. Since minX or minY could be negative, they could |
| // actually require more bits than maxX or maxY. |
| // AS: Christian, can they be negative, or is that a wasted check? |
| // CL: I think so, f.e. for shapes that have the top and/or left edge outside |
| // the page origin |
| sal_uInt8 nBits1 = sal::static_int_cast<sal_uInt8>( max( getMaxBitsSigned( minX ), getMaxBitsSigned( minY ) ) ); |
| sal_uInt8 nBits2 = sal::static_int_cast<sal_uInt8>( max( getMaxBitsSigned( maxX ), getMaxBitsSigned( maxY ) ) ); |
| sal_uInt8 nBitsMax = max( nBits1, nBits2 ); |
| |
| aBits.writeUB( nBitsMax, 5 ); |
| aBits.writeSB( minX, nBitsMax ); |
| aBits.writeSB( maxX, nBitsMax ); |
| aBits.writeSB( minY, nBitsMax ); |
| aBits.writeSB( maxY, nBitsMax ); |
| |
| aBits.writeTo( rOut ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void Tag::addMatrix( const ::basegfx::B2DHomMatrix& rMatrix ) // #i73264# |
| { |
| writeMatrix( *this, rMatrix ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void Tag::writeMatrix( SvStream& rOut, const ::basegfx::B2DHomMatrix& rMatrix ) // #i73264# |
| { |
| |
| BitStream aBits; |
| |
| const sal_uInt8 bHasScale = rMatrix.get(0, 0) != 1.0 || rMatrix.get(1, 1) != 1.0; |
| |
| aBits.writeUB( bHasScale, 1 ); |
| |
| if( bHasScale ) |
| { |
| sal_uInt8 nScaleBits = 31; |
| |
| aBits.writeUB( nScaleBits, 5 ); |
| aBits.writeFB( getFixed( rMatrix.get(0, 0) ), nScaleBits ); // Scale X |
| aBits.writeFB( getFixed( rMatrix.get(1, 1) ), nScaleBits ); // Scale Y |
| } |
| |
| const sal_uInt8 bHasRotate = rMatrix.get(0, 1) != 0.0 || rMatrix.get(1, 0) != 0.0; |
| |
| aBits.writeUB( bHasRotate, 1 ); |
| |
| if( bHasRotate ) |
| { |
| sal_uInt8 nRotateBits = 31; |
| |
| aBits.writeUB( nRotateBits, 5 ); |
| aBits.writeFB( getFixed( rMatrix.get(0, 1) ), nRotateBits ); // RotateSkew0 |
| aBits.writeFB( getFixed( rMatrix.get(1, 0) ), nRotateBits ); // RotateSkew1 |
| } |
| |
| sal_uInt8 nTranslateBits = 16; |
| |
| aBits.writeUB( nTranslateBits, 5 ); |
| aBits.writeSB( (sal_Int16)rMatrix.get(0, 2), nTranslateBits ); // Translate X |
| aBits.writeSB( (sal_Int16)rMatrix.get(1, 2), nTranslateBits ); // Translate Y |
| |
| aBits.writeTo( rOut ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void Tag::addString( const char* pString ) |
| { |
| if( pString ) |
| { |
| while( *pString ) |
| addUI8( *pString++ ); |
| } |
| |
| addUI8( 0 ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void Tag::addStream( SvStream& rIn ) |
| { |
| *this << rIn; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| Sprite::Sprite( sal_uInt16 nId ) |
| : mnId( nId ), mnFrames(0) |
| { |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| Sprite::~Sprite() |
| { |
| for(vector< Tag* >::iterator i = maTags.begin(); i != maTags.end(); i++) |
| delete *i; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void Sprite::write( SvStream& out ) |
| { |
| SvMemoryStream aTmp; |
| for(vector< Tag* >::iterator i = maTags.begin(); i != maTags.end(); i++) |
| (*i)->write( aTmp ); |
| |
| if( !mnFrames ) |
| mnFrames = 1; |
| |
| aTmp.Seek(0); |
| |
| Tag aTag( TAG_DEFINESPRITE ); |
| aTag.addUI16( mnId ); |
| aTag.addUI16( _uInt16( mnFrames ) ); |
| aTag.addStream( aTmp ); |
| aTag.write( out ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void Sprite::addTag( Tag* pNewTag ) |
| { |
| if( pNewTag ) |
| { |
| if( pNewTag->getTagId() == TAG_SHOWFRAME ) |
| mnFrames++; |
| |
| maTags.push_back( pNewTag ); |
| } |
| } |
| |
| ///////////////////////////////////////////////////////////////////////////////// |
| |
| sal_uInt32 swf::getFixed( double fValue ) |
| { |
| sal_Int16 nUpper = (sal_Int16)floor(fValue); |
| sal_uInt16 nLower = (sal_uInt16)((fValue - floor(fValue))*0x10000); |
| |
| sal_uInt32 temp = ((sal_Int32)nUpper)<<16; |
| temp |= nLower; |
| |
| return temp; |
| } |
| |
| ///////////////////////////////////////////////////////////////////////////////// |
| |
| /** constructs a new flash font for the given VCL Font */ |
| FlashFont::FlashFont( const Font& rFont, sal_uInt16 nId ) |
| : maFont( rFont ), mnNextIndex(0), mnId( nId ) |
| { |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| FlashFont::~FlashFont() |
| { |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| /** gets the glyph id for the given character. The glyphs are created on demand */ |
| sal_uInt16 FlashFont::getGlyph( sal_uInt16 nChar, VirtualDevice* pVDev ) |
| { |
| // see if we already created a glyph for this character |
| std::map<sal_uInt16, sal_uInt16, ltuint16>::iterator aIter( maGlyphIndex.find(nChar) ); |
| if( aIter != maGlyphIndex.end() ) |
| { |
| return aIter->second; |
| } |
| |
| // if not, we create one now |
| |
| maGlyphIndex[nChar] = mnNextIndex; |
| |
| Font aOldFont( pVDev->GetFont() ); |
| Font aNewFont( aOldFont ); |
| aNewFont.SetAlign( ALIGN_BASELINE ); |
| pVDev->SetFont( aNewFont ); |
| aOldFont.SetOrientation(0); |
| |
| // let the virtual device convert the character to polygons |
| PolyPolygon aPolyPoly; |
| pVDev->GetTextOutline( aPolyPoly, nChar ); |
| |
| maGlyphOffsets.push_back( _uInt16( maGlyphData.getOffset() ) ); |
| |
| // Number of fill and line index bits set to 1 |
| maGlyphData.writeUB( 0x11, 8 ); |
| |
| const sal_uInt16 nCount = aPolyPoly.Count(); |
| sal_uInt16 i,n; |
| for( i = 0; i < nCount; i++ ) |
| { |
| Polygon& rPoly = aPolyPoly[ i ]; |
| |
| const sal_uInt16 nSize = rPoly.GetSize(); |
| if( nSize ) |
| { |
| // convert polygon to flash EM_SQUARE (1024x1024) |
| for( n = 0; n < nSize; n++ ) |
| { |
| Point aPoint( rPoly[n] ); |
| aPoint.X() = static_cast<long>((double(aPoint.X()) * 1024.0 ) / double(aOldFont.GetHeight())); |
| aPoint.Y() = static_cast<long>((double(aPoint.Y()) * 1024.0 ) / double(aOldFont.GetHeight())); |
| rPoly[n] = aPoint; |
| } |
| Writer::Impl_addPolygon( maGlyphData, rPoly, true ); |
| } |
| } |
| Writer::Impl_addEndShapeRecord( maGlyphData ); |
| |
| maGlyphData.pad(); |
| |
| pVDev->SetFont( aOldFont ); |
| |
| return mnNextIndex++; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void FlashFont::write( SvStream& out ) |
| { |
| Tag aTag( TAG_DEFINEFONT ); |
| |
| aTag.addUI16( mnId ); |
| |
| sal_uInt16 nGlyphs = _uInt16( maGlyphOffsets.size() ); |
| sal_uInt16 nOffset = nGlyphs * sizeof( sal_uInt16 ); |
| |
| for(vector< sal_uInt16 >::iterator i = maGlyphOffsets.begin(); i != maGlyphOffsets.end(); i++) |
| aTag.addUI16( nOffset + (*i) ); |
| |
| aTag.addBits( maGlyphData ); |
| |
| aTag.write( out ); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /** this c'tor creates a solid fill style */ |
| FillStyle::FillStyle( const Color& rSolidColor ) |
| : meType( solid ), |
| maColor( rSolidColor ) |
| { |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| /** this c'tor creates a tiled or clipped bitmap fill style */ |
| FillStyle::FillStyle( sal_uInt16 nBitmapId, bool bClipped, const ::basegfx::B2DHomMatrix& rMatrix ) // #i73264# |
| : meType( bClipped ? clipped_bitmap : tiled_bitmap ), |
| maMatrix( rMatrix ), |
| mnBitmapId( nBitmapId ) |
| { |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| FillStyle::FillStyleType Impl_getFillStyleType( const Gradient& rGradient ) |
| { |
| switch( rGradient.GetStyle() ) |
| { |
| case GradientStyle_ELLIPTICAL: |
| case GradientStyle_RADIAL: |
| return FillStyle::radial_gradient; |
| // case GradientStyle_AXIAL: |
| // case GradientStyle_SQUARE: |
| // case GradientStyle_RECT: |
| // case GradientStyle_LINEAR: |
| default: |
| return FillStyle::linear_gradient; |
| } |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| /** this c'tor creates a linear or radial gradient fill style */ |
| FillStyle::FillStyle( const Rectangle& rBoundRect, const Gradient& rGradient ) |
| : meType( Impl_getFillStyleType( rGradient ) ), |
| maGradient( rGradient ), |
| maBoundRect( rBoundRect ) |
| { |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void FillStyle::addTo( Tag* pTag ) const |
| { |
| pTag->addUI8( sal::static_int_cast<sal_uInt8>( meType ) ); |
| switch( meType ) |
| { |
| case solid: |
| pTag->addRGBA( maColor ); |
| break; |
| case linear_gradient: |
| case radial_gradient: |
| Impl_addGradient( pTag ); |
| break; |
| case tiled_bitmap: |
| case clipped_bitmap: |
| pTag->addUI16( mnBitmapId ); |
| pTag->addMatrix( maMatrix ); |
| break; |
| } |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| struct GradRecord |
| { |
| sal_uInt8 mnRatio; |
| Color maColor; |
| |
| GradRecord( sal_uInt8 nRatio, const Color& rColor ) : mnRatio( nRatio ), maColor( rColor ) {} |
| }; |
| |
| // TODO: better emulation of our gradients |
| void FillStyle::Impl_addGradient( Tag* pTag ) const |
| { |
| vector< struct GradRecord > aGradientRecords; |
| basegfx::B2DHomMatrix m(basegfx::tools::createRotateB2DHomMatrix((maGradient.GetAngle() - 900) * F_PI1800)); |
| |
| switch( maGradient.GetStyle() ) |
| { |
| case GradientStyle_ELLIPTICAL: |
| case GradientStyle_RADIAL: |
| { |
| aGradientRecords.push_back( GradRecord( 0x00, maGradient.GetEndColor() ) ); |
| aGradientRecords.push_back( GradRecord( 0xff, maGradient.GetStartColor() ) ); |
| |
| double tx = ( maGradient.GetOfsX() * 32768.0 ) / 100.0; |
| double ty = ( maGradient.GetOfsY() * 32768.0 ) / 100.0; |
| double scalex = (double)maBoundRect.GetWidth() / 32768.0; |
| double scaley = (double)maBoundRect.GetHeight() / 32768.0; |
| |
| m.scale( 1.2, 1.2 ); |
| |
| if( scalex > scaley ) |
| { |
| double scale_move = scaley / scalex; |
| |
| m.translate( tx, scale_move * ty ); |
| |
| |
| m.scale( scalex, scalex ); |
| } |
| else |
| { |
| double scale_move = scalex / scaley; |
| |
| m.translate( scale_move * tx, ty ); |
| |
| |
| m.scale( scaley, scaley ); |
| } |
| |
| } |
| break; |
| case GradientStyle_AXIAL: |
| { |
| aGradientRecords.push_back( GradRecord( 0x00, maGradient.GetEndColor() ) ); |
| aGradientRecords.push_back( GradRecord( 0x80, maGradient.GetStartColor() ) ); |
| aGradientRecords.push_back( GradRecord( 0xff, maGradient.GetEndColor() ) ); |
| double tx = ( 32768.0 / 2.0 ); |
| double ty = ( 32768.0 / 2.0 ); |
| double scalex = (double)maBoundRect.GetWidth() / 32768.0; |
| double scaley = (double)maBoundRect.GetHeight() / 32768.0; |
| |
| m.translate( tx, ty ); |
| m.scale( scalex, scaley ); |
| } |
| break; |
| case GradientStyle_SQUARE: |
| case GradientStyle_RECT: |
| case GradientStyle_LINEAR: |
| { |
| aGradientRecords.push_back( GradRecord( 0x00, maGradient.GetStartColor() ) ); |
| aGradientRecords.push_back( GradRecord( 0xff, maGradient.GetEndColor() ) ); |
| double scalex = (double)maBoundRect.GetWidth() / 32768.0; |
| double scaley = (double)maBoundRect.GetHeight() / 32768.0; |
| |
| m.scale( scalex, scaley ); |
| |
| m.translate( maBoundRect.GetWidth() / 2.0, maBoundRect.GetHeight() / 2.0 ); |
| } |
| break; |
| case GradientStyle_FORCE_EQUAL_SIZE: break; |
| } |
| |
| m.translate( maBoundRect.nLeft, maBoundRect.nTop ); |
| |
| pTag->addMatrix( m ); |
| |
| DBG_ASSERT( aGradientRecords.size() < 8, "Illegal FlashGradient!" ); |
| |
| pTag->addUI8( static_cast<sal_uInt8>( aGradientRecords.size() ) ); |
| |
| for(std::vector< GradRecord >::iterator i = aGradientRecords.begin(); i != aGradientRecords.end(); i++) |
| { |
| pTag->addUI8( (*i).mnRatio ); |
| pTag->addRGBA( (*i).maColor ); |
| } |
| } |
| |