xref: /AOO41X/main/filter/source/flash/swfwriter.cxx (revision 9e0fc027f109ec4ffcb6033aeec742a099701108)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_filter.hxx"
26 #include "swfwriter.hxx"
27 #include <vcl/virdev.hxx>
28 #include <vcl/gdimtf.hxx>
29 #include <basegfx/matrix/b2dhommatrixtools.hxx>
30 
31 using namespace ::swf;
32 using namespace ::std;
33 using namespace ::com::sun::star::uno;
34 using namespace ::com::sun::star::io;
35 
36 // -----------------------------------------------------------------------------
37 
38 static MapMode aTWIPSMode( MAP_TWIP );
39 static MapMode a100thmmMode( MAP_100TH_MM );
40 
map100thmm(sal_Int32 n100thMM)41 static sal_Int32 map100thmm( sal_Int32 n100thMM )
42 {
43     Point aPoint( n100thMM, n100thMM );
44     sal_Int32 nX = OutputDevice::LogicToLogic( aPoint,  a100thmmMode, aTWIPSMode ).X();
45     return nX;
46 }
47 
48 // -----------------------------------------------------------------------------
49 
Writer(sal_Int32 nTWIPWidthOutput,sal_Int32 nTWIPHeightOutput,sal_Int32 nDocWidthInput,sal_Int32 nDocHeightInput,sal_Int32 nJPEGcompressMode)50 Writer::Writer( sal_Int32 nTWIPWidthOutput, sal_Int32 nTWIPHeightOutput, sal_Int32 nDocWidthInput, sal_Int32 nDocHeightInput, sal_Int32 nJPEGcompressMode )
51 :   mpClipPolyPolygon( NULL ),
52     mpTag( NULL ),
53     mpSprite( NULL ),
54     mnNextId( 1 ),
55     mnGlobalTransparency(0),
56     mnJPEGCompressMode(nJPEGcompressMode)
57 {
58     mpVDev = new VirtualDevice;
59     mpVDev->EnableOutput( sal_False );
60 
61     maMovieTempFile.EnableKillingFile();
62     maFontsTempFile.EnableKillingFile();
63 
64     mpMovieStream = maMovieTempFile.GetStream( STREAM_WRITE|STREAM_TRUNC );
65     mpFontsStream = maFontsTempFile.GetStream( STREAM_WRITE|STREAM_TRUNC );
66 
67     mnFrames = 0;
68 
69     mnDocWidth = map100thmm( nDocWidthInput );
70     mnDocHeight = map100thmm( nDocHeightInput );
71 
72     mnDocXScale = (double)nTWIPWidthOutput / mnDocWidth;
73     mnDocYScale = (double)nTWIPHeightOutput / mnDocHeight;
74 
75 #ifndef AUGUSTUS
76     // define an invisible button with the size of a page
77     Rectangle aRect( 0, 0, (long)( mnDocWidth * mnDocXScale ), (long)( mnDocHeight * mnDocYScale ) );
78     Polygon aPoly( aRect );
79     FillStyle aFill = FillStyle( Color(COL_WHITE) );
80     mnWhiteBackgroundShapeId = defineShape( aPoly, aFill );
81 
82     ::basegfx::B2DHomMatrix m; // #i73264#
83     mnPageButtonId = createID();
84     startTag( TAG_DEFINEBUTTON );
85     mpTag->addUI16( mnPageButtonId );           // character id for button
86 
87     // button records
88     mpTag->addUI8( 0x08 );                      // only hit state
89     mpTag->addUI16( mnWhiteBackgroundShapeId ); // shape id of background rectangle
90     mpTag->addUI16( 0 );                        // depth for button DANGER!
91     mpTag->addMatrix( m );                      // identity matrix
92     mpTag->addUI8( 0 );                         // empty color transform
93 
94 //  mpTag->addUI8( 0 );                         // end of button records
95 
96     // action records
97     mpTag->addUI8( 0x06 );                      // ActionPlay
98     mpTag->addUI8( 0 );                         // end of action records
99 
100     endTag();
101 
102     // place a shape that clips shapes depth 2-3 to document boundaries
103 //  placeShape( mnWhiteBackgroundShapeId, 1, 0, 0, 4 );
104 #endif
105 }
106 
107 // -----------------------------------------------------------------------------
108 
~Writer()109 Writer::~Writer()
110 {
111     delete mpVDev;
112     delete mpSprite;
113     delete mpTag;
114 }
115 
116 // -----------------------------------------------------------------------------
117 
ImplCopySvStreamToXOutputStream(SvStream & rIn,Reference<XOutputStream> & xOut)118 void ImplCopySvStreamToXOutputStream( SvStream& rIn, Reference< XOutputStream > &xOut )
119 {
120     sal_uInt32 nBufferSize = 64*1024;
121 
122     rIn.Seek( STREAM_SEEK_TO_END );
123     sal_uInt32 nSize = rIn.Tell();
124     rIn.Seek( STREAM_SEEK_TO_BEGIN );
125 
126     Sequence< sal_Int8 > aBuffer( min( nBufferSize, nSize ) );
127 
128     while( nSize )
129     {
130         if( nSize < nBufferSize )
131         {
132             nBufferSize = nSize;
133             aBuffer.realloc( nSize );
134         }
135 
136         sal_uInt32 nRead = rIn.Read( aBuffer.getArray(), nBufferSize );
137         DBG_ASSERT( nRead == nBufferSize, "ImplCopySvStreamToXOutputStream failed!" );
138         xOut->writeBytes( aBuffer );
139 
140         if( nRead == 0 )
141             break;
142 
143         nSize -= nRead;
144     }
145 }
146 
147 // -----------------------------------------------------------------------------
148 
storeTo(Reference<XOutputStream> & xOutStream)149 void Writer::storeTo( Reference< XOutputStream > &xOutStream )
150 {
151     for(FontMap::iterator i = maFonts.begin(); i != maFonts.end(); i++)
152     {
153         FlashFont* pFont = (*i);
154         pFont->write( *mpFontsStream );
155         delete pFont;
156     }
157 
158     // Endtag
159     *mpMovieStream << (sal_uInt16)0;
160 
161     Tag aHeader( 0xff );
162 
163     aHeader.addUI8( 'F' );
164     aHeader.addUI8( 'W' );
165     aHeader.addUI8( 'S' );
166     aHeader.addUI8( 5 );
167 
168     sal_uInt32 nSizePos = aHeader.Tell();
169 
170     aHeader << (sal_uInt32)0;
171 
172     Rectangle aDocRect( 0, 0, static_cast<long>(mnDocWidth*mnDocXScale), static_cast<long>(mnDocHeight*mnDocYScale) );
173 
174     aHeader.addRect( aDocRect );
175 
176     // frame delay in 8.8 fixed number of frames per second
177     aHeader.addUI8( 0 );
178     aHeader.addUI8( 12 );
179 
180     aHeader.addUI16( _uInt16(mnFrames) );
181 
182     const sal_uInt32 nSize = aHeader.Tell() + mpFontsStream->Tell() + mpMovieStream->Tell();
183 
184     aHeader.Seek( nSizePos );
185     aHeader << (sal_uInt32)nSize;
186 
187     ImplCopySvStreamToXOutputStream( aHeader, xOutStream );
188     ImplCopySvStreamToXOutputStream( *mpFontsStream, xOutStream );
189     ImplCopySvStreamToXOutputStream( *mpMovieStream, xOutStream );
190 }
191 
192 // -----------------------------------------------------------------------------
193 
startSprite()194 sal_uInt16 Writer::startSprite()
195 {
196     sal_uInt16 nShapeId = createID();
197     mvSpriteStack.push(mpSprite);
198     mpSprite = new Sprite( nShapeId );
199     return nShapeId;
200 }
201 
202 // -----------------------------------------------------------------------------
203 
endSprite()204 void Writer::endSprite()
205 {
206     if( mpSprite )
207     {
208         startTag( TAG_END );
209         endTag();
210 
211         mpSprite->write( *mpMovieStream );
212         delete mpSprite;
213 
214         if (mvSpriteStack.size() > 0)
215         {
216             mpSprite = mvSpriteStack.top();
217             mvSpriteStack.pop();
218         }
219         else
220             mpSprite = NULL;
221     }
222 }
223 
224 // -----------------------------------------------------------------------------
225 
placeShape(sal_uInt16 nID,sal_uInt16 nDepth,sal_Int32 x,sal_Int32 y,sal_uInt16 nClip,const char * pName)226 void Writer::placeShape( sal_uInt16 nID, sal_uInt16 nDepth, sal_Int32 x, sal_Int32 y, sal_uInt16 nClip, const char* pName )
227 {
228     startTag( TAG_PLACEOBJECT2 );
229 
230     BitStream aBits;
231 
232     aBits.writeUB( nClip != 0, 1 );     // Has Clip Actions?
233     aBits.writeUB( 0, 1 );              // reserved
234     aBits.writeUB( pName != NULL, 1 );  // has a name
235     aBits.writeUB( 0, 1 );              // no ratio
236     aBits.writeUB( 0, 1 );              // no color transform
237     aBits.writeUB( 1, 1 );              // has a matrix
238     aBits.writeUB( 1, 1 );              // places a character
239     aBits.writeUB( 0, 1 );              // does not define a character to be moved
240 
241     mpTag->addBits( aBits );
242     mpTag->addUI16( nDepth );       // depth
243     mpTag->addUI16( nID );          // character Id
244 
245     // #i73264#
246     const basegfx::B2DHomMatrix aMatrix(basegfx::tools::createTranslateB2DHomMatrix(
247         _Int16(static_cast<long>(map100thmm(x)*mnDocXScale)),
248         _Int16(static_cast<long>(map100thmm(y)*mnDocYScale))));
249     mpTag->addMatrix( aMatrix );        // transformation matrix
250 
251     if( pName )
252         mpTag->addString( pName );
253 
254     if( nClip != 0 )
255         mpTag->addUI16( nClip );
256 
257     endTag();
258 }
259 
260 #ifdef THEFUTURE
261 // -----------------------------------------------------------------------------
262 
moveShape(sal_uInt16 nDepth,sal_Int32 x,sal_Int32 y)263 void Writer::moveShape( sal_uInt16 nDepth, sal_Int32 x, sal_Int32 y )
264 {
265     startTag( TAG_PLACEOBJECT2 );
266 
267     BitStream aBits;
268     aBits.writeUB( 0, 1 );              // Has no Clip Actions
269     aBits.writeUB( 0, 1 );              // reserved
270     aBits.writeUB( 0, 1 );              // has no name
271     aBits.writeUB( 0, 1 );              // no ratio
272     aBits.writeUB( 0, 1 );              // no color transform
273     aBits.writeUB( 1, 1 );              // has a matrix
274     aBits.writeUB( 0, 1 );              // places a character
275     aBits.writeUB( 1, 1 );              // defines a character to be moved
276 
277     mpTag->addBits( aBits );
278     mpTag->addUI16( nDepth );           // depth
279 
280     // #i73264#
281     const basegfx::B2DHomMatrix aMatrix(basegfx::tools::createTranslateB2DHomMatrix(
282         _Int16(static_cast<long>(map100thmm(x)*mnDocXScale)),
283         _Int16(static_cast<long>(map100thmm(y)*mnDocYScale))));
284     mpTag->addMatrix( aMatrix );        // transformation matrix
285 
286     endTag();
287 }
288 #endif
289 
290 // -----------------------------------------------------------------------------
291 
removeShape(sal_uInt16 nDepth)292 void Writer::removeShape( sal_uInt16 nDepth )
293 {
294     startTag( TAG_REMOVEOBJECT2 );
295     mpTag->addUI16( nDepth );           // depth
296     endTag();
297 }
298 
299 // -----------------------------------------------------------------------------
300 
startTag(sal_uInt8 nTagId)301 void Writer::startTag( sal_uInt8 nTagId )
302 {
303     DBG_ASSERT( mpTag == NULL, "Last tag was not ended");
304 
305     mpTag = new Tag( nTagId );
306 }
307 
308 // -----------------------------------------------------------------------------
309 
endTag()310 void Writer::endTag()
311 {
312     sal_uInt8 nTag = mpTag->getTagId();
313 
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) ) )
315     {
316         mpSprite->addTag( mpTag );
317         mpTag = NULL;
318     }
319     else
320     {
321         mpTag->write( *mpMovieStream );
322         delete mpTag;
323         mpTag = NULL;
324     }
325 }
326 
327 // -----------------------------------------------------------------------------
328 
createID()329 sal_uInt16 Writer::createID()
330 {
331     return mnNextId++;
332 }
333 
334 // -----------------------------------------------------------------------------
335 
showFrame()336 void Writer::showFrame()
337 {
338     startTag( TAG_SHOWFRAME );
339     endTag();
340 
341     if(NULL == mpSprite)
342         mnFrames++;
343 }
344 
345 // -----------------------------------------------------------------------------
346 
defineShape(const GDIMetaFile & rMtf,sal_Int16 x,sal_Int16 y)347 sal_uInt16 Writer::defineShape( const GDIMetaFile& rMtf, sal_Int16 x, sal_Int16 y )
348 {
349     mpVDev->SetMapMode( rMtf.GetPrefMapMode() );
350     Impl_writeActions( rMtf );
351 
352     sal_uInt16 nId = 0;
353     sal_uInt16 iDepth = 1;
354     {
355         CharacterIdVector::iterator aIter( maShapeIds.begin() );
356         const CharacterIdVector::iterator aEnd( maShapeIds.end() );
357 
358         sal_Bool bHaveShapes = aIter != aEnd;
359 
360         if (bHaveShapes)
361         {
362             nId = startSprite();
363 
364             while( aIter != aEnd )
365             {
366                 placeShape( *aIter, iDepth++, x, y );
367                 aIter++;
368             }
369 
370             endSprite();
371         }
372     }
373 
374     maShapeIds.clear();
375 
376     return nId;
377 }
378 
379 // -----------------------------------------------------------------------------
380 
defineShape(const Polygon & rPoly,const FillStyle & rFillStyle)381 sal_uInt16 Writer::defineShape( const Polygon& rPoly, const FillStyle& rFillStyle )
382 {
383     const PolyPolygon aPolyPoly( rPoly );
384     return defineShape( aPolyPoly, rFillStyle );
385 }
386 
387 // -----------------------------------------------------------------------------
388 
defineShape(const PolyPolygon & rPolyPoly,const FillStyle & rFillStyle)389 sal_uInt16 Writer::defineShape( const PolyPolygon& rPolyPoly, const FillStyle& rFillStyle )
390 {
391     sal_uInt16 nShapeId = createID();
392 
393     // start a DefineShape3 tag
394     startTag( TAG_DEFINESHAPE3 );
395 
396     mpTag->addUI16( nShapeId );
397     mpTag->addRect( rPolyPoly.GetBoundRect() );
398 
399 
400     // FILLSTYLEARRAY
401     mpTag->addUI8( 1 );         // FillStyleCount
402 
403     // FILLSTYLE
404     rFillStyle.addTo( mpTag );
405 
406     // LINESTYLEARRAY
407     mpTag->addUI8( 0 );         // LineStyleCount
408 
409     // Number of fill and line index bits to 1
410     mpTag->addUI8( 0x11 );
411 
412     BitStream aBits;
413 
414     const sal_uInt16 nCount = rPolyPoly.Count();
415     sal_uInt16 i;
416     for( i = 0; i < nCount; i++ )
417     {
418         const Polygon& rPoly = rPolyPoly[ i ];
419         if( rPoly.GetSize() )
420             Impl_addPolygon( aBits, rPoly, true );
421     }
422 
423     Impl_addEndShapeRecord( aBits );
424 
425     mpTag->addBits( aBits );
426     endTag();
427 
428     return nShapeId;
429 }
430 
431 // -----------------------------------------------------------------------------
432 
defineShape(const PolyPolygon & rPolyPoly,sal_uInt16 nLineWidth,const Color & rLineColor)433 sal_uInt16 Writer::defineShape( const PolyPolygon& rPolyPoly, sal_uInt16 nLineWidth, const Color& rLineColor )
434 {
435     sal_uInt16 nShapeId = createID();
436 
437     // start a DefineShape3 tag
438     startTag( TAG_DEFINESHAPE3 );
439 
440     mpTag->addUI16( nShapeId );
441     mpTag->addRect( rPolyPoly.GetBoundRect() );
442 
443 
444     // FILLSTYLEARRAY
445     mpTag->addUI8( 0 );         // FillStyleCount
446 
447     // LINESTYLEARRAY
448     mpTag->addUI8( 1 );         // LineStyleCount
449 
450     // LINESTYLE
451     mpTag->addUI16( nLineWidth );   // Width of line in twips
452     mpTag->addRGBA( rLineColor );   // Color
453 
454     // Number of fill and line index bits to 1
455     mpTag->addUI8( 0x11 );
456 
457     BitStream aBits;
458 
459     const sal_uInt16 nCount = rPolyPoly.Count();
460     sal_uInt16 i;
461     for( i = 0; i < nCount; i++ )
462     {
463         const Polygon& rPoly = rPolyPoly[ i ];
464         if( rPoly.GetSize() )
465             Impl_addPolygon( aBits, rPoly, false );
466     }
467 
468     Impl_addEndShapeRecord( aBits );
469 
470     mpTag->addBits( aBits );
471     endTag();
472 
473     return nShapeId;
474 }
475 
476 #ifdef AUGUSTUS
477 enum {NO_COMPRESSION, ADPCM_COMPRESSION, MP3_COMPRESSION } COMPRESSION_TYPE;
streamSound(const char * filename)478 sal_Bool Writer::streamSound( const char * filename )
479 {
480     SF_INFO      info;
481     SNDFILE *sf = sf_open(filename, SFM_READ, &info);
482 
483     if (NULL == sf)
484         return sal_False;
485     else
486     {
487         // AS: Start up lame.
488         m_lame_flags = lame_init();
489 
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:
493 
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 */
499 
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.
504 
505         //4. Set more internal configuration based on data provided above,
506         //   as well as checking for problems.  Check that ret_code >= 0.
507 
508         int ret_code = lame_init_params(m_lame_flags);
509 
510         if (ret_code < 0)
511             throw 0;
512 
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);
516 
517 
518         startTag(TAG_SOUNDSTREAMHEAD2);
519 
520         mpTag->addUI8(2<<2 | 1<<1 | 0<<0);  // Preferred mixer format ??
521 
522         BitStream bs;
523 
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.
528 
529         mpTag->addBits(bs);
530 
531         mpTag->addUI16(samples_per_frame);
532         endTag();
533 
534         short *sample_buff = new short[static_cast<int>(info.frames)];
535         sf_readf_short(sf, sample_buff, info.frames);
536 
537         unsigned char* mp3buffer = new unsigned char[mp3buffer_size];
538 
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.
546 //
547 // The return code = number of bytes output in mp3buffer.  This can be 0.
548 // If it is <0, an error occured.
549 
550 
551         for (int samples_written = 0; samples_written < info.frames; samples_written += samples_per_frame)
552         {
553             startTag(TAG_SOUNDSTREAMBLOCK);
554 
555             int samples_to_write = std::min((int)info.frames - samples_written, samples_per_frame);
556 
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);
562 
563             if (ret < 0)
564                 throw 0;
565 
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.
569 
570             if (mp3buffer_size - ret < 7200)
571                 throw 0;
572 
573             int ret2 = lame_encode_flush(m_lame_flags, mp3buffer + ret, mp3buffer_size - ret);
574 
575             if (ret2 < 0)
576                 throw 0;
577 
578 
579             SvMemoryStream strm(mp3buffer, ret + ret2, STREAM_READWRITE);
580 
581             mpTag->addUI16(samples_to_write); //lame_frame_size);
582             mpTag->addUI16(0);
583             mpTag->addStream(strm);
584 
585             endTag();
586 
587             showFrame();
588         }
589 
590 
591         delete[] mp3buffer;
592 
593         delete[] sample_buff;
594         int err = sf_close(sf);
595 
596         // 8. free the internal data structures.
597         lame_close(m_lame_flags);
598     }
599 
600     return sal_True;
601 }
602 #endif // AUGUSTUS
603 
604 
605 // -----------------------------------------------------------------------------
606 
stop()607 void Writer::stop()
608 {
609     startTag( TAG_DOACTION );
610     mpTag->addUI8( 0x07 );
611     mpTag->addUI8( 0 );
612     endTag();
613 }
614 
615 // -----------------------------------------------------------------------------
616 
waitOnClick(sal_uInt16 nDepth)617 void Writer::waitOnClick( sal_uInt16 nDepth )
618 {
619     placeShape( _uInt16( mnPageButtonId ), nDepth, 0, 0 );
620     stop();
621     showFrame();
622     removeShape( nDepth );
623 }
624 
625 // -----------------------------------------------------------------------------
626 
627 /** inserts a doaction tag with an ActionGotoFrame */
gotoFrame(sal_uInt16 nFrame)628 void Writer::gotoFrame( sal_uInt16 nFrame )
629 {
630     startTag( TAG_DOACTION );
631     mpTag->addUI8( 0x81 );
632     mpTag->addUI16( 2 );
633     mpTag->addUI16( nFrame );
634     mpTag->addUI8( 0 );
635     endTag();
636 }
637