merge the formfield patch from ooo-build
[ooovba.git] / filter / source / flash / swfwriter.cxx
blob3d2a34c325ef54b23e815c0731f83af3ca0503a2
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: swfwriter.cxx,v $
10 * $Revision: 1.12 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_filter.hxx"
33 #include "swfwriter.hxx"
34 #include <vcl/virdev.hxx>
35 #include <vcl/gdimtf.hxx>
37 using namespace ::swf;
38 using namespace ::std;
39 using namespace ::com::sun::star::uno;
40 using namespace ::com::sun::star::io;
42 // -----------------------------------------------------------------------------
44 static MapMode aTWIPSMode( MAP_TWIP );
45 static MapMode a100thmmMode( MAP_100TH_MM );
47 static sal_Int32 map100thmm( sal_Int32 n100thMM )
49 Point aPoint( n100thMM, n100thMM );
50 sal_Int32 nX = OutputDevice::LogicToLogic( aPoint, a100thmmMode, aTWIPSMode ).X();
51 return nX;
54 // -----------------------------------------------------------------------------
56 Writer::Writer( sal_Int32 nTWIPWidthOutput, sal_Int32 nTWIPHeightOutput, sal_Int32 nDocWidthInput, sal_Int32 nDocHeightInput, sal_Int32 nJPEGcompressMode )
57 : mpClipPolyPolygon( NULL ),
58 mpTag( NULL ),
59 mpSprite( NULL ),
60 mnNextId( 1 ),
61 mnGlobalTransparency(0),
62 mnJPEGCompressMode(nJPEGcompressMode)
64 mpVDev = new VirtualDevice;
65 mpVDev->EnableOutput( sal_False );
67 maMovieTempFile.EnableKillingFile();
68 maFontsTempFile.EnableKillingFile();
70 mpMovieStream = maMovieTempFile.GetStream( STREAM_WRITE|STREAM_TRUNC );
71 mpFontsStream = maFontsTempFile.GetStream( STREAM_WRITE|STREAM_TRUNC );
73 mnFrames = 0;
75 mnDocWidth = map100thmm( nDocWidthInput );
76 mnDocHeight = map100thmm( nDocHeightInput );
78 mnDocXScale = (double)nTWIPWidthOutput / mnDocWidth;
79 mnDocYScale = (double)nTWIPHeightOutput / mnDocHeight;
81 #ifndef AUGUSTUS
82 // define an invisible button with the size of a page
83 Rectangle aRect( 0, 0, (long)( mnDocWidth * mnDocXScale ), (long)( mnDocHeight * mnDocYScale ) );
84 Polygon aPoly( aRect );
85 FillStyle aFill = FillStyle( Color(COL_WHITE) );
86 mnWhiteBackgroundShapeId = defineShape( aPoly, aFill );
88 ::basegfx::B2DHomMatrix m; // #i73264#
89 mnPageButtonId = createID();
90 startTag( TAG_DEFINEBUTTON );
91 mpTag->addUI16( mnPageButtonId ); // character id for button
93 // button records
94 mpTag->addUI8( 0x08 ); // only hit state
95 mpTag->addUI16( mnWhiteBackgroundShapeId ); // shape id of background rectangle
96 mpTag->addUI16( 0 ); // depth for button DANGER!
97 mpTag->addMatrix( m ); // identity matrix
98 mpTag->addUI8( 0 ); // empty color transform
100 // mpTag->addUI8( 0 ); // end of button records
102 // action records
103 mpTag->addUI8( 0x06 ); // ActionPlay
104 mpTag->addUI8( 0 ); // end of action records
106 endTag();
108 // place a shape that clips shapes depth 2-3 to document boundaries
109 // placeShape( mnWhiteBackgroundShapeId, 1, 0, 0, 4 );
110 #endif
113 // -----------------------------------------------------------------------------
115 Writer::~Writer()
117 delete mpVDev;
118 delete mpSprite;
119 delete mpTag;
122 // -----------------------------------------------------------------------------
124 void ImplCopySvStreamToXOutputStream( SvStream& rIn, Reference< XOutputStream > &xOut )
126 sal_uInt32 nBufferSize = 64*1024;
128 rIn.Seek( STREAM_SEEK_TO_END );
129 sal_uInt32 nSize = rIn.Tell();
130 rIn.Seek( STREAM_SEEK_TO_BEGIN );
132 Sequence< sal_Int8 > aBuffer( min( nBufferSize, nSize ) );
134 while( nSize )
136 if( nSize < nBufferSize )
138 nBufferSize = nSize;
139 aBuffer.realloc( nSize );
142 sal_uInt32 nRead = rIn.Read( aBuffer.getArray(), nBufferSize );
143 DBG_ASSERT( nRead == nBufferSize, "ImplCopySvStreamToXOutputStream failed!" );
144 xOut->writeBytes( aBuffer );
146 if( nRead == 0 )
147 break;
149 nSize -= nRead;
153 // -----------------------------------------------------------------------------
155 void Writer::storeTo( Reference< XOutputStream > &xOutStream )
157 for(FontMap::iterator i = maFonts.begin(); i != maFonts.end(); i++)
159 FlashFont* pFont = (*i);
160 pFont->write( *mpFontsStream );
161 delete pFont;
164 // Endtag
165 *mpMovieStream << (sal_uInt16)0;
167 Tag aHeader( 0xff );
169 aHeader.addUI8( 'F' );
170 aHeader.addUI8( 'W' );
171 aHeader.addUI8( 'S' );
172 aHeader.addUI8( 5 );
174 sal_uInt32 nSizePos = aHeader.Tell();
176 aHeader << (sal_uInt32)0;
178 Rectangle aDocRect( 0, 0, static_cast<long>(mnDocWidth*mnDocXScale), static_cast<long>(mnDocHeight*mnDocYScale) );
180 aHeader.addRect( aDocRect );
182 // frame delay in 8.8 fixed number of frames per second
183 aHeader.addUI8( 0 );
184 aHeader.addUI8( 12 );
186 aHeader.addUI16( _uInt16(mnFrames) );
188 const sal_uInt32 nSize = aHeader.Tell() + mpFontsStream->Tell() + mpMovieStream->Tell();
190 aHeader.Seek( nSizePos );
191 aHeader << (sal_uInt32)nSize;
193 ImplCopySvStreamToXOutputStream( aHeader, xOutStream );
194 ImplCopySvStreamToXOutputStream( *mpFontsStream, xOutStream );
195 ImplCopySvStreamToXOutputStream( *mpMovieStream, xOutStream );
198 // -----------------------------------------------------------------------------
200 sal_uInt16 Writer::startSprite()
202 sal_uInt16 nShapeId = createID();
203 mvSpriteStack.push(mpSprite);
204 mpSprite = new Sprite( nShapeId );
205 return nShapeId;
208 // -----------------------------------------------------------------------------
210 void Writer::endSprite()
212 if( mpSprite )
214 startTag( TAG_END );
215 endTag();
217 mpSprite->write( *mpMovieStream );
218 delete mpSprite;
220 if (mvSpriteStack.size() > 0)
222 mpSprite = mvSpriteStack.top();
223 mvSpriteStack.pop();
225 else
226 mpSprite = NULL;
230 // -----------------------------------------------------------------------------
232 void Writer::placeShape( sal_uInt16 nID, sal_uInt16 nDepth, sal_Int32 x, sal_Int32 y, sal_uInt16 nClip, const char* pName )
234 startTag( TAG_PLACEOBJECT2 );
236 BitStream aBits;
238 aBits.writeUB( nClip != 0, 1 ); // Has Clip Actions?
239 aBits.writeUB( 0, 1 ); // reserved
240 aBits.writeUB( pName != NULL, 1 ); // has a name
241 aBits.writeUB( 0, 1 ); // no ratio
242 aBits.writeUB( 0, 1 ); // no color transform
243 aBits.writeUB( 1, 1 ); // has a matrix
244 aBits.writeUB( 1, 1 ); // places a character
245 aBits.writeUB( 0, 1 ); // does not define a character to be moved
247 mpTag->addBits( aBits );
248 mpTag->addUI16( nDepth ); // depth
249 mpTag->addUI16( nID ); // character Id
251 ::basegfx::B2DHomMatrix aMatrix; // #i73264#
252 aMatrix.translate( _Int16(static_cast<long>(map100thmm(x)*mnDocXScale)), _Int16(static_cast<long>(map100thmm(y)*mnDocYScale)));
253 mpTag->addMatrix( aMatrix ); // transformation matrix
255 if( pName )
256 mpTag->addString( pName );
258 if( nClip != 0 )
259 mpTag->addUI16( nClip );
261 endTag();
264 // -----------------------------------------------------------------------------
266 void Writer::moveShape( sal_uInt16 nDepth, sal_Int32 x, sal_Int32 y )
268 startTag( TAG_PLACEOBJECT2 );
270 BitStream aBits;
271 aBits.writeUB( 0, 1 ); // Has no Clip Actions
272 aBits.writeUB( 0, 1 ); // reserved
273 aBits.writeUB( 0, 1 ); // has no name
274 aBits.writeUB( 0, 1 ); // no ratio
275 aBits.writeUB( 0, 1 ); // no color transform
276 aBits.writeUB( 1, 1 ); // has a matrix
277 aBits.writeUB( 0, 1 ); // places a character
278 aBits.writeUB( 1, 1 ); // defines a character to be moved
280 mpTag->addBits( aBits );
281 mpTag->addUI16( nDepth ); // depth
283 ::basegfx::B2DHomMatrix aMatrix; // #i73264#
284 aMatrix.translate( _Int16(static_cast<long>(map100thmm(x)*mnDocXScale)), _Int16(static_cast<long>(map100thmm(y)*mnDocYScale)));
285 mpTag->addMatrix( aMatrix ); // transformation matrix
287 endTag();
290 // -----------------------------------------------------------------------------
292 void Writer::removeShape( sal_uInt16 nDepth )
294 startTag( TAG_REMOVEOBJECT2 );
295 mpTag->addUI16( nDepth ); // depth
296 endTag();
299 // -----------------------------------------------------------------------------
301 void Writer::startTag( sal_uInt8 nTagId )
303 DBG_ASSERT( mpTag == NULL, "Last tag was not ended");
305 mpTag = new Tag( nTagId );
308 // -----------------------------------------------------------------------------
310 void Writer::endTag()
312 sal_uInt8 nTag = mpTag->getTagId();
314 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) ) )
316 mpSprite->addTag( mpTag );
317 mpTag = NULL;
319 else
321 mpTag->write( *mpMovieStream );
322 delete mpTag;
323 mpTag = NULL;
327 // -----------------------------------------------------------------------------
329 sal_uInt16 Writer::createID()
331 return mnNextId++;
334 // -----------------------------------------------------------------------------
336 void Writer::showFrame()
338 startTag( TAG_SHOWFRAME );
339 endTag();
341 if(NULL == mpSprite)
342 mnFrames++;
345 // -----------------------------------------------------------------------------
347 sal_uInt16 Writer::defineShape( const GDIMetaFile& rMtf, sal_Int16 x, sal_Int16 y )
349 mpVDev->SetMapMode( rMtf.GetPrefMapMode() );
350 Impl_writeActions( rMtf );
352 sal_uInt16 nId = 0;
353 sal_uInt16 iDepth = 1;
355 CharacterIdVector::iterator aIter( maShapeIds.begin() );
356 const CharacterIdVector::iterator aEnd( maShapeIds.end() );
358 sal_Bool bHaveShapes = aIter != aEnd;
360 if (bHaveShapes)
362 nId = startSprite();
364 while( aIter != aEnd )
366 placeShape( *aIter, iDepth++, x, y );
367 aIter++;
370 endSprite();
374 maShapeIds.clear();
376 return nId;
379 // -----------------------------------------------------------------------------
381 sal_uInt16 Writer::defineShape( const Polygon& rPoly, const FillStyle& rFillStyle )
383 const PolyPolygon aPolyPoly( rPoly );
384 return defineShape( aPolyPoly, rFillStyle );
387 // -----------------------------------------------------------------------------
389 sal_uInt16 Writer::defineShape( const PolyPolygon& rPolyPoly, const FillStyle& rFillStyle )
391 sal_uInt16 nShapeId = createID();
393 // start a DefineShape3 tag
394 startTag( TAG_DEFINESHAPE3 );
396 mpTag->addUI16( nShapeId );
397 mpTag->addRect( rPolyPoly.GetBoundRect() );
400 // FILLSTYLEARRAY
401 mpTag->addUI8( 1 ); // FillStyleCount
403 // FILLSTYLE
404 rFillStyle.addTo( mpTag );
406 // LINESTYLEARRAY
407 mpTag->addUI8( 0 ); // LineStyleCount
409 // Number of fill and line index bits to 1
410 mpTag->addUI8( 0x11 );
412 BitStream aBits;
414 const sal_uInt16 nCount = rPolyPoly.Count();
415 sal_uInt16 i;
416 for( i = 0; i < nCount; i++ )
418 const Polygon& rPoly = rPolyPoly[ i ];
419 if( rPoly.GetSize() )
420 Impl_addPolygon( aBits, rPoly, true );
423 Impl_addEndShapeRecord( aBits );
425 mpTag->addBits( aBits );
426 endTag();
428 return nShapeId;
431 // -----------------------------------------------------------------------------
433 sal_uInt16 Writer::defineShape( const PolyPolygon& rPolyPoly, sal_uInt16 nLineWidth, const Color& rLineColor )
435 sal_uInt16 nShapeId = createID();
437 // start a DefineShape3 tag
438 startTag( TAG_DEFINESHAPE3 );
440 mpTag->addUI16( nShapeId );
441 mpTag->addRect( rPolyPoly.GetBoundRect() );
444 // FILLSTYLEARRAY
445 mpTag->addUI8( 0 ); // FillStyleCount
447 // LINESTYLEARRAY
448 mpTag->addUI8( 1 ); // LineStyleCount
450 // LINESTYLE
451 mpTag->addUI16( nLineWidth ); // Width of line in twips
452 mpTag->addRGBA( rLineColor ); // Color
454 // Number of fill and line index bits to 1
455 mpTag->addUI8( 0x11 );
457 BitStream aBits;
459 const sal_uInt16 nCount = rPolyPoly.Count();
460 sal_uInt16 i;
461 for( i = 0; i < nCount; i++ )
463 const Polygon& rPoly = rPolyPoly[ i ];
464 if( rPoly.GetSize() )
465 Impl_addPolygon( aBits, rPoly, false );
468 Impl_addEndShapeRecord( aBits );
470 mpTag->addBits( aBits );
471 endTag();
473 return nShapeId;
476 #ifdef AUGUSTUS
477 enum {NO_COMPRESSION, ADPCM_COMPRESSION, MP3_COMPRESSION } COMPRESSION_TYPE;
478 sal_Bool Writer::streamSound( const char * filename )
480 SF_INFO info;
481 SNDFILE *sf = sf_open(filename, SFM_READ, &info);
483 if (NULL == sf)
484 return sal_False;
485 else
487 // AS: Start up lame.
488 m_lame_flags = lame_init();
490 // The default (if you set nothing) is a a J-Stereo, 44.1khz
491 // 128kbps CBR mp3 file at quality 5. Override various default settings
492 // as necessary, for example:
494 lame_set_num_channels(m_lame_flags,1);
495 lame_set_in_samplerate(m_lame_flags,22050);
496 lame_set_brate(m_lame_flags,48);
497 lame_set_mode(m_lame_flags,MONO);
498 lame_set_quality(m_lame_flags,2); /* 2=high 5 = medium 7=low */
500 // See lame.h for the complete list of options. Note that there are
501 // some lame_set_*() calls not documented in lame.h. These functions
502 // are experimental and for testing only. They may be removed in
503 // the future.
505 //4. Set more internal configuration based on data provided above,
506 // as well as checking for problems. Check that ret_code >= 0.
508 int ret_code = lame_init_params(m_lame_flags);
510 if (ret_code < 0)
511 throw 0;
513 int lame_frame_size = lame_get_framesize(m_lame_flags);
514 int samples_per_frame = 22050 / 12; // AS: (samples/sec) / (frames/sec) = samples/frame
515 int mp3buffer_size = static_cast<int>(samples_per_frame*1.25 + 7200 + 7200);
518 startTag(TAG_SOUNDSTREAMHEAD2);
520 mpTag->addUI8(2<<2 | 1<<1 | 0<<0); // Preferred mixer format ??
522 BitStream bs;
524 bs.writeUB(MP3_COMPRESSION,4);
525 bs.writeUB(2, 2); // AS: Reserved zero bits.
526 bs.writeUB(1, 1); // AS: 16 Bit
527 bs.writeUB(0, 1); // AS: Mono.
529 mpTag->addBits(bs);
531 mpTag->addUI16(samples_per_frame);
532 endTag();
534 short *sample_buff = new short[static_cast<int>(info.frames)];
535 sf_readf_short(sf, sample_buff, info.frames);
537 unsigned char* mp3buffer = new unsigned char[mp3buffer_size];
539 // 5. Encode some data. input pcm data, output (maybe) mp3 frames.
540 // This routine handles all buffering, resampling and filtering for you.
541 // The required mp3buffer_size can be computed from num_samples,
542 // samplerate and encoding rate, but here is a worst case estimate:
543 // mp3buffer_size (in bytes) = 1.25*num_samples + 7200.
544 // num_samples = the number of PCM samples in each channel. It is
545 // not the sum of the number of samples in the L and R channels.
547 // The return code = number of bytes output in mp3buffer. This can be 0.
548 // If it is <0, an error occured.
551 for (int samples_written = 0; samples_written < info.frames; samples_written += samples_per_frame)
553 startTag(TAG_SOUNDSTREAMBLOCK);
555 int samples_to_write = std::min((int)info.frames - samples_written, samples_per_frame);
557 // AS: Since we're mono, left and right sample buffs are the same
558 // ie, samplebuff (which is why we pass it twice).
559 int ret = lame_encode_buffer(m_lame_flags, sample_buff + samples_written,
560 sample_buff + samples_written,
561 samples_to_write, mp3buffer, mp3buffer_size);
563 if (ret < 0)
564 throw 0;
566 // 6. lame_encode_flush will flush the buffers and may return a
567 // final few mp3 frames. mp3buffer should be at least 7200 bytes.
568 // return code = number of bytes output to mp3buffer. This can be 0.
570 if (mp3buffer_size - ret < 7200)
571 throw 0;
573 int ret2 = lame_encode_flush(m_lame_flags, mp3buffer + ret, mp3buffer_size - ret);
575 if (ret2 < 0)
576 throw 0;
579 SvMemoryStream strm(mp3buffer, ret + ret2, STREAM_READWRITE);
581 mpTag->addUI16(samples_to_write); //lame_frame_size);
582 mpTag->addUI16(0);
583 mpTag->addStream(strm);
585 endTag();
587 showFrame();
591 delete[] mp3buffer;
593 delete[] sample_buff;
594 int err = sf_close(sf);
596 // 8. free the internal data structures.
597 lame_close(m_lame_flags);
600 return sal_True;
602 #endif // AUGUSTUS
605 // -----------------------------------------------------------------------------
607 void Writer::stop()
609 startTag( TAG_DOACTION );
610 mpTag->addUI8( 0x07 );
611 mpTag->addUI8( 0 );
612 endTag();
615 // -----------------------------------------------------------------------------
617 void Writer::waitOnClick( sal_uInt16 nDepth )
619 placeShape( _uInt16( mnPageButtonId ), nDepth, 0, 0 );
620 stop();
621 showFrame();
622 removeShape( nDepth );
625 // -----------------------------------------------------------------------------
627 /** inserts a doaction tag with an ActionGotoFrame */
628 void Writer::gotoFrame( sal_uInt16 nFrame )
630 startTag( TAG_DOACTION );
631 mpTag->addUI8( 0x81 );
632 mpTag->addUI16( 2 );
633 mpTag->addUI16( nFrame );
634 mpTag->addUI8( 0 );
635 endTag();