/************************************************************************* * * $RCSfile: implrenderer.cxx,v $ * * $Revision: 1.2 $ * * last change: $Author: thb $ $Date: 2004-03-18 10:41:04 $ * * The Contents of this file are made available subject to the terms of * either of the following licenses * * - GNU Lesser General Public License Version 2.1 * - Sun Industry Standards Source License Version 1.1 * * Sun Microsystems Inc., October, 2000 * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2000 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * * Sun Industry Standards Source License Version 1.1 * ================================================= * The contents of this file are subject to the Sun Industry Standards * Source License Version 1.1 (the "License"); You may not use this file * except in compliance with the License. You may obtain a copy of the * License at http://www.openoffice.org/license.html. * * Software provided under this License is provided on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. * See the License for the specific provisions governing your rights and * obligations concerning the Software. * * The Initial Developer of the Original Code is: Sun Microsystems, Inc. * * Copyright: 2000 by Sun Microsystems, Inc. * * All Rights Reserved. * * Contributor(s): _______________________________________ * * ************************************************************************/ #ifndef _OSL_MUTEX_HXX_ #include #endif #ifndef _VOS_MUTEX_HXX_ #include #endif #ifndef _SV_SVAPP_HXX #include #endif #ifndef _COMPHELPER_SEQUENCE_HXX_ #include #endif #include #ifndef _VCL_CANVASTOOLS_HXX #include #endif #ifndef _BGFX_TOOLS_CANVASTOOLS_HXX #include #endif #ifndef _CANVAS_CANVASTOOLS_HXX #include #endif #include "implrenderer.hxx" #include "tools.hxx" #include "outdevstate.hxx" #include "action.hxx" #include "bitmapaction.hxx" #include "lineaction.hxx" #include "pointaction.hxx" #include "polypolyaction.hxx" #include "textaction.hxx" #include #include #ifndef _COM_SUN_STAR_UNO_SEQUENCE_HXX_ #include #endif #ifndef _DRAFTS_COM_SUN_STAR_GEOMETRY_REALPOINT2D_HPP__ #include #endif #ifndef _DRAFTS_COM_SUN_STAR_RENDERING_VIEWSTATE_HPP__ #include #endif #ifndef _DRAFTS_COM_SUN_STAR_RENDERING_RENDERSTATE_HPP__ #include #endif #ifndef _DRAFTS_COM_SUN_STAR_RENDERING_XCANVASFONT_HPP__ #include #endif #ifndef _DRAFTS_COM_SUN_STAR_RENDERING_XPOLYPOLYGON2D_HPP__ #include #endif #ifndef _DRAFTS_COM_SUN_STAR_RENDERING_XCANVAS_HPP__ #include #endif #ifndef _BGFX_MATRIX_B2DHOMMATRIX_HXX #include #endif #ifndef _SV_GDIMTF_HXX #include #endif #ifndef _SV_METAACT_HXX #include #endif #ifndef _SV_VIRDEV_HXX #include #endif #ifndef _TL_POLY_HXX #include #endif #include "outdevstate.hxx" using namespace ::drafts::com::sun::star; using namespace ::com::sun::star; // free support functions // ====================== namespace { template < class MetaActionType > void setStateColor( MetaActionType* pAct, bool& rIsColorSet, uno::Sequence< double >& rColorSequence, const cppcanvas::CanvasSharedPtr& rCanvas ) { // set rIsColorSet and check for true at the same time if( (rIsColorSet=pAct->IsSetting()) ) { ::Color aColor( pAct->GetColor() ); // force alpha part of color to // opaque. transparent painting is done // explicitely via META_TRANSPARENT_ACTION aColor.SetTransparency(0); //aColor.SetTransparency(128); rColorSequence = ::vcl::unotools::colorToDoubleSequence( rCanvas->getUNOCanvas()->getDevice(), aColor ); } } // Doing that via inline class. Compilers tend to not inline free // functions. class ActionIndexComparator { public: ActionIndexComparator() {} bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS, const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS ) { return rLHS.mnOrigIndex < rRHS.mnOrigIndex; } }; // state stack manipulators // ------------------------ void clearStateStack( ::cppcanvas::internal::VectorOfOutDevStates& rStates ) { rStates.clear(); const ::cppcanvas::internal::OutDevState aDefaultState; rStates.push_back( aDefaultState ); } ::cppcanvas::internal::OutDevState& getState( ::cppcanvas::internal::VectorOfOutDevStates& rStates ) { return rStates.back(); } void pushState( ::cppcanvas::internal::VectorOfOutDevStates& rStates ) { rStates.push_back( getState( rStates ) ); } void popState( ::cppcanvas::internal::VectorOfOutDevStates& rStates ) { rStates.pop_back(); } } namespace cppcanvas { namespace internal { bool ImplRenderer::createFillAndStroke( const ::PolyPolygon& rPolyPoly, const CanvasSharedPtr& rCanvas, int rActionIndex, VectorOfOutDevStates& rStates ) { maActions.push_back( MtfAction( ActionSharedPtr( new internal::PolyPolyAction( rPolyPoly, rCanvas, getState( rStates ) ) ), rActionIndex ) ); return true; } void ImplRenderer::skipContent( GDIMetaFile& rMtf, const char& rCommentString ) const { MetaAction* pCurrAct; while( (pCurrAct=rMtf.NextAction()) ) { if( pCurrAct->GetType() == META_COMMENT_ACTION && static_cast(pCurrAct)->GetComment().CompareIgnoreCaseToAscii( rCommentString ) == COMPARE_EQUAL ) { // requested comment found, done return; } } // EOF return; } void ImplRenderer::createGradientAction( const Rectangle& rRect, const Gradient& rGradient, VirtualDevice& rVDev, const CanvasSharedPtr& rCanvas, VectorOfOutDevStates& rStates ) { DBG_TESTSOLARMUTEX(); // TODO: Use native canvas gradients here (saves a lot of UNO calls) GDIMetaFile aTmpMtf; rVDev.AddGradientActions( rRect, rGradient, aTmpMtf ); pushState( rStates ); createActions( rCanvas, rVDev, aTmpMtf, rStates ); popState( rStates ); } bool ImplRenderer::createActions( const CanvasSharedPtr& rCanvas, VirtualDevice& rVDev, GDIMetaFile& rMtf, VectorOfOutDevStates& rStates ) { /* TODO: ===== - Float-Transparency (skipped for prototype - bitmap fillings (do that via comments) - gradient fillings (do that via comments) - think about mapping. _If_ we do everything in logical coordinates (which would solve the probs for stroke widths and and text offsets), then we would have to recalc scaling for every drawing operation. This is because the outdev map mode might change at any time. */ // Loop over every metaaction // ========================== MetaAction* pCurrAct; int nCurrActionIndex; // TODO: think about caching for( nCurrActionIndex=0, pCurrAct=rMtf.FirstAction(); pCurrAct; ++nCurrActionIndex, pCurrAct = rMtf.NextAction() ) { // execute every action, to keep VDev state up-to-date // (currently used only for the map mode, and for // line/fill color when processing a // META_TRANSPARENT_ACTION) pCurrAct->Execute( &rVDev ); switch( pCurrAct->GetType() ) { // ------------------------------------------------------------ // In the first part of this monster-switch, we // handle all state-changing meta actions. These // are all handled locally. // ------------------------------------------------------------ case META_PUSH_ACTION: pushState( rStates ); break; case META_POP_ACTION: popState( rStates ); break; // monitor clip regions, to assemble clip polygon on our own case META_CLIPREGION_ACTION: case META_ISECTRECTCLIPREGION_ACTION: case META_ISECTREGIONCLIPREGION_ACTION: case META_MOVECLIPREGION_ACTION: // TODO: NYI break; case META_LINECOLOR_ACTION: setStateColor( static_cast(pCurrAct), getState( rStates ).isLineColorSet, getState( rStates ).lineColor, rCanvas ); break; case META_FILLCOLOR_ACTION: setStateColor( static_cast(pCurrAct), getState( rStates ).isFillColorSet, getState( rStates ).fillColor, rCanvas ); break; case META_TEXTCOLOR_ACTION: { // Text color is set unconditionally, thus, no // use of setStateColor here ::Color aColor( static_cast(pCurrAct)->GetColor() ); // force alpha part of color to // opaque. transparent painting is done // explicitely via META_TRANSPARENT_ACTION aColor.SetTransparency(0); getState( rStates ).textColor = ::vcl::unotools::colorToDoubleSequence( rCanvas->getUNOCanvas()->getDevice(), aColor ); } break; case META_TEXTFILLCOLOR_ACTION: setStateColor( static_cast(pCurrAct), getState( rStates ).isTextFillColorSet, getState( rStates ).textFillColor, rCanvas ); break; case META_TEXTLINECOLOR_ACTION: setStateColor( static_cast(pCurrAct), getState( rStates ).isTextLineColorSet, getState( rStates ).textLineColor, rCanvas ); break; case META_TEXTALIGN_ACTION: // TODO: NYI break; case META_FONT_ACTION: { // TODO: For now, only dummy implementation rendering::FontRequest aFontRequest; const ::Font& rFont( static_cast(pCurrAct)->GetFont() ); aFontRequest.FamilyName = rFont.GetName(); aFontRequest.StyleName = rFont.GetStyleName(); // TODO: use correct scale direction, font // height might be width or anything else const Size aSize( 0, rFont.GetHeight() ); aFontRequest.CellSize = rVDev.LogicToPixel( aSize ).Height(); const short nFontAngle( rFont.GetOrientation() ); // setup state-local text transformation, // should the font be rotated if( nFontAngle != 0 ) { // VCL font does not access system structs here const double rAngle( nFontAngle * (F_PI / 1800.0) ); // reset transform getState( rStates ).fontTransform.identity(); // rotate by given angle getState( rStates ).fontTransform.rotate( -rAngle ); } getState( rStates ).xFont = rCanvas->getUNOCanvas()->queryFont( aFontRequest ); } break; case META_RASTEROP_ACTION: // TODO: NYI break; case META_REFPOINT_ACTION: // TODO: NYI break; case META_LAYOUTMODE_ACTION: { // TODO: A lot is missing here switch( static_cast(pCurrAct)->GetLayoutMode() ) { case TEXT_LAYOUT_BIDI_RTL: case TEXT_LAYOUT_TEXTORIGIN_RIGHT: getState( rStates ).textDirection = rendering::TextDirection::RIGHT_TO_LEFT; break; case TEXT_LAYOUT_BIDI_LTR: case TEXT_LAYOUT_BIDI_STRONG: case TEXT_LAYOUT_TEXTORIGIN_LEFT: case TEXT_LAYOUT_COMPLEX_DISABLED: case TEXT_LAYOUT_ENABLE_LIGATURES: case TEXT_LAYOUT_SUBSTITUTE_DIGITS: getState( rStates ).textDirection = rendering::TextDirection::LEFT_TO_RIGHT; break; } } break; // ------------------------------------------------------------ // In the second part of this monster-switch, we // handle all recursing meta actions. These are the // ones generating a metafile by themselves, which is // then processed by recursively calling this method. // ------------------------------------------------------------ case META_GRADIENT_ACTION: { MetaGradientAction* pGradAct = static_cast(pCurrAct); createGradientAction( pGradAct->GetRect(), pGradAct->GetGradient(), rVDev, rCanvas, rStates ); } break; case META_HATCH_ACTION: { // TODO: use native Canvas hatches here GDIMetaFile aTmpMtf; rVDev.AddHatchActions( static_cast(pCurrAct)->GetPolyPolygon(), static_cast(pCurrAct)->GetHatch(), aTmpMtf ); createActions( rCanvas, rVDev, aTmpMtf, rStates ); } break; case META_EPS_ACTION: { MetaEPSAction* pAct = static_cast(pCurrAct); const GDIMetaFile& pSubstitute = pAct->GetSubstitute(); const Size aMtfSizePix( rVDev.LogicToPixel( pSubstitute.GetPrefSize(), pSubstitute.GetPrefMapMode() ) ); // skip null-sized output if( aMtfSizePix.Width() != 0 && aMtfSizePix.Height() != 0 ) { const Point aEmptyPt; const Point aMtfOriginPix( rVDev.LogicToPixel( aEmptyPt, pSubstitute.GetPrefMapMode() ) ); // Setup local transform, such that the // metafile renders itself into the given // output rectangle pushState( rStates ); getState( rStates ).transform.translate( -aMtfOriginPix.X(), -aMtfOriginPix.Y() ); getState( rStates ).transform.scale( 1.0 / aMtfSizePix.Width(), 1.0 / aMtfSizePix.Height() ); createActions( rCanvas, rVDev, const_cast(pAct->GetSubstitute()), rStates ); popState( rStates ); } } break; // handle metafile comments, to retrieve // meta-information for gradients, fills and // strokes. May skip actions, and may recurse. case META_COMMENT_ACTION: { MetaCommentAction* pAct = static_cast(pCurrAct); // Handle gradients if ( pAct->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL ) { MetaGradientExAction* pGradAction = NULL; bool bDone( false ); while( !bDone && (pCurrAct=rMtf.NextAction()) ) { switch( pCurrAct->GetType() ) { // extract gradient info case META_GRADIENTEX_ACTION: pGradAction = static_cast(pCurrAct); break; // skip broken-down rendering, output gradient when sequence is ended case META_COMMENT_ACTION: if( static_cast(pCurrAct)->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL ) { bDone = true; if( pGradAction ) { pushState( rStates ); // TODO: Hack! If // current state // already contains a // clipping, we'll // overwrite it here! getState( rStates ).xClipPoly = ::vcl::unotools::xPolyPolygonFromPolyPolygon( rCanvas->getUNOCanvas()->getDevice(), rVDev.LogicToPixel( pGradAction->GetPolyPolygon() ) ); createGradientAction( pGradAction->GetPolyPolygon().GetBoundRect(), pGradAction->GetGradient(), rVDev, rCanvas, rStates ); popState( rStates ); } } break; } } } // Handle drawing layer strokes else if( pAct->GetComment().Equals( "XPATHSTROKE_SEQ_BEGIN" ) ) { // TODO: Later #if 0 const BYTE* pData = pAct->GetData(); if ( pData ) { SvMemoryStream aMemStm( (void*)pData, pA->GetDataSize(), STREAM_READ ); SvtGraphicStroke aStroke; aMemStm >> aStroke; // TODO: respect exceptions, like // start/end arrows and joins not // displayable via Canvas // TODO: use correct scale direction, stroke // width might be height or anything else const Size aSize( aStroke.getStrokeWidth(), 0 ); internal::StrokeAction aStrokeAction( rVDev.LogicToPixel( aStroke.getPath() ), aStroke.getTransparency(), rVDev.LogicToPixel( aSize ).Width(), aStroke.getJoinType(), aStroke.getDashArray(), aStroke.getMiterLimit(), aStroke.getCapType(), rCanvas, getState( rStates ) ); aStrokeAction.render( rViewState ); // skip broken-down render output skipContent( rMtf, "XPATHSTROKE_SEQ_END" ); } #endif } // Handle drawing layer fills else if( pAct->GetComment().Equals( "XPATHFILL_SEQ_BEGIN" ) ) { // TODO: Later #if 0 const BYTE* pData = pAct->GetData(); if ( pData ) { SvMemoryStream aMemStm( (void*)pData, pA->GetDataSize(), STREAM_READ ); SvtGraphicFill aFill; aMemStm >> aFill; switch( aFill.getType() ) { case SvtGraphicFill::fillSolid: { internal::SolidFillAction aFillAction( rVDev.LogicToPixel( aFill.getPath() ), aFill.getFillColor(), aFill.getTransparency(), aFill.getFillRule(), rCanvas, getState( rStates ) ); aFillAction.render( rViewState ); } break; case SvtGraphicFill::fillGradient: { internal::GradientFillAction aFillAction( rVDev.LogicToPixel( aFill.getPath() ), aFill.getTransparency(), aFill.getFillRule(), aFill.getTransform(), aFill.getGradientType(), aFill.getGradient1stColor(), aFill.getGradient2ndColor(), aFill.getGradientStepCount(), rCanvas, getState( rStates ) ); aFillAction.render( rViewState ); } break; case SvtGraphicFill::fillHatch: { internal::HatchedFillAction aFillAction( rVDev.LogicToPixel( aFill.getPath() ), aFill.getTransparency(), aFill.getFillRule(), aFill.getTransform(), aFill.getHatchType(), aFill.getHatchColor(), rCanvas, getState( rStates ) ); aFillAction.render( rViewState ); } break; case SvtGraphicFill::fillTexture: { internal::BitmapFillAction aFillAction( rVDev.LogicToPixel( aFill.getPath() ), aFill.getTransparency(), aFill.getFillRule(), aFill.getTransform(), aFill.getFillGraphic(), rCanvas, getState( rStates ) ); aFillAction.render( rViewState ); } break; } // skip broken-down render output skipContent( rMtf, "XPATHFILL_SEQ_END" ); } #endif } } break; // ------------------------------------------------------------ // In the third part of this monster-switch, we // handle all 'acting' meta actions. These are all // processed by constructing function objects for // them, which will later ease caching. // ------------------------------------------------------------ case META_POINT_ACTION: { maActions.push_back( MtfAction( ActionSharedPtr( new internal::PointAction( rVDev.LogicToPixel( static_cast(pCurrAct)->GetPoint() ), rCanvas, getState( rStates ) ) ), nCurrActionIndex ) ); } break; case META_PIXEL_ACTION: { maActions.push_back( MtfAction( ActionSharedPtr( new internal::PointAction( rVDev.LogicToPixel( static_cast(pCurrAct)->GetPoint() ), rCanvas, getState( rStates ), static_cast(pCurrAct)->GetColor() ) ), nCurrActionIndex ) ); } break; case META_LINE_ACTION: { maActions.push_back( MtfAction( ActionSharedPtr( new internal::LineAction( rVDev.LogicToPixel( static_cast(pCurrAct)->GetStartPoint() ), rVDev.LogicToPixel( static_cast(pCurrAct)->GetEndPoint() ), rCanvas, getState( rStates ) ) ), nCurrActionIndex ) ); } break; case META_RECT_ACTION: createFillAndStroke( ::PolyPolygon( ::Polygon( rVDev.LogicToPixel( static_cast(pCurrAct)->GetRect() ) ) ), rCanvas, nCurrActionIndex, rStates ); break; case META_ROUNDRECT_ACTION: createFillAndStroke( rVDev.LogicToPixel( Polygon( static_cast(pCurrAct)->GetRect(), static_cast(pCurrAct)->GetHorzRound(), static_cast(pCurrAct)->GetVertRound() ) ), rCanvas, nCurrActionIndex, rStates ); break; case META_ELLIPSE_ACTION: { const Rectangle& rRect = static_cast(pCurrAct)->GetRect(); createFillAndStroke( rVDev.LogicToPixel( Polygon( rRect.Center(), rRect.GetWidth() >> 1, rRect.GetHeight() >> 1 ) ), rCanvas, nCurrActionIndex, rStates ); break; } case META_ARC_ACTION: createFillAndStroke( rVDev.LogicToPixel( Polygon( static_cast(pCurrAct)->GetRect(), static_cast(pCurrAct)->GetStartPoint(), static_cast(pCurrAct)->GetEndPoint(), POLY_ARC ) ), rCanvas, nCurrActionIndex, rStates ); break; case META_PIE_ACTION: createFillAndStroke( rVDev.LogicToPixel( Polygon( static_cast(pCurrAct)->GetRect(), static_cast(pCurrAct)->GetStartPoint(), static_cast(pCurrAct)->GetEndPoint(), POLY_PIE ) ), rCanvas, nCurrActionIndex, rStates ); break; case META_CHORD_ACTION: createFillAndStroke( rVDev.LogicToPixel( Polygon( static_cast(pCurrAct)->GetRect(), static_cast(pCurrAct)->GetStartPoint(), static_cast(pCurrAct)->GetEndPoint(), POLY_CHORD ) ), rCanvas, nCurrActionIndex, rStates ); break; case META_POLYLINE_ACTION: { maActions.push_back( MtfAction( ActionSharedPtr( new internal::PolyPolyAction( rVDev.LogicToPixel( static_cast(pCurrAct)->GetPolygon() ), rCanvas, getState( rStates ), internal::PolyPolyAction::strokeOnly ) ), nCurrActionIndex ) ); } break; case META_POLYGON_ACTION: createFillAndStroke( rVDev.LogicToPixel( static_cast(pCurrAct)->GetPolygon() ), rCanvas, nCurrActionIndex, rStates ); break; case META_POLYPOLYGON_ACTION: createFillAndStroke( rVDev.LogicToPixel( static_cast(pCurrAct)->GetPolyPolygon() ), rCanvas, nCurrActionIndex, rStates ); break; case META_BMP_ACTION: { MetaBmpAction* pAct = static_cast(pCurrAct); maActions.push_back( MtfAction( ActionSharedPtr( new internal::BitmapAction( pAct->GetBitmap(), rVDev.LogicToPixel( pAct->GetPoint() ), rCanvas, getState( rStates ) ) ), nCurrActionIndex ) ); } break; case META_BMPSCALE_ACTION: { MetaBmpScaleAction* pAct = static_cast(pCurrAct); maActions.push_back( MtfAction( ActionSharedPtr( new internal::BitmapAction( pAct->GetBitmap(), rVDev.LogicToPixel( pAct->GetPoint() ), rVDev.LogicToPixel( pAct->GetSize() ), rCanvas, getState( rStates ) ) ), nCurrActionIndex ) ); } break; case META_BMPSCALEPART_ACTION: { MetaBmpScalePartAction* pAct = static_cast(pCurrAct); maActions.push_back( MtfAction( ActionSharedPtr( new internal::BitmapAction( pAct->GetBitmap(), pAct->GetSrcPoint(), pAct->GetSrcSize(), rVDev.LogicToPixel( pAct->GetDestPoint() ), rVDev.LogicToPixel( pAct->GetDestSize() ), rCanvas, getState( rStates ) ) ), nCurrActionIndex ) ); } break; case META_BMPEX_ACTION: { MetaBmpExAction* pAct = static_cast(pCurrAct); maActions.push_back( MtfAction( ActionSharedPtr( new internal::BitmapAction( pAct->GetBitmapEx(), rVDev.LogicToPixel( pAct->GetPoint() ), rCanvas, getState( rStates ) ) ), nCurrActionIndex ) ); } break; case META_BMPEXSCALE_ACTION: { MetaBmpExScaleAction* pAct = static_cast(pCurrAct); maActions.push_back( MtfAction( ActionSharedPtr( new internal::BitmapAction( pAct->GetBitmapEx(), rVDev.LogicToPixel( pAct->GetPoint() ), rVDev.LogicToPixel( pAct->GetSize() ), rCanvas, getState( rStates ) ) ), nCurrActionIndex ) ); } break; case META_BMPEXSCALEPART_ACTION: { MetaBmpExScalePartAction* pAct = static_cast(pCurrAct); maActions.push_back( MtfAction( ActionSharedPtr( new internal::BitmapAction( pAct->GetBitmapEx(), pAct->GetSrcPoint(), pAct->GetSrcSize(), rVDev.LogicToPixel( pAct->GetDestPoint() ), rVDev.LogicToPixel( pAct->GetDestSize() ), rCanvas, getState( rStates ) ) ), nCurrActionIndex ) ); } break; case META_MASK_ACTION: { MetaMaskAction* pAct = static_cast(pCurrAct); // TODO: masking NYI. Further members: mask color maActions.push_back( MtfAction( ActionSharedPtr( new internal::BitmapAction( pAct->GetBitmap(), rVDev.LogicToPixel( pAct->GetPoint() ), rCanvas, getState( rStates ) ) ), nCurrActionIndex ) ); } break; case META_MASKSCALE_ACTION: { MetaMaskScaleAction* pAct = static_cast(pCurrAct); // TODO: masking NYI. Further members: mask color maActions.push_back( MtfAction( ActionSharedPtr( new internal::BitmapAction( pAct->GetBitmap(), rVDev.LogicToPixel( pAct->GetPoint() ), rVDev.LogicToPixel( pAct->GetSize() ), rCanvas, getState( rStates ) ) ), nCurrActionIndex ) ); } break; case META_MASKSCALEPART_ACTION: { MetaMaskScalePartAction* pAct = static_cast(pCurrAct); // TODO: masking NYI. Further members: mask color maActions.push_back( MtfAction( ActionSharedPtr( new internal::BitmapAction( pAct->GetBitmap(), pAct->GetSrcPoint(), pAct->GetSrcSize(), rVDev.LogicToPixel( pAct->GetDestPoint() ), rVDev.LogicToPixel( pAct->GetDestSize() ), rCanvas, getState( rStates ) ) ), nCurrActionIndex ) ); } break; case META_GRADIENTEX_ACTION: // TODO: use native Canvas gradients here // action is ignored here, because redundant to META_GRADIENT_ACTION break; case META_WALLPAPER_ACTION: // TODO: NYI break; case META_TRANSPARENT_ACTION: { MetaTransparentAction* pAct = static_cast(pCurrAct); maActions.push_back( MtfAction( ActionSharedPtr( new internal::PolyPolyAction( rVDev.LogicToPixel( pAct->GetPolyPolygon() ), rCanvas, getState( rStates ), pAct->GetTransparence() ) ), nCurrActionIndex ) ); } break; case META_FLOATTRANSPARENT_ACTION: // TODO: NYI. This has to be rendered into a separate bitmap canvas break; case META_TEXT_ACTION: { MetaTextAction* pAct = static_cast(pCurrAct); maActions.push_back( MtfAction( ActionSharedPtr( new internal::TextAction( rVDev.LogicToPixel(pAct->GetPoint()), pAct->GetText(), pAct->GetIndex(), pAct->GetLen(), rCanvas, getState( rStates ) ) ), nCurrActionIndex ) ); } break; case META_TEXTARRAY_ACTION: { MetaTextArrayAction* pAct = static_cast(pCurrAct); uno::Sequence< double > offsets( ::comphelper::arrayToSequence( pAct->GetDXArray(), pAct->GetLen() ) ); // convert offsets to physical // TODO: use correct scale direction, text advancement // might be horizontal, vertical, or anything else int i; for( i=0; i( offsets[i] + .5 ), 0 ); offsets[i] = rVDev.LogicToPixel( aSize ).Width(); } maActions.push_back( MtfAction( ActionSharedPtr( new internal::TextAction( rVDev.LogicToPixel(pAct->GetPoint()), pAct->GetText(), pAct->GetIndex(), pAct->GetLen(), offsets, rCanvas, getState( rStates ) ) ), nCurrActionIndex ) ); } break; case META_TEXTRECT_ACTION: case META_STRETCHTEXT_ACTION: case META_TEXTLINE_ACTION: // TODO: NYI DBG_ERROR("META_TEXT* not yet supported"); break; default: break; } } return true; } ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas, const GDIMetaFile& rMtf ) : CanvasGraphicHelper( rCanvas ), maActions() { OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(), "ImplRenderer::ImplRenderer(): Invalid canvas" ); OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(), "ImplRenderer::ImplRenderer(): Invalid graphic device" ); // make sure canvas and graphic device are valid; action // creation don't check that every time if( rCanvas.get() == NULL || !rCanvas->getUNOCanvas().is() || !rCanvas->getUNOCanvas()->getDevice().is() ) { // leave actions empty return; } VectorOfOutDevStates aStateStack; VirtualDevice aVDev; aVDev.EnableOutput( FALSE ); // Setup VDev for state tracking and mapping // ========================================= aVDev.SetMapMode( rMtf.GetPrefMapMode() ); const Size aMtfSizePix( aVDev.LogicToPixel( rMtf.GetPrefSize(), rMtf.GetPrefMapMode() ) ); const Point aEmptyPt; const Point aMtfOriginPix( aVDev.LogicToPixel( aEmptyPt ) ); // skip null-sized output if( aMtfSizePix.Width() != 0 && aMtfSizePix.Height() != 0 ) { // init state stack clearStateStack( aStateStack ); // Setup local state, such that the metafile renders // itself into a one-by-one square for identity view // and render transformations getState( aStateStack ).transform.translate( -aMtfOriginPix.X(), -aMtfOriginPix.Y() ); getState( aStateStack ).transform.scale( 1.0 / aMtfSizePix.Width(), 1.0 / aMtfSizePix.Height() ); createActions( rCanvas, aVDev, const_cast(rMtf), // HACK: // we're // changing // the // current // action // in // createActions! aStateStack ); } } ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas, const BitmapEx& rBmpEx ) : CanvasGraphicHelper( rCanvas ), maActions() { OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(), "ImplRenderer::ImplRenderer(): Invalid canvas" ); OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(), "ImplRenderer::ImplRenderer(): Invalid graphic device" ); // make sure canvas and graphic device are valid; action // creation don't check that every time if( rCanvas.get() == NULL || !rCanvas->getUNOCanvas().is() || !rCanvas->getUNOCanvas()->getDevice().is() ) { // leave actions empty return; } OutDevState aState; const Size aBmpSize( rBmpEx.GetSizePixel() ); // Setup local state, such that the bitmap renders itself // into a one-by-one square for identity view and render // transformations aState.transform.scale( 1.0 / aBmpSize.Width(), 1.0 / aBmpSize.Height() ); // create a single action for the provided BitmapEx maActions.push_back( MtfAction( ActionSharedPtr( new BitmapAction(rBmpEx, Point(), rCanvas, aState) ), 0 ) ); } ImplRenderer::~ImplRenderer() { } namespace { class ActionRenderer { public: ActionRenderer() : mbRet( true ) { } bool result() { return mbRet; } void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction ) { // ANDing the result. We want to fail if at least // one action failed. mbRet &= rAction.mpAction->render(); } private: bool mbRet; }; } bool ImplRenderer::drawSubset( int startIndex, int endIndex ) const { OSL_ENSURE( startIndex<=endIndex, "ImplRenderer::draw() invalid action range" ); // find start and end action ActionVector::const_iterator aIterBegin( ::std::lower_bound( maActions.begin(), maActions.end(), MtfAction( ActionSharedPtr(), startIndex ), ActionIndexComparator() ) ); ActionVector::const_iterator aIterEnd( ::std::lower_bound( maActions.begin(), maActions.end(), MtfAction( ActionSharedPtr(), endIndex ), ActionIndexComparator() ) ); // render subset of actions return ::std::for_each( aIterBegin, aIterEnd, ActionRenderer() ).result(); } bool ImplRenderer::draw() const { return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer() ).result(); } } }