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_cppcanvas.hxx"
26 
27 #include <tools/gen.hxx>
28 
29 #include <canvas/debug.hxx>
30 #include <canvas/verbosetrace.hxx>
31 #include <canvas/canvastools.hxx>
32 
33 #include <rtl/logfile.hxx>
34 
35 #include <com/sun/star/rendering/XBitmap.hpp>
36 #include <com/sun/star/rendering/XCanvas.hpp>
37 
38 #include <rtl/math.hxx>
39 
40 #include <vcl/metaact.hxx>
41 #include <vcl/bitmapex.hxx>
42 #include <vcl/canvastools.hxx>
43 #include <vcl/svapp.hxx>
44 #include <vcl/outdev.hxx>
45 #include <vcl/virdev.hxx>
46 #include <vcl/virdev.hxx>
47 #include <vcl/gdimtf.hxx>
48 #include <vcl/gradient.hxx>
49 
50 #include <canvas/canvastools.hxx>
51 
52 #include <basegfx/range/b2drange.hxx>
53 #include <basegfx/point/b2dpoint.hxx>
54 #include <basegfx/vector/b2dsize.hxx>
55 #include <basegfx/numeric/ftools.hxx>
56 #include <basegfx/matrix/b2dhommatrix.hxx>
57 #include <basegfx/tuple/b2dtuple.hxx>
58 #include <basegfx/tools/canvastools.hxx>
59 
60 #include <boost/utility.hpp>
61 
62 #include "transparencygroupaction.hxx"
63 #include "outdevstate.hxx"
64 #include "mtftools.hxx"
65 #include "cppcanvas/vclfactory.hxx"
66 
67 
68 using namespace ::com::sun::star;
69 
70 namespace cppcanvas
71 {
72     namespace internal
73     {
74 		// free support functions
75 		// ======================
76         namespace
77         {
78             class TransparencyGroupAction : public Action, private ::boost::noncopyable
79             {
80             public:
81                 /** Create new transparency group action.
82 
83         	    	@param rGroupMtf
84                     Metafile that groups all actions to be rendered
85                     transparent
86 
87                     @param rParms
88                     Render parameters
89 
90                     @param rDstPoint
91                     Left, top edge of destination, in current state
92                     coordinate system
93 
94                     @param rDstSize
95                     Size of the transparency group object, in current
96                     state coordinate system.
97 
98                     @param nAlpha
99                     Alpha value, must be in the range [0,1]
100                 */
101                 TransparencyGroupAction( MtfAutoPtr&					rGroupMtf,
102                                          const Renderer::Parameters& 	rParms,
103                                          const ::basegfx::B2DPoint& 	rDstPoint,
104                                          const ::basegfx::B2DVector& 	rDstSize,
105                                          double 						nAlpha,
106                                          const CanvasSharedPtr&			rCanvas,
107                                          const OutDevState& 			rState );
108 
109                 /** Create new transparency group action.
110 
111 	            	@param rGroupMtf
112                     Metafile that groups all actions to be rendered
113                     transparent.
114 
115                     @param rAlphaGradient
116                     VCL gradient, to be rendered into the action's alpha
117                     channel.
118 
119                     @param rParms
120                     Render parameters
121 
122                     @param rDstPoint
123                     Left, top edge of destination, in current state
124                     coordinate system
125 
126                     @param rDstSize
127                     Size of the transparency group object, in current
128                     state coordinate system.
129                 */
130                 TransparencyGroupAction( MtfAutoPtr&					rGroupMtf,
131                                          GradientAutoPtr&				rAlphaGradient,
132                                          const Renderer::Parameters& 	rParms,
133                                          const ::basegfx::B2DPoint& 	rDstPoint,
134                                          const ::basegfx::B2DVector& 	rDstSize,
135                                          const CanvasSharedPtr&			rCanvas,
136                                          const OutDevState& 			rState );
137 
138                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
139                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation,
140                                      const Subset&					rSubset ) const;
141 
142                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
143                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
144                                                        const Subset&					rSubset ) const;
145 
146                 virtual sal_Int32 getActionCount() const;
147 
148             private:
149                 MtfAutoPtr											mpGroupMtf;
150                 GradientAutoPtr										mpAlphaGradient;
151 
152                 const Renderer::Parameters 							maParms;
153 
154                 const ::basegfx::B2DSize 							maDstSize;
155 
156                 mutable uno::Reference< rendering::XBitmap > 		mxBufferBitmap; // contains last rendered version
157                 mutable ::basegfx::B2DHomMatrix						maLastTransformation; // contains last active transformation
158                 mutable Subset										maLastSubset; // contains last effective subset
159 
160                 // transformation for
161                 // mxBufferBitmap content
162                 CanvasSharedPtr										mpCanvas;
163                 rendering::RenderState								maState;
164                 const double										mnAlpha;
165             };
166 
167 
168             /** Setup transformation such that the next render call is
169                 moved rPoint away, and scaled according to the ratio
170                 given by src and dst size.
171             */
172             void implSetupTransform( rendering::RenderState& 	rRenderState,
173                                      const ::basegfx::B2DPoint&	rDstPoint	)
174             {
175                 ::basegfx::B2DHomMatrix	aLocalTransformation;
176 
177                 aLocalTransformation.translate( rDstPoint.getX(),
178                                                 rDstPoint.getY() );
179                 ::canvas::tools::appendToRenderState( rRenderState,
180                                                       aLocalTransformation );
181             }
182 
183             TransparencyGroupAction::TransparencyGroupAction( MtfAutoPtr&					rGroupMtf,
184                                                               const Renderer::Parameters& 	rParms,
185                                                               const ::basegfx::B2DPoint& 	rDstPoint,
186                                                               const ::basegfx::B2DVector& 	rDstSize,
187                                                               double 						nAlpha,
188                                                               const CanvasSharedPtr&		rCanvas,
189                                                               const OutDevState& 			rState ) :
190                 mpGroupMtf( rGroupMtf ),
191                 mpAlphaGradient(),
192                 maParms( rParms ),
193                 maDstSize( rDstSize ),
194                 mxBufferBitmap(),
195                 maLastTransformation(),
196                 mpCanvas( rCanvas ),
197                 maState(),
198                 mnAlpha( nAlpha )
199             {
200                 tools::initRenderState(maState,rState);
201                 implSetupTransform( maState, rDstPoint );
202 
203                 // correct clip (which is relative to original transform)
204                 tools::modifyClip( maState,
205                                    rState,
206                                    rCanvas,
207                                    rDstPoint,
208                                    NULL,
209                                    NULL );
210 
211                 maLastSubset.mnSubsetBegin = 0;
212                 maLastSubset.mnSubsetEnd = -1;
213             }
214 
215             TransparencyGroupAction::TransparencyGroupAction( MtfAutoPtr&					rGroupMtf,
216                                                               GradientAutoPtr&				rAlphaGradient,
217                                                               const Renderer::Parameters& 	rParms,
218                                                               const ::basegfx::B2DPoint& 	rDstPoint,
219                                                               const ::basegfx::B2DVector& 	rDstSize,
220                                                               const CanvasSharedPtr&		rCanvas,
221                                                               const OutDevState& 			rState ) :
222                 mpGroupMtf( rGroupMtf ),
223                 mpAlphaGradient( rAlphaGradient ),
224                 maParms( rParms ),
225                 maDstSize( rDstSize ),
226                 mxBufferBitmap(),
227                 maLastTransformation(),
228                 mpCanvas( rCanvas ),
229                 maState(),
230                 mnAlpha( 1.0 )
231             {
232                 tools::initRenderState(maState,rState);
233                 implSetupTransform( maState, rDstPoint );
234 
235                 // correct clip (which is relative to original transform)
236                 tools::modifyClip( maState,
237                                    rState,
238                                    rCanvas,
239                                    rDstPoint,
240                                    NULL,
241                                    NULL );
242 
243                 maLastSubset.mnSubsetBegin = 0;
244                 maLastSubset.mnSubsetEnd = -1;
245             }
246 
247             // TODO(P3): The whole float transparency handling is a mess,
248             // this should be refactored. What's more, the old idea of
249             // having only internal 'metaactions', and not the original
250             // GDIMetaFile now looks a lot less attractive. Try to move
251             // into the direction of having a direct GDIMetaFile2XCanvas
252             // renderer, and maybe a separate metafile XCanvas
253             // implementation.
254             bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix&	rTransformation,
255                                                   const Subset&						rSubset ) const
256             {
257                 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TransparencyGroupAction::render()" );
258                 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TransparencyGroupAction: 0x%X", this );
259 
260                 // determine overall transformation matrix (render, view,
261                 // and passed transformation)
262                 ::basegfx::B2DHomMatrix aTransform;
263                 ::canvas::tools::getRenderStateTransform( aTransform, maState );
264                 aTransform = rTransformation * aTransform;
265 
266                 ::basegfx::B2DHomMatrix aTotalTransform;
267                 ::canvas::tools::getViewStateTransform( aTotalTransform, mpCanvas->getViewState() );
268                 aTotalTransform = aTotalTransform * aTransform;
269 
270                 // since pure translational changes to the transformation
271                 // does not matter, remove them before comparing
272                 aTotalTransform.set( 0, 2, 0.0 );
273                 aTotalTransform.set( 1, 2, 0.0 );
274 
275                 // if there's no buffer bitmap, or as soon as the
276                 // total transformation changes, we've got to
277                 // re-render the bitmap
278                 if( !mxBufferBitmap.is() ||
279                     aTotalTransform != maLastTransformation ||
280                     rSubset.mnSubsetBegin != maLastSubset.mnSubsetBegin ||
281                     rSubset.mnSubsetEnd != maLastSubset.mnSubsetEnd )
282                 {
283                     DBG_TESTSOLARMUTEX();
284 
285                     // determine total scaling factor of the
286                     // transformation matrix - need to make the bitmap
287                     // large enough
288                     ::basegfx::B2DTuple aScale;
289                     ::basegfx::B2DTuple aTranslate;
290                     double				nRotate;
291                     double				nShearX;
292                     if( !aTotalTransform.decompose( aScale,
293                                                     aTranslate,
294                                                     nRotate,
295                                                     nShearX ) )
296                     {
297                         OSL_ENSURE( false,
298                                     "TransparencyGroupAction::render(): non-decomposable transformation" );
299                         return false;
300                     }
301 
302                     // output size of metafile
303                     ::Size aOutputSizePixel( ::basegfx::fround( aScale.getX() * maDstSize.getX() ),
304                                              ::basegfx::fround( aScale.getY() * maDstSize.getY() ) );
305 
306                     // pixel size of cache bitmap: round up to nearest int
307                     ::Size aBitmapSizePixel( static_cast<sal_Int32>( aScale.getX() * maDstSize.getX() )+1,
308                                              static_cast<sal_Int32>( aScale.getY() * maDstSize.getY() )+1 );
309 
310                     ::Point aEmptyPoint;
311 
312                     // render our content into an appropriately sized
313                     // VirtualDevice with alpha channel
314                     VirtualDevice aVDev(
315                         *::Application::GetDefaultDevice(), 0, 0 );
316                     aVDev.SetOutputSizePixel( aBitmapSizePixel );
317                     aVDev.SetMapMode();
318 
319                     if( rSubset.mnSubsetBegin != 0 ||
320                         rSubset.mnSubsetEnd != -1 )
321                     {
322                         // true subset - extract referenced
323                         // metaactions from mpGroupMtf
324                         GDIMetaFile aMtf;
325                         MetaAction* pCurrAct;
326                         int 		nCurrActionIndex;
327 
328                         // extract subset actions
329                         for( nCurrActionIndex=0,
330                                  pCurrAct=mpGroupMtf->FirstAction();
331                              pCurrAct;
332                              ++nCurrActionIndex, pCurrAct = mpGroupMtf->NextAction() )
333                         {
334                             switch( pCurrAct->GetType() )
335                             {
336                                 case META_PUSH_ACTION:
337                                 case META_POP_ACTION:
338                                 case META_CLIPREGION_ACTION:
339                                 case META_ISECTRECTCLIPREGION_ACTION:
340                                 case META_ISECTREGIONCLIPREGION_ACTION:
341                                 case META_MOVECLIPREGION_ACTION:
342                                 case META_LINECOLOR_ACTION:
343                                 case META_FILLCOLOR_ACTION:
344                                 case META_TEXTCOLOR_ACTION:
345                                 case META_TEXTFILLCOLOR_ACTION:
346                                 case META_TEXTLINECOLOR_ACTION:
347                                 case META_TEXTALIGN_ACTION:
348                                 case META_FONT_ACTION:
349                                 case META_RASTEROP_ACTION:
350                                 case META_REFPOINT_ACTION:
351                                 case META_LAYOUTMODE_ACTION:
352                                     // state-changing action - copy as-is
353                                     aMtf.AddAction( pCurrAct->Clone() );
354                                     break;
355 
356                                 case META_GRADIENT_ACTION:
357                                 case META_HATCH_ACTION:
358                                 case META_EPS_ACTION:
359                                 case META_COMMENT_ACTION:
360                                 case META_POINT_ACTION:
361                                 case META_PIXEL_ACTION:
362                                 case META_LINE_ACTION:
363                                 case META_RECT_ACTION:
364                                 case META_ROUNDRECT_ACTION:
365                                 case META_ELLIPSE_ACTION:
366                                 case META_ARC_ACTION:
367                                 case META_PIE_ACTION:
368                                 case META_CHORD_ACTION:
369                                 case META_POLYLINE_ACTION:
370                                 case META_POLYGON_ACTION:
371                                 case META_POLYPOLYGON_ACTION:
372                                 case META_BMP_ACTION:
373                                 case META_BMPSCALE_ACTION:
374                                 case META_BMPSCALEPART_ACTION:
375                                 case META_BMPEX_ACTION:
376                                 case META_BMPEXSCALE_ACTION:
377                                 case META_BMPEXSCALEPART_ACTION:
378                                 case META_MASK_ACTION:
379                                 case META_MASKSCALE_ACTION:
380                                 case META_MASKSCALEPART_ACTION:
381                                 case META_GRADIENTEX_ACTION:
382                                 case META_WALLPAPER_ACTION:
383                                 case META_TRANSPARENT_ACTION:
384                                 case META_FLOATTRANSPARENT_ACTION:
385                                 case META_TEXT_ACTION:
386                                 case META_TEXTARRAY_ACTION:
387                                 case META_TEXTLINE_ACTION:
388                                 case META_TEXTRECT_ACTION:
389                                 case META_STRETCHTEXT_ACTION:
390                                 case META_RENDERGRAPHIC_ACTION:
391                                     // output-generating action - only
392                                     // copy, if we're within the
393                                     // requested subset
394                                     if( rSubset.mnSubsetBegin <= nCurrActionIndex &&
395                                         rSubset.mnSubsetEnd > nCurrActionIndex )
396                                     {
397                                         aMtf.AddAction( pCurrAct->Clone() );
398                                     }
399                                     break;
400 
401                                 default:
402                                     OSL_ENSURE( false,
403                                                 "Unknown meta action type encountered" );
404                                     break;
405                             }
406                         }
407 
408                         aVDev.DrawTransparent( aMtf,
409                                                aEmptyPoint,
410                                                aOutputSizePixel,
411                                                *mpAlphaGradient );
412                     }
413                     else
414                     {
415                         // no subsetting - render whole mtf
416                         aVDev.DrawTransparent( *mpGroupMtf,
417                                                aEmptyPoint,
418                                                aOutputSizePixel,
419                                                *mpAlphaGradient );
420                     }
421 
422 
423                     // update buffered bitmap and transformation
424                     BitmapSharedPtr aBmp( VCLFactory::getInstance().createBitmap(
425                                               mpCanvas,
426                                               aVDev.GetBitmapEx(
427                                                   aEmptyPoint,
428                                                   aBitmapSizePixel ) ) );
429                     mxBufferBitmap = aBmp->getUNOBitmap();
430                     maLastTransformation = aTotalTransform;
431                     maLastSubset = rSubset;
432                 }
433 
434                 // determine target transformation (we can't simply pass
435                 // aTotalTransform as assembled above, since we must take
436                 // the canvas' view state as is, it might contain clipping
437                 // (which, in turn, is relative to the view
438                 // transformation))
439 
440                 // given that aTotalTransform is the identity
441                 // transformation, we could simply render our bitmap
442                 // as-is. Now, since the mxBufferBitmap content already
443                 // accounts for scale changes in the overall
444                 // transformation, we must factor this out
445                 // before. Generally, the transformation matrix should be
446                 // structured like this:
447                 // Translation*Rotation*Shear*Scale. Thus, to neutralize
448                 // the contained scaling, we've got to right-multiply with
449                 // the inverse.
450                 ::basegfx::B2ISize aBmpSize(
451                     ::basegfx::unotools::b2ISizeFromIntegerSize2D( mxBufferBitmap->getSize() ) );
452 
453                 ::basegfx::B2DHomMatrix aScaleCorrection;
454                 aScaleCorrection.scale( (double)maDstSize.getX() / aBmpSize.getX(),
455                                         (double)maDstSize.getY() / aBmpSize.getY() );
456                 aTransform = aTransform * aScaleCorrection;
457 
458                 rendering::RenderState aLocalState( maState );
459                 ::canvas::tools::setRenderStateTransform(aLocalState, aTransform);
460 
461 #ifdef SPECIAL_DEBUG
462                 aLocalState.Clip.clear();
463                 aLocalState.DeviceColor =
464                     ::vcl::unotools::colorToDoubleSequence(
465                         ::Color( 0x80FF0000 ),
466                         mpCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
467 
468                 if( maState.Clip.is() )
469                     mpCanvas->getUNOCanvas()->fillPolyPolygon( maState.Clip,
470                                                                mpCanvas->getViewState(),
471                                                                aLocalState );
472 
473                 aLocalState.DeviceColor = maState.DeviceColor;
474 #endif
475 
476                 if( ::rtl::math::approxEqual(mnAlpha, 1.0) )
477                 {
478                     // no further alpha changes necessary -> draw directly
479                     mpCanvas->getUNOCanvas()->drawBitmap( mxBufferBitmap,
480                                                           mpCanvas->getViewState(),
481                                                           aLocalState );
482                 }
483                 else
484                 {
485                     // add alpha modulation value to DeviceColor
486                     uno::Sequence<rendering::ARGBColor> aCols(1);
487                     aCols[0] = rendering::ARGBColor( mnAlpha, 1.0, 1.0, 1.0);
488                     aLocalState.DeviceColor =
489                         mpCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace()->convertFromARGB(
490                             aCols);
491 
492                     mpCanvas->getUNOCanvas()->drawBitmapModulated( mxBufferBitmap,
493                                                                    mpCanvas->getViewState(),
494                                                                    aLocalState );
495                 }
496 
497                 return true;
498             }
499 
500             // TODO(P3): The whole float transparency handling is a mess,
501             // this should be refactored. What's more, the old idea of
502             // having only internal 'metaactions', and not the original
503             // GDIMetaFile now looks a lot less attractive. Try to move
504             // into the direction of having a direct GDIMetaFile2XCanvas
505             // renderer, and maybe a separate metafile XCanvas
506             // implementation.
507             bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
508             {
509                 Subset aSubset;
510 
511                 aSubset.mnSubsetBegin = 0;
512                 aSubset.mnSubsetEnd   = -1;
513 
514                 return render( rTransformation, aSubset );
515             }
516 
517             ::basegfx::B2DRange TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix&	rTransformation ) const
518             {
519                 rendering::RenderState aLocalState( maState );
520                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
521 
522                 return tools::calcDevicePixelBounds(
523                     ::basegfx::B2DRange( 0,0,
524                                          maDstSize.getX(),
525                                          maDstSize.getY() ),
526                     mpCanvas->getViewState(),
527                     aLocalState );
528             }
529 
530             ::basegfx::B2DRange TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
531                                                                     const Subset&					rSubset ) const
532             {
533                 // TODO(F3): Currently, the bounds for
534                 // TransparencyGroupAction subsets equal those of the
535                 // full set, although this action is able to render
536                 // true subsets.
537 
538                 // polygon only contains a single action, empty bounds
539                 // if subset requests different range
540                 if( rSubset.mnSubsetBegin != 0 ||
541                     rSubset.mnSubsetEnd != 1 )
542                     return ::basegfx::B2DRange();
543 
544                 return getBounds( rTransformation );
545             }
546 
547             sal_Int32 TransparencyGroupAction::getActionCount() const
548             {
549                 return mpGroupMtf.get() ? mpGroupMtf->GetActionCount() : 0;
550             }
551 
552         }
553 
554         ActionSharedPtr TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr&					rGroupMtf,
555                                                                                        const Renderer::Parameters&	rParms,
556                                                                                        const ::basegfx::B2DPoint& 	rDstPoint,
557                                                                                        const ::basegfx::B2DVector& 	rDstSize,
558                                                                                        double 						nAlpha,
559                                                                                        const CanvasSharedPtr&		rCanvas,
560                                                                                        const OutDevState& 			rState )
561         {
562             return ActionSharedPtr( new TransparencyGroupAction(rGroupMtf,
563                                                                 rParms,
564                                                                 rDstPoint,
565                                                                 rDstSize,
566                                                                 nAlpha,
567                                                                 rCanvas,
568                                                                 rState ) );
569         }
570 
571         ActionSharedPtr TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr&					rGroupMtf,
572                                                                                        GradientAutoPtr&				rAlphaGradient,
573                                                                                        const Renderer::Parameters&	rParms,
574                                                                                        const ::basegfx::B2DPoint& 	rDstPoint,
575                                                                                        const ::basegfx::B2DVector&  rDstSize,
576                                                                                        const CanvasSharedPtr&		rCanvas,
577                                                                                        const OutDevState& 			rState )
578         {
579             return ActionSharedPtr( new TransparencyGroupAction(rGroupMtf,
580                                                                 rAlphaGradient,
581                                                                 rParms,
582                                                                 rDstPoint,
583                                                                 rDstSize,
584                                                                 rCanvas,
585                                                                 rState ) );
586         }
587 
588     }
589 }
590