1227 lines
55 KiB
C++

/*************************************************************************
*
* $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 <osl/mutex.hxx>
#endif
#ifndef _VOS_MUTEX_HXX_
#include <vos/mutex.hxx>
#endif
#ifndef _SV_SVAPP_HXX
#include <vcl/svapp.hxx>
#endif
#ifndef _COMPHELPER_SEQUENCE_HXX_
#include <comphelper/sequence.hxx>
#endif
#include <cppcanvas/canvas.hxx>
#ifndef _VCL_CANVASTOOLS_HXX
#include <vcl/canvastools.hxx>
#endif
#ifndef _BGFX_TOOLS_CANVASTOOLS_HXX
#include <basegfx/tools/canvastools.hxx>
#endif
#ifndef _CANVAS_CANVASTOOLS_HXX
#include <canvas/canvastools.hxx>
#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 <vector>
#include <algorithm>
#ifndef _COM_SUN_STAR_UNO_SEQUENCE_HXX_
#include <com/sun/star/uno/Sequence.hxx>
#endif
#ifndef _DRAFTS_COM_SUN_STAR_GEOMETRY_REALPOINT2D_HPP__
#include <drafts/com/sun/star/geometry/RealPoint2D.hpp>
#endif
#ifndef _DRAFTS_COM_SUN_STAR_RENDERING_VIEWSTATE_HPP__
#include <drafts/com/sun/star/rendering/ViewState.hpp>
#endif
#ifndef _DRAFTS_COM_SUN_STAR_RENDERING_RENDERSTATE_HPP__
#include <drafts/com/sun/star/rendering/RenderState.hpp>
#endif
#ifndef _DRAFTS_COM_SUN_STAR_RENDERING_XCANVASFONT_HPP__
#include <drafts/com/sun/star/rendering/XCanvasFont.hpp>
#endif
#ifndef _DRAFTS_COM_SUN_STAR_RENDERING_XPOLYPOLYGON2D_HPP__
#include <drafts/com/sun/star/rendering/XPolyPolygon2D.hpp>
#endif
#ifndef _DRAFTS_COM_SUN_STAR_RENDERING_XCANVAS_HPP__
#include <drafts/com/sun/star/rendering/XCanvas.hpp>
#endif
#ifndef _BGFX_MATRIX_B2DHOMMATRIX_HXX
#include <basegfx/matrix/b2dhommatrix.hxx>
#endif
#ifndef _SV_GDIMTF_HXX
#include <vcl/gdimtf.hxx>
#endif
#ifndef _SV_METAACT_HXX
#include <vcl/metaact.hxx>
#endif
#ifndef _SV_VIRDEV_HXX
#include <vcl/virdev.hxx>
#endif
#ifndef _TL_POLY_HXX
#include <tools/poly.hxx>
#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<MetaCommentAction*>(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<MetaLineColorAction*>(pCurrAct),
getState( rStates ).isLineColorSet,
getState( rStates ).lineColor,
rCanvas );
break;
case META_FILLCOLOR_ACTION:
setStateColor( static_cast<MetaFillColorAction*>(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<MetaTextColorAction*>(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<MetaTextFillColorAction*>(pCurrAct),
getState( rStates ).isTextFillColorSet,
getState( rStates ).textFillColor,
rCanvas );
break;
case META_TEXTLINECOLOR_ACTION:
setStateColor( static_cast<MetaTextLineColorAction*>(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<MetaFontAction*>(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<MetaLayoutModeAction*>(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<MetaGradientAction*>(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<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(),
static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(),
aTmpMtf );
createActions( rCanvas, rVDev, aTmpMtf, rStates );
}
break;
case META_EPS_ACTION:
{
MetaEPSAction* pAct = static_cast<MetaEPSAction*>(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<GDIMetaFile&>(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<MetaCommentAction*>(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<MetaGradientExAction*>(pCurrAct);
break;
// skip broken-down rendering, output gradient when sequence is ended
case META_COMMENT_ACTION:
if( static_cast<MetaCommentAction*>(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<MetaPointAction*>(pCurrAct)->GetPoint() ),
rCanvas,
getState( rStates ) ) ),
nCurrActionIndex ) );
}
break;
case META_PIXEL_ACTION:
{
maActions.push_back(
MtfAction(
ActionSharedPtr(
new internal::PointAction(
rVDev.LogicToPixel(
static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ),
rCanvas,
getState( rStates ),
static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) ),
nCurrActionIndex ) );
}
break;
case META_LINE_ACTION:
{
maActions.push_back(
MtfAction(
ActionSharedPtr(
new internal::LineAction(
rVDev.LogicToPixel( static_cast<MetaLineAction*>(pCurrAct)->GetStartPoint() ),
rVDev.LogicToPixel( static_cast<MetaLineAction*>(pCurrAct)->GetEndPoint() ),
rCanvas,
getState( rStates ) ) ),
nCurrActionIndex ) );
}
break;
case META_RECT_ACTION:
createFillAndStroke( ::PolyPolygon( ::Polygon( rVDev.LogicToPixel( static_cast<MetaRectAction*>(pCurrAct)->GetRect() ) ) ),
rCanvas, nCurrActionIndex,
rStates );
break;
case META_ROUNDRECT_ACTION:
createFillAndStroke( rVDev.LogicToPixel( Polygon( static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect(),
static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound(),
static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() ) ),
rCanvas, nCurrActionIndex,
rStates );
break;
case META_ELLIPSE_ACTION:
{
const Rectangle& rRect = static_cast<MetaEllipseAction*>(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<MetaArcAction*>(pCurrAct)->GetRect(),
static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(),
static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC ) ),
rCanvas, nCurrActionIndex,
rStates );
break;
case META_PIE_ACTION:
createFillAndStroke( rVDev.LogicToPixel( Polygon( static_cast<MetaPieAction*>(pCurrAct)->GetRect(),
static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(),
static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE ) ),
rCanvas, nCurrActionIndex,
rStates );
break;
case META_CHORD_ACTION:
createFillAndStroke( rVDev.LogicToPixel( Polygon( static_cast<MetaChordAction*>(pCurrAct)->GetRect(),
static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(),
static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD ) ),
rCanvas, nCurrActionIndex,
rStates );
break;
case META_POLYLINE_ACTION:
{
maActions.push_back(
MtfAction(
ActionSharedPtr(
new internal::PolyPolyAction(
rVDev.LogicToPixel( static_cast<MetaPolyLineAction*>(pCurrAct)->GetPolygon() ),
rCanvas,
getState( rStates ),
internal::PolyPolyAction::strokeOnly ) ),
nCurrActionIndex ) );
}
break;
case META_POLYGON_ACTION:
createFillAndStroke( rVDev.LogicToPixel( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon() ),
rCanvas, nCurrActionIndex,
rStates );
break;
case META_POLYPOLYGON_ACTION:
createFillAndStroke( rVDev.LogicToPixel( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon() ),
rCanvas, nCurrActionIndex,
rStates );
break;
case META_BMP_ACTION:
{
MetaBmpAction* pAct = static_cast<MetaBmpAction*>(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<MetaBmpScaleAction*>(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<MetaBmpScalePartAction*>(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<MetaBmpExAction*>(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<MetaBmpExScaleAction*>(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<MetaBmpExScalePartAction*>(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<MetaMaskAction*>(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<MetaMaskScaleAction*>(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<MetaMaskScalePartAction*>(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<MetaTransparentAction*>(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<MetaTextAction*>(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<MetaTextArrayAction*>(pCurrAct);
uno::Sequence< double > offsets( ::comphelper::arrayToSequence<long int, double>( 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.getLength(); ++i )
{
const Size aSize( static_cast<long>( 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<GDIMetaFile&>(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();
}
}
}