1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: swfwriter.cxx,v $
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();
54 // -----------------------------------------------------------------------------
56 Writer::Writer( sal_Int32 nTWIPWidthOutput
, sal_Int32 nTWIPHeightOutput
, sal_Int32 nDocWidthInput
, sal_Int32 nDocHeightInput
, sal_Int32 nJPEGcompressMode
)
57 : mpClipPolyPolygon( NULL
),
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
);
75 mnDocWidth
= map100thmm( nDocWidthInput
);
76 mnDocHeight
= map100thmm( nDocHeightInput
);
78 mnDocXScale
= (double)nTWIPWidthOutput
/ mnDocWidth
;
79 mnDocYScale
= (double)nTWIPHeightOutput
/ mnDocHeight
;
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
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
103 mpTag
->addUI8( 0x06 ); // ActionPlay
104 mpTag
->addUI8( 0 ); // end of action records
108 // place a shape that clips shapes depth 2-3 to document boundaries
109 // placeShape( mnWhiteBackgroundShapeId, 1, 0, 0, 4 );
113 // -----------------------------------------------------------------------------
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
) );
136 if( nSize
< nBufferSize
)
139 aBuffer
.realloc( nSize
);
142 sal_uInt32 nRead
= rIn
.Read( aBuffer
.getArray(), nBufferSize
);
143 DBG_ASSERT( nRead
== nBufferSize
, "ImplCopySvStreamToXOutputStream failed!" );
144 xOut
->writeBytes( aBuffer
);
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
);
165 *mpMovieStream
<< (sal_uInt16
)0;
169 aHeader
.addUI8( 'F' );
170 aHeader
.addUI8( 'W' );
171 aHeader
.addUI8( 'S' );
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
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
);
208 // -----------------------------------------------------------------------------
210 void Writer::endSprite()
217 mpSprite
->write( *mpMovieStream
);
220 if (mvSpriteStack
.size() > 0)
222 mpSprite
= mvSpriteStack
.top();
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
);
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
256 mpTag
->addString( pName
);
259 mpTag
->addUI16( nClip
);
264 // -----------------------------------------------------------------------------
266 void Writer::moveShape( sal_uInt16 nDepth
, sal_Int32 x
, sal_Int32 y
)
268 startTag( TAG_PLACEOBJECT2
);
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
290 // -----------------------------------------------------------------------------
292 void Writer::removeShape( sal_uInt16 nDepth
)
294 startTag( TAG_REMOVEOBJECT2
);
295 mpTag
->addUI16( nDepth
); // depth
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
);
321 mpTag
->write( *mpMovieStream
);
327 // -----------------------------------------------------------------------------
329 sal_uInt16
Writer::createID()
334 // -----------------------------------------------------------------------------
336 void Writer::showFrame()
338 startTag( TAG_SHOWFRAME
);
345 // -----------------------------------------------------------------------------
347 sal_uInt16
Writer::defineShape( const GDIMetaFile
& rMtf
, sal_Int16 x
, sal_Int16 y
)
349 mpVDev
->SetMapMode( rMtf
.GetPrefMapMode() );
350 Impl_writeActions( rMtf
);
353 sal_uInt16 iDepth
= 1;
355 CharacterIdVector::iterator
aIter( maShapeIds
.begin() );
356 const CharacterIdVector::iterator
aEnd( maShapeIds
.end() );
358 sal_Bool bHaveShapes
= aIter
!= aEnd
;
364 while( aIter
!= aEnd
)
366 placeShape( *aIter
, iDepth
++, x
, y
);
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() );
401 mpTag
->addUI8( 1 ); // FillStyleCount
404 rFillStyle
.addTo( mpTag
);
407 mpTag
->addUI8( 0 ); // LineStyleCount
409 // Number of fill and line index bits to 1
410 mpTag
->addUI8( 0x11 );
414 const sal_uInt16 nCount
= rPolyPoly
.Count();
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
);
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() );
445 mpTag
->addUI8( 0 ); // FillStyleCount
448 mpTag
->addUI8( 1 ); // LineStyleCount
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 );
459 const sal_uInt16 nCount
= rPolyPoly
.Count();
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
);
477 enum {NO_COMPRESSION
, ADPCM_COMPRESSION
, MP3_COMPRESSION
} COMPRESSION_TYPE
;
478 sal_Bool
Writer::streamSound( const char * filename
)
481 SNDFILE
*sf
= sf_open(filename
, SFM_READ
, &info
);
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
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
);
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 ??
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.
531 mpTag
->addUI16(samples_per_frame
);
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
);
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)
573 int ret2
= lame_encode_flush(m_lame_flags
, mp3buffer
+ ret
, mp3buffer_size
- ret
);
579 SvMemoryStream
strm(mp3buffer
, ret
+ ret2
, STREAM_READWRITE
);
581 mpTag
->addUI16(samples_to_write
); //lame_frame_size);
583 mpTag
->addStream(strm
);
593 delete[] sample_buff
;
594 int err
= sf_close(sf
);
596 // 8. free the internal data structures.
597 lame_close(m_lame_flags
);
605 // -----------------------------------------------------------------------------
609 startTag( TAG_DOACTION
);
610 mpTag
->addUI8( 0x07 );
615 // -----------------------------------------------------------------------------
617 void Writer::waitOnClick( sal_uInt16 nDepth
)
619 placeShape( _uInt16( mnPageButtonId
), nDepth
, 0, 0 );
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 );
633 mpTag
->addUI16( nFrame
);