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 occurred.
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