/************************************************************************* * * $RCSfile: swfwriter.cxx,v $ * * $Revision: 1.2 $ * * last change: $Author: hr $ $Date: 2003-03-25 17:57:46 $ * * The Contents of this file are made available subject to the terms of * either of the following licenses * * - GNU Lesser General Public License Version 2.1 * - Sun Industry Standards Source License Version 1.1 * * Sun Microsystems Inc., October, 2000 * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2000 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * * Sun Industry Standards Source License Version 1.1 * ================================================= * The contents of this file are subject to the Sun Industry Standards * Source License Version 1.1 (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.openoffice.org/license.html. * * Software provided under this License is provided on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. * See the License for the specific provisions governing your rights and * obligations concerning the Software. * * The Initial Developer of the Original Code is: Sun Microsystems, Inc. * * Copyright: 2000 by Sun Microsystems, Inc. * * All Rights Reserved. * * Contributor(s): Christian Lippka (christian.lippka@sun.com) * * ************************************************************************/ #ifndef _SWF_WRITER_HXX_ #include "swfwriter.hxx" #endif #ifndef _SV_VIRDEV_HXX #include #endif #ifndef _SV_GDIMTF_HXX #include #endif using namespace ::swf; using namespace ::std; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::io; // ----------------------------------------------------------------------------- static MapMode aTWIPSMode( MAP_TWIP ); static MapMode a100thmmMode( MAP_100TH_MM ); static sal_Int32 map100thmm( sal_Int32 n100thMM ) { Point aPoint( n100thMM, n100thMM ); sal_Int32 nX = OutputDevice::LogicToLogic( aPoint, a100thmmMode, aTWIPSMode ).X(); return nX; } // ----------------------------------------------------------------------------- Writer::Writer( sal_Int32 nTWIPWidthOutput, sal_Int32 nTWIPHeightOutput, sal_Int32 nDocWidthInput, sal_Int32 nDocHeightInput, sal_Int32 nJPEGcompressMode ) : mpClipPolyPolygon( NULL ), mpSprite( NULL ), mpTag( NULL ), mnNextId( 1 ), mnJPEGCompressMode(nJPEGcompressMode), mbWrittenJPEGTables(false), mnGlobalTransparency(0) { mpVDev = new VirtualDevice; mpVDev->EnableOutput( sal_False ); maMovieTempFile.EnableKillingFile(); maFontsTempFile.EnableKillingFile(); mpMovieStream = maMovieTempFile.GetStream( STREAM_WRITE|STREAM_TRUNC ); mpFontsStream = maFontsTempFile.GetStream( STREAM_WRITE|STREAM_TRUNC ); mnFrames = 0; mnDocWidth = map100thmm( nDocWidthInput ); mnDocHeight = map100thmm( nDocHeightInput ); mnDocXScale = (double)nTWIPWidthOutput / mnDocWidth; mnDocYScale = (double)nTWIPHeightOutput / mnDocHeight; #ifndef AUGUSTUS // define an invisible button with the size of a page Rectangle aRect( 0, 0, mnDocWidth * mnDocXScale, mnDocHeight * mnDocYScale ); Polygon aPoly( aRect ); FillStyle aFill = FillStyle( Color(COL_WHITE) ); mnWhiteBackgroundShapeId = defineShape( aPoly, aFill ); Matrix3D m; mnPageButtonId = createID(); startTag( TAG_DEFINEBUTTON ); mpTag->addUI16( mnPageButtonId ); // character id for button // button records mpTag->addUI8( 0x08 ); // only hit state mpTag->addUI16( mnWhiteBackgroundShapeId ); // shape id of background rectangle mpTag->addUI16( 0 ); // depth for button DANGER! mpTag->addMatrix( m ); // identity matrix mpTag->addUI8( 0 ); // empty color transform // mpTag->addUI8( 0 ); // end of button records // action records mpTag->addUI8( 0x06 ); // ActionPlay mpTag->addUI8( 0 ); // end of action records endTag(); // place a shape that clips shapes depth 2-3 to document boundaries // placeShape( mnWhiteBackgroundShapeId, 1, 0, 0, 4 ); #endif } // ----------------------------------------------------------------------------- Writer::~Writer() { delete mpVDev; delete mpSprite; delete mpTag; } // ----------------------------------------------------------------------------- void ImplCopySvStreamToXOutputStream( SvStream& rIn, Reference< XOutputStream > &xOut ) { sal_uInt32 nBufferSize = 64*1024; rIn.Seek( STREAM_SEEK_TO_END ); sal_uInt32 nSize = rIn.Tell(); rIn.Seek( STREAM_SEEK_TO_BEGIN ); Sequence< sal_Int8 > aBuffer( min( nBufferSize, nSize ) ); while( nSize ) { if( nSize < nBufferSize ) { nBufferSize = nSize; aBuffer.realloc( nSize ); } sal_uInt32 nRead = rIn.Read( aBuffer.getArray(), nBufferSize ); DBG_ASSERT( nRead == nBufferSize, "ImplCopySvStreamToXOutputStream failed!" ); xOut->writeBytes( aBuffer ); nSize -= nBufferSize; } } // ----------------------------------------------------------------------------- void Writer::storeTo( Reference< XOutputStream > &xOutStream ) { for(FontMap::iterator i = maFonts.begin(); i != maFonts.end(); i++) { FlashFont* pFont = (*i); pFont->write( *mpFontsStream ); delete pFont; } // Endtag *mpMovieStream << (sal_uInt8)0; Tag aHeader( 0xff ); aHeader.addUI8( 'F' ); aHeader.addUI8( 'W' ); aHeader.addUI8( 'S' ); aHeader.addUI8( 5 ); sal_uInt32 nSizePos = aHeader.Tell(); aHeader << (sal_uInt32)0; Rectangle aDocRect( 0, 0, static_cast(mnDocWidth*mnDocXScale), static_cast(mnDocHeight*mnDocYScale) ); aHeader.addRect( aDocRect ); // frame delay in 8.8 fixed number of frames per second aHeader.addUI8( 0 ); aHeader.addUI8( 12 ); aHeader.addUI16( _uInt16(mnFrames) ); const sal_uInt32 nSize = aHeader.Tell() + mpFontsStream->Tell() + mpMovieStream->Tell(); aHeader.Seek( nSizePos ); aHeader << (sal_uInt32)nSize; ImplCopySvStreamToXOutputStream( aHeader, xOutStream ); ImplCopySvStreamToXOutputStream( *mpFontsStream, xOutStream ); ImplCopySvStreamToXOutputStream( *mpMovieStream, xOutStream ); } // ----------------------------------------------------------------------------- sal_uInt16 Writer::startSprite() { sal_uInt16 nShapeId = createID(); mvSpriteStack.push(mpSprite); mpSprite = new Sprite( nShapeId ); return nShapeId; } // ----------------------------------------------------------------------------- void Writer::endSprite() { if( mpSprite ) { startTag( TAG_END ); endTag(); mpSprite->write( *mpMovieStream ); delete mpSprite; if (mvSpriteStack.size() > 0) { mpSprite = mvSpriteStack.top(); mvSpriteStack.pop(); } else mpSprite = NULL; } } // ----------------------------------------------------------------------------- void Writer::placeShape( sal_uInt16 nID, sal_uInt16 nDepth, sal_Int32 x, sal_Int32 y, sal_uInt16 nClip, const char* pName ) { startTag( TAG_PLACEOBJECT2 ); BitStream aBits; aBits.writeUB( nClip != 0, 1 ); // Has Clip Actions? aBits.writeUB( 0, 1 ); // reserved aBits.writeUB( pName != NULL, 1 ); // has a name aBits.writeUB( 0, 1 ); // no ratio aBits.writeUB( 0, 1 ); // no color transform aBits.writeUB( 1, 1 ); // has a matrix aBits.writeUB( 1, 1 ); // places a character aBits.writeUB( 0, 1 ); // does not define a character to be moved mpTag->addBits( aBits ); mpTag->addUI16( nDepth ); // depth mpTag->addUI16( nID ); // character Id Matrix3D aMatrix; aMatrix.Translate( _Int16(static_cast(map100thmm(x)*mnDocXScale)), _Int16(static_cast(map100thmm(y)*mnDocYScale)) ); mpTag->addMatrix( aMatrix ); // transformation matrix if( pName ) mpTag->addString( pName ); if( nClip != 0 ) mpTag->addUI16( nClip ); endTag(); } // ----------------------------------------------------------------------------- void Writer::moveShape( sal_uInt16 nDepth, sal_Int32 x, sal_Int32 y ) { startTag( TAG_PLACEOBJECT2 ); BitStream aBits; aBits.writeUB( 0, 1 ); // Has no Clip Actions aBits.writeUB( 0, 1 ); // reserved aBits.writeUB( 0, 1 ); // has no name aBits.writeUB( 0, 1 ); // no ratio aBits.writeUB( 0, 1 ); // no color transform aBits.writeUB( 1, 1 ); // has a matrix aBits.writeUB( 0, 1 ); // places a character aBits.writeUB( 1, 1 ); // defines a character to be moved mpTag->addBits( aBits ); mpTag->addUI16( nDepth ); // depth Matrix3D aMatrix; aMatrix.Translate( _Int16(static_cast(map100thmm(x)*mnDocXScale)), _Int16(static_cast(map100thmm(y)*mnDocYScale)) ); mpTag->addMatrix( aMatrix ); // transformation matrix endTag(); } // ----------------------------------------------------------------------------- void Writer::removeShape( sal_uInt16 nDepth ) { startTag( TAG_REMOVEOBJECT2 ); mpTag->addUI16( nDepth ); // depth endTag(); } // ----------------------------------------------------------------------------- void Writer::setBackgroundColor( Color& rColor ) { startTag( TAG_BACKGROUNDCOLOR ); mpTag->addRGBA( rColor ); endTag(); } // ----------------------------------------------------------------------------- void Writer::startTag( sal_uInt8 nTagId ) { DBG_ASSERT( mpTag == NULL, "Last tag was not ended"); mpTag = new Tag( nTagId ); } // ----------------------------------------------------------------------------- void Writer::endTag() { sal_uInt8 nTag = mpTag->getTagId(); if( mpSprite && ( (nTag == TAG_END) || (nTag == TAG_SHOWFRAME) || (nTag == TAG_DOACTION) || (nTag == TAG_STARTSOUND) || (nTag == TAG_PLACEOBJECT) || (nTag == TAG_PLACEOBJECT2) || (nTag == TAG_REMOVEOBJECT2) || (nTag == TAG_FRAMELABEL) ) ) { mpSprite->addTag( mpTag ); mpTag = NULL; } else { mpTag->write( *mpMovieStream ); delete mpTag; mpTag = NULL; } } // ----------------------------------------------------------------------------- sal_uInt16 Writer::createID() { return mnNextId++; } // ----------------------------------------------------------------------------- void Writer::showFrame() { startTag( TAG_SHOWFRAME ); endTag(); if(NULL == mpSprite) mnFrames++; } // ----------------------------------------------------------------------------- sal_uInt16 Writer::defineShape( const GDIMetaFile& rMtf, sal_Int16 x, sal_Int16 y ) { mpVDev->SetMapMode( rMtf.GetPrefMapMode() ); Impl_writeActions( rMtf ); sal_uInt16 nId = 0; sal_uInt16 iDepth = 1; { CharacterIdVector::iterator aIter( maShapeIds.begin() ); const CharacterIdVector::iterator aEnd( maShapeIds.end() ); sal_Bool bHaveShapes = aIter != aEnd; if (bHaveShapes) { nId = startSprite(); while( aIter != aEnd ) { placeShape( *aIter, iDepth++, x, y ); aIter++; } endSprite(); } } maShapeIds.clear(); return nId; } // ----------------------------------------------------------------------------- sal_uInt16 Writer::defineShape( const Polygon& rPoly, const FillStyle& rFillStyle ) { const PolyPolygon aPolyPoly( rPoly ); return defineShape( aPolyPoly, rFillStyle ); } // ----------------------------------------------------------------------------- sal_uInt16 Writer::defineShape( const Polygon& rPoly, sal_uInt16 nLineWidth, const Color& rLineColor ) { const PolyPolygon aPolyPoly( rPoly ); return defineShape( aPolyPoly, nLineWidth, rLineColor ); } // ----------------------------------------------------------------------------- sal_uInt16 Writer::defineShape( const PolyPolygon& rPolyPoly, const FillStyle& rFillStyle ) { sal_uInt16 nShapeId = createID(); // start a DefineShape3 tag startTag( TAG_DEFINESHAPE3 ); mpTag->addUI16( nShapeId ); mpTag->addRect( rPolyPoly.GetBoundRect() ); // FILLSTYLEARRAY mpTag->addUI8( 1 ); // FillStyleCount // FILLSTYLE rFillStyle.addTo( mpTag ); // LINESTYLEARRAY mpTag->addUI8( 0 ); // LineStyleCount // Number of fill and line index bits to 1 mpTag->addUI8( 0x11 ); BitStream aBits; const sal_uInt16 nCount = rPolyPoly.Count(); sal_uInt16 i; for( i = 0; i < nCount; i++ ) { const Polygon& rPoly = rPolyPoly[ i ]; if( rPoly.GetSize() ) Impl_addPolygon( aBits, rPoly, true ); } Impl_addEndShapeRecord( aBits ); mpTag->addBits( aBits ); endTag(); return nShapeId; } // ----------------------------------------------------------------------------- sal_uInt16 Writer::defineShape( const PolyPolygon& rPolyPoly, sal_uInt16 nLineWidth, const Color& rLineColor ) { sal_uInt16 nShapeId = createID(); // start a DefineShape3 tag startTag( TAG_DEFINESHAPE3 ); mpTag->addUI16( nShapeId ); mpTag->addRect( rPolyPoly.GetBoundRect() ); // FILLSTYLEARRAY mpTag->addUI8( 0 ); // FillStyleCount // LINESTYLEARRAY mpTag->addUI8( 1 ); // LineStyleCount // LINESTYLE mpTag->addUI16( nLineWidth ); // Width of line in twips mpTag->addRGBA( rLineColor ); // Color // Number of fill and line index bits to 1 mpTag->addUI8( 0x11 ); BitStream aBits; const sal_uInt16 nCount = rPolyPoly.Count(); sal_uInt16 i; for( i = 0; i < nCount; i++ ) { const Polygon& rPoly = rPolyPoly[ i ]; if( rPoly.GetSize() ) Impl_addPolygon( aBits, rPoly, false ); } Impl_addEndShapeRecord( aBits ); mpTag->addBits( aBits ); endTag(); return nShapeId; } #ifdef AUGUSTUS enum {NO_COMPRESSION, ADPCM_COMPRESSION, MP3_COMPRESSION } COMPRESSION_TYPE; sal_Bool Writer::streamSound( const char * filename ) { SF_INFO info; SNDFILE *sf = sf_open(filename, SFM_READ, &info); if (NULL == sf) return sal_False; else { // AS: Start up lame. m_lame_flags = lame_init(); // The default (if you set nothing) is a a J-Stereo, 44.1khz // 128kbps CBR mp3 file at quality 5. Override various default settings // as necessary, for example: lame_set_num_channels(m_lame_flags,1); lame_set_in_samplerate(m_lame_flags,22050); lame_set_brate(m_lame_flags,48); lame_set_mode(m_lame_flags,MONO); lame_set_quality(m_lame_flags,2); /* 2=high 5 = medium 7=low */ // See lame.h for the complete list of options. Note that there are // some lame_set_*() calls not documented in lame.h. These functions // are experimental and for testing only. They may be removed in // the future. //4. Set more internal configuration based on data provided above, // as well as checking for problems. Check that ret_code >= 0. int ret_code = lame_init_params(m_lame_flags); if (ret_code < 0) throw 0; int lame_frame_size = lame_get_framesize(m_lame_flags); int samples_per_frame = 22050 / 12; // AS: (samples/sec) / (frames/sec) = samples/frame int mp3buffer_size = static_cast(samples_per_frame*1.25 + 7200 + 7200); startTag(TAG_SOUNDSTREAMHEAD2); mpTag->addUI8(2<<2 | 1<<1 | 0<<0); // Preferred mixer format ?? BitStream bs; bs.writeUB(MP3_COMPRESSION,4); bs.writeUB(2, 2); // AS: Reserved zero bits. bs.writeUB(1, 1); // AS: 16 Bit bs.writeUB(0, 1); // AS: Mono. mpTag->addBits(bs); mpTag->addUI16(samples_per_frame); endTag(); short *sample_buff = new short[static_cast(info.frames)]; sf_readf_short(sf, sample_buff, info.frames); unsigned char* mp3buffer = new unsigned char[mp3buffer_size]; // 5. Encode some data. input pcm data, output (maybe) mp3 frames. // This routine handles all buffering, resampling and filtering for you. // The required mp3buffer_size can be computed from num_samples, // samplerate and encoding rate, but here is a worst case estimate: // mp3buffer_size (in bytes) = 1.25*num_samples + 7200. // num_samples = the number of PCM samples in each channel. It is // not the sum of the number of samples in the L and R channels. // // The return code = number of bytes output in mp3buffer. This can be 0. // If it is <0, an error occured. for (int samples_written = 0; samples_written < info.frames; samples_written += samples_per_frame) { startTag(TAG_SOUNDSTREAMBLOCK); int samples_to_write = std::min((int)info.frames - samples_written, samples_per_frame); // AS: Since we're mono, left and right sample buffs are the same // ie, samplebuff (which is why we pass it twice). int ret = lame_encode_buffer(m_lame_flags, sample_buff + samples_written, sample_buff + samples_written, samples_to_write, mp3buffer, mp3buffer_size); if (ret < 0) throw 0; // 6. lame_encode_flush will flush the buffers and may return a // final few mp3 frames. mp3buffer should be at least 7200 bytes. // return code = number of bytes output to mp3buffer. This can be 0. if (mp3buffer_size - ret < 7200) throw 0; int ret2 = lame_encode_flush(m_lame_flags, mp3buffer + ret, mp3buffer_size - ret); if (ret2 < 0) throw 0; SvMemoryStream strm(mp3buffer, ret + ret2, STREAM_READWRITE); mpTag->addUI16(samples_to_write); //lame_frame_size); mpTag->addUI16(0); mpTag->addStream(strm); endTag(); showFrame(); } delete[] mp3buffer; delete[] sample_buff; int err = sf_close(sf); // 8. free the internal data structures. lame_close(m_lame_flags); } return sal_True; } #endif // AUGUSTUS // ----------------------------------------------------------------------------- void Writer::stop() { startTag( TAG_DOACTION ); mpTag->addUI8( 0x07 ); mpTag->addUI8( 0 ); endTag(); } // ----------------------------------------------------------------------------- void Writer::play() { startTag( TAG_DOACTION ); mpTag->addUI8( 0x06 ); mpTag->addUI8( 0 ); endTag(); } // ----------------------------------------------------------------------------- void Writer::waitOnClick( sal_uInt16 nDepth ) { placeShape( _uInt16( mnPageButtonId ), nDepth, 0, 0 ); stop(); showFrame(); removeShape( nDepth ); } // ----------------------------------------------------------------------------- /** inserts a doaction tag with an ActionGotoFrame */ void Writer::gotoFrame( sal_uInt16 nFrame ) { startTag( TAG_DOACTION ); mpTag->addUI8( 0x81 ); mpTag->addUI16( 2 ); mpTag->addUI16( nFrame ); mpTag->addUI8( 0 ); endTag(); } // ----------------------------------------------------------------------------- sal_uInt16 Writer::getWhiteBackgroundShapeId() { return mnWhiteBackgroundShapeId; }