Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : :
30 : : #include <canvas/debug.hxx>
31 : : #include <tools/diagnose_ex.h>
32 : :
33 : : #include <rtl/math.hxx>
34 : :
35 : : #include <com/sun/star/rendering/CompositeOperation.hpp>
36 : : #include <com/sun/star/util/Endianness.hpp>
37 : : #include <com/sun/star/rendering/TextDirection.hpp>
38 : : #include <com/sun/star/rendering/TexturingMode.hpp>
39 : : #include <com/sun/star/rendering/PathCapType.hpp>
40 : : #include <com/sun/star/rendering/PathJoinType.hpp>
41 : :
42 : : #include <tools/poly.hxx>
43 : : #include <vcl/window.hxx>
44 : : #include <vcl/bitmapex.hxx>
45 : : #include <vcl/bmpacc.hxx>
46 : : #include <vcl/canvastools.hxx>
47 : :
48 : : #include <basegfx/matrix/b2dhommatrix.hxx>
49 : : #include <basegfx/range/b2drectangle.hxx>
50 : : #include <basegfx/point/b2dpoint.hxx>
51 : : #include <basegfx/vector/b2dsize.hxx>
52 : : #include <basegfx/polygon/b2dpolygon.hxx>
53 : : #include <basegfx/polygon/b2dpolygontools.hxx>
54 : : #include <basegfx/polygon/b2dpolypolygontools.hxx>
55 : : #include <basegfx/polygon/b2dlinegeometry.hxx>
56 : : #include <basegfx/tools/canvastools.hxx>
57 : : #include <basegfx/numeric/ftools.hxx>
58 : :
59 : : #include <utility>
60 : :
61 : : #include <comphelper/sequence.hxx>
62 : : #include <canvas/canvastools.hxx>
63 : :
64 : : #include "textlayout.hxx"
65 : : #include "canvashelper.hxx"
66 : : #include "canvasbitmap.hxx"
67 : : #include "impltools.hxx"
68 : : #include "canvasfont.hxx"
69 : :
70 : :
71 : : using namespace ::com::sun::star;
72 : :
73 : : namespace vclcanvas
74 : : {
75 : : namespace
76 : : {
77 : 0 : basegfx::B2DLineJoin b2DJoineFromJoin( sal_Int8 nJoinType )
78 : : {
79 : 0 : switch( nJoinType )
80 : : {
81 : : case rendering::PathJoinType::NONE:
82 : 0 : return basegfx::B2DLINEJOIN_NONE;
83 : :
84 : : case rendering::PathJoinType::MITER:
85 : 0 : return basegfx::B2DLINEJOIN_MITER;
86 : :
87 : : case rendering::PathJoinType::ROUND:
88 : 0 : return basegfx::B2DLINEJOIN_ROUND;
89 : :
90 : : case rendering::PathJoinType::BEVEL:
91 : 0 : return basegfx::B2DLINEJOIN_BEVEL;
92 : :
93 : : default:
94 : 0 : ENSURE_OR_THROW( false,
95 : : "b2DJoineFromJoin(): Unexpected join type" );
96 : : }
97 : :
98 : : return basegfx::B2DLINEJOIN_NONE;
99 : : }
100 : : }
101 : :
102 : 0 : CanvasHelper::CanvasHelper() :
103 : : mpDevice(),
104 : : mpProtectedOutDev(),
105 : : mpOutDev(),
106 : : mp2ndOutDev(),
107 : 0 : mbHaveAlpha( false )
108 : : {
109 : 0 : }
110 : :
111 : 0 : void CanvasHelper::disposing()
112 : : {
113 : 0 : mpDevice = NULL;
114 : 0 : mpProtectedOutDev.reset();
115 : 0 : mpOutDev.reset();
116 : 0 : mp2ndOutDev.reset();
117 : 0 : }
118 : :
119 : 0 : void CanvasHelper::init( rendering::XGraphicDevice& rDevice,
120 : : const OutDevProviderSharedPtr& rOutDev,
121 : : bool bProtect,
122 : : bool bHaveAlpha )
123 : : {
124 : : // cast away const, need to change refcount (as this is
125 : : // ~invisible to client code, still logically const)
126 : 0 : mpDevice = &rDevice;
127 : 0 : mbHaveAlpha = bHaveAlpha;
128 : :
129 : 0 : setOutDev( rOutDev, bProtect );
130 : 0 : }
131 : :
132 : 0 : void CanvasHelper::setOutDev( const OutDevProviderSharedPtr& rOutDev,
133 : : bool bProtect )
134 : : {
135 : 0 : if( bProtect )
136 : 0 : mpProtectedOutDev = rOutDev;
137 : : else
138 : 0 : mpProtectedOutDev.reset();
139 : :
140 : 0 : mpOutDev = rOutDev;
141 : 0 : }
142 : :
143 : 0 : void CanvasHelper::setBackgroundOutDev( const OutDevProviderSharedPtr& rOutDev )
144 : : {
145 : 0 : mp2ndOutDev = rOutDev;
146 : 0 : mp2ndOutDev->getOutDev().EnableMapMode( sal_False );
147 : 0 : }
148 : :
149 : 0 : void CanvasHelper::clear()
150 : : {
151 : : // are we disposed?
152 : 0 : if( mpOutDev )
153 : : {
154 : 0 : OutputDevice& rOutDev( mpOutDev->getOutDev() );
155 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
156 : :
157 : 0 : rOutDev.EnableMapMode( sal_False );
158 : 0 : rOutDev.SetLineColor( COL_WHITE );
159 : 0 : rOutDev.SetFillColor( COL_WHITE );
160 : 0 : rOutDev.SetClipRegion();
161 : : rOutDev.DrawRect( Rectangle( Point(),
162 : 0 : rOutDev.GetOutputSizePixel()) );
163 : :
164 : 0 : if( mp2ndOutDev )
165 : : {
166 : 0 : OutputDevice& rOutDev2( mp2ndOutDev->getOutDev() );
167 : :
168 : 0 : rOutDev2.SetDrawMode( DRAWMODE_DEFAULT );
169 : 0 : rOutDev2.EnableMapMode( sal_False );
170 : 0 : rOutDev2.SetLineColor( COL_WHITE );
171 : 0 : rOutDev2.SetFillColor( COL_WHITE );
172 : 0 : rOutDev2.SetClipRegion();
173 : : rOutDev2.DrawRect( Rectangle( Point(),
174 : 0 : rOutDev2.GetOutputSizePixel()) );
175 : : rOutDev2.SetDrawMode( DRAWMODE_BLACKLINE | DRAWMODE_BLACKFILL | DRAWMODE_BLACKTEXT |
176 : 0 : DRAWMODE_BLACKGRADIENT | DRAWMODE_BLACKBITMAP );
177 : 0 : }
178 : : }
179 : 0 : }
180 : :
181 : 0 : void CanvasHelper::drawPoint( const rendering::XCanvas* ,
182 : : const geometry::RealPoint2D& aPoint,
183 : : const rendering::ViewState& viewState,
184 : : const rendering::RenderState& renderState )
185 : : {
186 : : // are we disposed?
187 : 0 : if( mpOutDev )
188 : : {
189 : : // nope, render
190 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
191 : 0 : setupOutDevState( viewState, renderState, LINE_COLOR );
192 : :
193 : : const Point aOutPoint( tools::mapRealPoint2D( aPoint,
194 : 0 : viewState, renderState ) );
195 : : // TODO(F1): alpha
196 : 0 : mpOutDev->getOutDev().DrawPixel( aOutPoint );
197 : :
198 : 0 : if( mp2ndOutDev )
199 : 0 : mp2ndOutDev->getOutDev().DrawPixel( aOutPoint );
200 : : }
201 : 0 : }
202 : :
203 : 0 : void CanvasHelper::drawLine( const rendering::XCanvas* ,
204 : : const geometry::RealPoint2D& aStartRealPoint2D,
205 : : const geometry::RealPoint2D& aEndRealPoint2D,
206 : : const rendering::ViewState& viewState,
207 : : const rendering::RenderState& renderState )
208 : : {
209 : : // are we disposed?
210 : 0 : if( mpOutDev )
211 : : {
212 : : // nope, render
213 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
214 : 0 : setupOutDevState( viewState, renderState, LINE_COLOR );
215 : :
216 : : const Point aStartPoint( tools::mapRealPoint2D( aStartRealPoint2D,
217 : 0 : viewState, renderState ) );
218 : : const Point aEndPoint( tools::mapRealPoint2D( aEndRealPoint2D,
219 : 0 : viewState, renderState ) );
220 : : // TODO(F2): alpha
221 : 0 : mpOutDev->getOutDev().DrawLine( aStartPoint, aEndPoint );
222 : :
223 : 0 : if( mp2ndOutDev )
224 : 0 : mp2ndOutDev->getOutDev().DrawLine( aStartPoint, aEndPoint );
225 : : }
226 : 0 : }
227 : :
228 : 0 : void CanvasHelper::drawBezier( const rendering::XCanvas* ,
229 : : const geometry::RealBezierSegment2D& aBezierSegment,
230 : : const geometry::RealPoint2D& _aEndPoint,
231 : : const rendering::ViewState& viewState,
232 : : const rendering::RenderState& renderState )
233 : : {
234 : 0 : if( mpOutDev )
235 : : {
236 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
237 : 0 : setupOutDevState( viewState, renderState, LINE_COLOR );
238 : :
239 : : const Point& rStartPoint( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.Px,
240 : : aBezierSegment.Py),
241 : 0 : viewState, renderState ) );
242 : : const Point& rCtrlPoint1( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.C1x,
243 : : aBezierSegment.C1y),
244 : 0 : viewState, renderState ) );
245 : : const Point& rCtrlPoint2( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.C2x,
246 : : aBezierSegment.C2y),
247 : 0 : viewState, renderState ) );
248 : : const Point& rEndPoint( tools::mapRealPoint2D( _aEndPoint,
249 : 0 : viewState, renderState ) );
250 : :
251 : 0 : ::Polygon aPoly(4);
252 : 0 : aPoly.SetPoint( rStartPoint, 0 );
253 : 0 : aPoly.SetFlags( 0, POLY_NORMAL );
254 : 0 : aPoly.SetPoint( rCtrlPoint1, 1 );
255 : 0 : aPoly.SetFlags( 1, POLY_CONTROL );
256 : 0 : aPoly.SetPoint( rCtrlPoint2, 2 );
257 : 0 : aPoly.SetFlags( 2, POLY_CONTROL );
258 : 0 : aPoly.SetPoint( rEndPoint, 3 );
259 : 0 : aPoly.SetFlags( 3, POLY_NORMAL );
260 : :
261 : : // TODO(F2): alpha
262 : 0 : mpOutDev->getOutDev().DrawPolygon( aPoly );
263 : 0 : if( mp2ndOutDev )
264 : 0 : mp2ndOutDev->getOutDev().DrawPolygon( aPoly );
265 : : }
266 : 0 : }
267 : :
268 : 0 : uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* ,
269 : : const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
270 : : const rendering::ViewState& viewState,
271 : : const rendering::RenderState& renderState )
272 : : {
273 : 0 : ENSURE_ARG_OR_THROW( xPolyPolygon.is(),
274 : : "polygon is NULL");
275 : :
276 : 0 : if( mpOutDev )
277 : : {
278 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
279 : 0 : setupOutDevState( viewState, renderState, LINE_COLOR );
280 : :
281 : : const ::basegfx::B2DPolyPolygon& rPolyPoly(
282 : 0 : ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) );
283 : 0 : const PolyPolygon aPolyPoly( tools::mapPolyPolygon( rPolyPoly, viewState, renderState ) );
284 : :
285 : 0 : if( rPolyPoly.isClosed() )
286 : : {
287 : 0 : mpOutDev->getOutDev().DrawPolyPolygon( aPolyPoly );
288 : :
289 : 0 : if( mp2ndOutDev )
290 : 0 : mp2ndOutDev->getOutDev().DrawPolyPolygon( aPolyPoly );
291 : : }
292 : : else
293 : : {
294 : : // mixed open/closed state. Cannot render open polygon
295 : : // via DrawPolyPolygon(), since that implicitley
296 : : // closed every polygon. OTOH, no need to distinguish
297 : : // further and render closed polygons via
298 : : // DrawPolygon(), and open ones via DrawPolyLine():
299 : : // closed polygons will simply already contain the
300 : : // closing segment.
301 : 0 : sal_uInt16 nSize( aPolyPoly.Count() );
302 : :
303 : 0 : for( sal_uInt16 i=0; i<nSize; ++i )
304 : : {
305 : 0 : mpOutDev->getOutDev().DrawPolyLine( aPolyPoly[i] );
306 : :
307 : 0 : if( mp2ndOutDev )
308 : 0 : mp2ndOutDev->getOutDev().DrawPolyLine( aPolyPoly[i] );
309 : : }
310 : 0 : }
311 : : }
312 : :
313 : : // TODO(P1): Provide caching here.
314 : 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
315 : : }
316 : :
317 : 0 : uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* ,
318 : : const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
319 : : const rendering::ViewState& viewState,
320 : : const rendering::RenderState& renderState,
321 : : const rendering::StrokeAttributes& strokeAttributes )
322 : : {
323 : 0 : ENSURE_ARG_OR_THROW( xPolyPolygon.is(),
324 : : "polygon is NULL");
325 : :
326 : 0 : if( mpOutDev )
327 : : {
328 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
329 : :
330 : 0 : ::basegfx::B2DHomMatrix aMatrix;
331 : 0 : ::canvas::tools::mergeViewAndRenderTransform(aMatrix, viewState, renderState);
332 : :
333 : : ::basegfx::B2DSize aLinePixelSize(strokeAttributes.StrokeWidth,
334 : 0 : strokeAttributes.StrokeWidth);
335 : 0 : aLinePixelSize *= aMatrix;
336 : :
337 : : ::basegfx::B2DPolyPolygon aPolyPoly(
338 : 0 : ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) );
339 : :
340 : 0 : if( aPolyPoly.areControlPointsUsed() )
341 : : {
342 : : // AW: Not needed for ApplyLineDashing anymore; should be removed
343 : 0 : aPolyPoly = ::basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly);
344 : : }
345 : :
346 : : // apply dashing, if any
347 : 0 : if( strokeAttributes.DashArray.getLength() )
348 : : {
349 : : const ::std::vector<double>& aDashArray(
350 : 0 : ::comphelper::sequenceToContainer< ::std::vector<double> >(strokeAttributes.DashArray) );
351 : :
352 : 0 : ::basegfx::B2DPolyPolygon aDashedPolyPoly;
353 : :
354 : 0 : for( sal_uInt32 i=0; i<aPolyPoly.count(); ++i )
355 : : {
356 : : // AW: new interface; You may also get gaps in the same run now
357 : 0 : basegfx::tools::applyLineDashing(aPolyPoly.getB2DPolygon(i), aDashArray, &aDashedPolyPoly);
358 : : //aDashedPolyPoly.append(
359 : : // ::basegfx::tools::applyLineDashing( aPolyPoly.getB2DPolygon(i),
360 : : // aDashArray ) );
361 : : }
362 : :
363 : 0 : aPolyPoly = aDashedPolyPoly;
364 : : }
365 : :
366 : 0 : ::basegfx::B2DPolyPolygon aStrokedPolyPoly;
367 : 0 : if( aLinePixelSize.getLength() < 1.42 )
368 : : {
369 : : // line width < 1.0 in device pixel, thus, output as a
370 : : // simple hairline poly-polygon
371 : 0 : setupOutDevState( viewState, renderState, LINE_COLOR );
372 : :
373 : 0 : aStrokedPolyPoly = aPolyPoly;
374 : : }
375 : : else
376 : : {
377 : : // render as a 'thick' line
378 : 0 : setupOutDevState( viewState, renderState, FILL_COLOR );
379 : :
380 : 0 : for( sal_uInt32 i=0; i<aPolyPoly.count(); ++i )
381 : : {
382 : : // TODO(F2): Use MiterLimit from StrokeAttributes,
383 : : // need to convert it here to angle.
384 : :
385 : : // TODO(F2): Also use Cap settings from
386 : : // StrokeAttributes, the
387 : : // createAreaGeometryForLineStartEnd() method does not
388 : : // seem to fit very well here
389 : :
390 : : // AW: New interface, will create bezier polygons now
391 : : aStrokedPolyPoly.append(basegfx::tools::createAreaGeometry(
392 : 0 : aPolyPoly.getB2DPolygon(i), strokeAttributes.StrokeWidth*0.5, b2DJoineFromJoin(strokeAttributes.JoinType)));
393 : : //aStrokedPolyPoly.append(
394 : : // ::basegfx::tools::createAreaGeometryForPolygon( aPolyPoly.getB2DPolygon(i),
395 : : // strokeAttributes.StrokeWidth*0.5,
396 : : // b2DJoineFromJoin(strokeAttributes.JoinType) ) );
397 : : }
398 : : }
399 : :
400 : : // transform only _now_, all the StrokeAttributes are in
401 : : // user coordinates.
402 : 0 : aStrokedPolyPoly.transform( aMatrix );
403 : :
404 : 0 : const PolyPolygon aVCLPolyPoly( aStrokedPolyPoly );
405 : :
406 : : // TODO(F2): When using alpha here, must handle that via
407 : : // temporary surface or somesuch.
408 : :
409 : : // Note: the generated stroke poly-polygon is NOT free of
410 : : // self-intersections. Therefore, if we would render it
411 : : // via OutDev::DrawPolyPolygon(), on/off fill would
412 : : // generate off areas on those self-intersections.
413 : 0 : sal_uInt16 nSize( aVCLPolyPoly.Count() );
414 : :
415 : 0 : for( sal_uInt16 i=0; i<nSize; ++i )
416 : : {
417 : 0 : if( aStrokedPolyPoly.getB2DPolygon( i ).isClosed() ) {
418 : 0 : mpOutDev->getOutDev().DrawPolygon( aVCLPolyPoly[i] );
419 : 0 : if( mp2ndOutDev )
420 : 0 : mp2ndOutDev->getOutDev().DrawPolygon( aVCLPolyPoly[i] );
421 : : } else {
422 : 0 : const sal_uInt16 nPolySize = aVCLPolyPoly[i].GetSize();
423 : 0 : if( nPolySize ) {
424 : 0 : Point rPrevPoint = aVCLPolyPoly[i].GetPoint( 0 );
425 : 0 : Point rPoint;
426 : :
427 : 0 : for( sal_uInt16 j=1; j<nPolySize; j++ ) {
428 : 0 : rPoint = aVCLPolyPoly[i].GetPoint( j );
429 : 0 : mpOutDev->getOutDev().DrawLine( rPrevPoint, rPoint );
430 : 0 : if( mp2ndOutDev )
431 : 0 : mp2ndOutDev->getOutDev().DrawLine( rPrevPoint, rPoint );
432 : 0 : rPrevPoint = rPoint;
433 : : }
434 : : }
435 : : }
436 : 0 : }
437 : : }
438 : :
439 : : // TODO(P1): Provide caching here.
440 : 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
441 : : }
442 : :
443 : 0 : uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* ,
444 : : const uno::Reference< rendering::XPolyPolygon2D >& ,
445 : : const rendering::ViewState& ,
446 : : const rendering::RenderState& ,
447 : : const uno::Sequence< rendering::Texture >& ,
448 : : const rendering::StrokeAttributes& )
449 : : {
450 : 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
451 : : }
452 : :
453 : 0 : uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* ,
454 : : const uno::Reference< rendering::XPolyPolygon2D >& ,
455 : : const rendering::ViewState& ,
456 : : const rendering::RenderState& ,
457 : : const uno::Sequence< rendering::Texture >& ,
458 : : const uno::Reference< geometry::XMapping2D >& ,
459 : : const rendering::StrokeAttributes& )
460 : : {
461 : 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
462 : : }
463 : :
464 : 0 : uno::Reference< rendering::XPolyPolygon2D > CanvasHelper::queryStrokeShapes( const rendering::XCanvas* ,
465 : : const uno::Reference< rendering::XPolyPolygon2D >& ,
466 : : const rendering::ViewState& ,
467 : : const rendering::RenderState& ,
468 : : const rendering::StrokeAttributes& )
469 : : {
470 : 0 : return uno::Reference< rendering::XPolyPolygon2D >(NULL);
471 : : }
472 : :
473 : 0 : uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* ,
474 : : const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
475 : : const rendering::ViewState& viewState,
476 : : const rendering::RenderState& renderState )
477 : : {
478 : 0 : ENSURE_ARG_OR_THROW( xPolyPolygon.is(),
479 : : "polygon is NULL");
480 : :
481 : 0 : if( mpOutDev )
482 : : {
483 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
484 : :
485 : 0 : const int nTransparency( setupOutDevState( viewState, renderState, FILL_COLOR ) );
486 : : ::basegfx::B2DPolyPolygon aB2DPolyPoly(
487 : 0 : ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon));
488 : 0 : aB2DPolyPoly.setClosed(true); // ensure closed poly, otherwise VCL does not fill
489 : : const PolyPolygon aPolyPoly( tools::mapPolyPolygon(
490 : : aB2DPolyPoly,
491 : 0 : viewState, renderState ) );
492 : 0 : const bool bSourceAlpha( renderState.CompositeOperation == rendering::CompositeOperation::SOURCE );
493 : 0 : if( !nTransparency || bSourceAlpha )
494 : : {
495 : 0 : mpOutDev->getOutDev().DrawPolyPolygon( aPolyPoly );
496 : : }
497 : : else
498 : : {
499 : 0 : const int nTransPercent( (nTransparency * 100 + 128) / 255 ); // normal rounding, no truncation here
500 : 0 : mpOutDev->getOutDev().DrawTransparent( aPolyPoly, (sal_uInt16)nTransPercent );
501 : : }
502 : :
503 : 0 : if( mp2ndOutDev )
504 : : {
505 : : // HACK. Normally, CanvasHelper does not care about
506 : : // actually what mp2ndOutDev is... well, here we do &
507 : : // assume a 1bpp target - everything beyond 97%
508 : : // transparency is fully transparent
509 : 0 : if( nTransparency < 253 )
510 : : {
511 : 0 : mp2ndOutDev->getOutDev().SetFillColor( COL_BLACK );
512 : 0 : mp2ndOutDev->getOutDev().DrawPolyPolygon( aPolyPoly );
513 : : }
514 : 0 : }
515 : : }
516 : :
517 : : // TODO(P1): Provide caching here.
518 : 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
519 : : }
520 : :
521 : 0 : uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* ,
522 : : const uno::Reference< rendering::XPolyPolygon2D >& ,
523 : : const rendering::ViewState& ,
524 : : const rendering::RenderState& ,
525 : : const uno::Sequence< rendering::Texture >& ,
526 : : const uno::Reference< geometry::XMapping2D >& )
527 : : {
528 : 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
529 : : }
530 : :
531 : 0 : uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas* ,
532 : : const rendering::FontRequest& fontRequest,
533 : : const uno::Sequence< beans::PropertyValue >& extraFontProperties,
534 : : const geometry::Matrix2D& fontMatrix )
535 : : {
536 : 0 : if( mpOutDev && mpDevice )
537 : : {
538 : : // TODO(F2): font properties and font matrix
539 : : return uno::Reference< rendering::XCanvasFont >(
540 : : new CanvasFont(fontRequest, extraFontProperties, fontMatrix,
541 : 0 : *mpDevice, mpOutDev) );
542 : : }
543 : :
544 : 0 : return uno::Reference< rendering::XCanvasFont >();
545 : : }
546 : :
547 : 0 : uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* ,
548 : : const rendering::FontInfo& ,
549 : : const uno::Sequence< beans::PropertyValue >& )
550 : : {
551 : : // TODO(F2)
552 : 0 : return uno::Sequence< rendering::FontInfo >();
553 : : }
554 : :
555 : 0 : uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* ,
556 : : const rendering::StringContext& text,
557 : : const uno::Reference< rendering::XCanvasFont >& xFont,
558 : : const rendering::ViewState& viewState,
559 : : const rendering::RenderState& renderState,
560 : : sal_Int8 textDirection )
561 : : {
562 : 0 : ENSURE_ARG_OR_THROW( xFont.is(),
563 : : "font is NULL");
564 : :
565 : 0 : if( mpOutDev )
566 : : {
567 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
568 : :
569 : 0 : ::Point aOutpos;
570 : 0 : if( !setupTextOutput( aOutpos, viewState, renderState, xFont ) )
571 : 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL); // no output necessary
572 : :
573 : : // change text direction and layout mode
574 : 0 : sal_uIntPtr nLayoutMode(0);
575 : 0 : switch( textDirection )
576 : : {
577 : : case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
578 : 0 : nLayoutMode |= TEXT_LAYOUT_BIDI_LTR;
579 : : // FALLTHROUGH intended
580 : : case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
581 : 0 : nLayoutMode |= TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG;
582 : 0 : nLayoutMode |= TEXT_LAYOUT_TEXTORIGIN_LEFT;
583 : 0 : break;
584 : :
585 : : case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
586 : 0 : nLayoutMode |= TEXT_LAYOUT_BIDI_RTL;
587 : : // FALLTHROUGH intended
588 : : case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
589 : 0 : nLayoutMode |= TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG;
590 : 0 : nLayoutMode |= TEXT_LAYOUT_TEXTORIGIN_RIGHT;
591 : 0 : break;
592 : : }
593 : :
594 : : // TODO(F2): alpha
595 : 0 : mpOutDev->getOutDev().SetLayoutMode( nLayoutMode );
596 : 0 : mpOutDev->getOutDev().DrawText( aOutpos,
597 : : text.Text,
598 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(text.StartPosition),
599 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(text.Length) );
600 : :
601 : 0 : if( mp2ndOutDev )
602 : : {
603 : 0 : mp2ndOutDev->getOutDev().SetLayoutMode( nLayoutMode );
604 : 0 : mp2ndOutDev->getOutDev().DrawText( aOutpos,
605 : : text.Text,
606 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(text.StartPosition),
607 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(text.Length) );
608 : 0 : }
609 : : }
610 : :
611 : 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
612 : : }
613 : :
614 : 0 : uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* ,
615 : : const uno::Reference< rendering::XTextLayout >& xLayoutedText,
616 : : const rendering::ViewState& viewState,
617 : : const rendering::RenderState& renderState )
618 : : {
619 : 0 : ENSURE_ARG_OR_THROW( xLayoutedText.is(),
620 : : "layout is NULL");
621 : :
622 : 0 : TextLayout* pTextLayout = dynamic_cast< TextLayout* >( xLayoutedText.get() );
623 : :
624 : 0 : if( pTextLayout )
625 : : {
626 : 0 : if( mpOutDev )
627 : : {
628 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
629 : :
630 : : // TODO(T3): Race condition. We're taking the font
631 : : // from xLayoutedText, and then calling draw() at it,
632 : : // without exclusive access. Move setupTextOutput(),
633 : : // e.g. to impltools?
634 : :
635 : 0 : ::Point aOutpos;
636 : 0 : if( !setupTextOutput( aOutpos, viewState, renderState, xLayoutedText->getFont() ) )
637 : 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL); // no output necessary
638 : :
639 : : // TODO(F2): What about the offset scalings?
640 : : // TODO(F2): alpha
641 : 0 : pTextLayout->draw( mpOutDev->getOutDev(), aOutpos, viewState, renderState );
642 : :
643 : 0 : if( mp2ndOutDev )
644 : 0 : pTextLayout->draw( mp2ndOutDev->getOutDev(), aOutpos, viewState, renderState );
645 : : }
646 : : }
647 : : else
648 : : {
649 : 0 : ENSURE_ARG_OR_THROW( false,
650 : : "TextLayout not compatible with this canvas" );
651 : : }
652 : :
653 : 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
654 : : }
655 : :
656 : 0 : uno::Reference< rendering::XCachedPrimitive > CanvasHelper::implDrawBitmap( const rendering::XCanvas* pCanvas,
657 : : const uno::Reference< rendering::XBitmap >& xBitmap,
658 : : const rendering::ViewState& viewState,
659 : : const rendering::RenderState& renderState,
660 : : bool bModulateColors )
661 : : {
662 : 0 : ENSURE_ARG_OR_THROW( xBitmap.is(),
663 : : "bitmap is NULL");
664 : :
665 : : ::canvas::tools::verifyInput( renderState,
666 : : BOOST_CURRENT_FUNCTION,
667 : : mpDevice,
668 : : 4,
669 : 0 : bModulateColors ? 3 : 0 );
670 : :
671 : 0 : if( mpOutDev )
672 : : {
673 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
674 : 0 : setupOutDevState( viewState, renderState, IGNORE_COLOR );
675 : :
676 : 0 : ::basegfx::B2DHomMatrix aMatrix;
677 : 0 : ::canvas::tools::mergeViewAndRenderTransform(aMatrix, viewState, renderState);
678 : :
679 : 0 : ::basegfx::B2DPoint aOutputPos( 0.0, 0.0 );
680 : 0 : aOutputPos *= aMatrix;
681 : :
682 : 0 : BitmapEx aBmpEx( tools::bitmapExFromXBitmap(xBitmap) );
683 : :
684 : : // TODO(F2): Implement modulation again for other color
685 : : // channels (currently, works only for alpha). Note: this
686 : : // is already implemented in transformBitmap()
687 : 0 : if( bModulateColors &&
688 : 0 : renderState.DeviceColor.getLength() > 3 )
689 : : {
690 : : // optimize away the case where alpha modulation value
691 : : // is 1.0 - we then simply switch off modulation at all
692 : : bModulateColors = !::rtl::math::approxEqual(
693 : 0 : renderState.DeviceColor[3], 1.0);
694 : : }
695 : :
696 : : // check whether we can render bitmap as-is: must not
697 : : // modulate colors, matrix must either be the identity
698 : : // transform (that's clear), _or_ contain only
699 : : // translational components.
700 : 0 : if( !bModulateColors &&
701 : 0 : (aMatrix.isIdentity() ||
702 : 0 : (::basegfx::fTools::equalZero( aMatrix.get(0,1) ) &&
703 : 0 : ::basegfx::fTools::equalZero( aMatrix.get(1,0) ) &&
704 : 0 : ::rtl::math::approxEqual(aMatrix.get(0,0), 1.0) &&
705 : 0 : ::rtl::math::approxEqual(aMatrix.get(1,1), 1.0)) ) )
706 : : {
707 : : // optimized case: identity matrix, or only
708 : : // translational components.
709 : 0 : mpOutDev->getOutDev().DrawBitmapEx( ::vcl::unotools::pointFromB2DPoint( aOutputPos ),
710 : 0 : aBmpEx );
711 : :
712 : 0 : if( mp2ndOutDev )
713 : : {
714 : : // HACK. Normally, CanvasHelper does not care about
715 : : // actually what mp2ndOutDev is... well, here we do &
716 : : // assume a 1bpp target - everything beyond 97%
717 : : // transparency is fully transparent
718 : 0 : if( aBmpEx.IsAlpha() )
719 : : {
720 : 0 : Bitmap aMask( aBmpEx.GetAlpha().GetBitmap() );
721 : 0 : aMask.MakeMono( 253 );
722 : 0 : aBmpEx = BitmapEx( aBmpEx.GetBitmap(), aMask );
723 : : }
724 : 0 : else if( aBmpEx.IsTransparent() )
725 : : {
726 : 0 : aBmpEx = BitmapEx( aBmpEx.GetBitmap(), aBmpEx.GetMask() );
727 : : }
728 : :
729 : 0 : mp2ndOutDev->getOutDev().DrawBitmapEx( ::vcl::unotools::pointFromB2DPoint( aOutputPos ),
730 : 0 : aBmpEx );
731 : : }
732 : :
733 : : // Returning a cache object is not useful, the XBitmap
734 : : // itself serves this purpose
735 : 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
736 : : }
737 : : else
738 : : {
739 : : // Matrix contains non-trivial transformation (or
740 : : // color modulation is requested), decompose to check
741 : : // whether GraphicObject suffices
742 : 0 : ::basegfx::B2DVector aScale;
743 : : double nRotate;
744 : : double nShearX;
745 : 0 : aMatrix.decompose( aScale, aOutputPos, nRotate, nShearX );
746 : :
747 : 0 : GraphicAttr aGrfAttr;
748 : 0 : GraphicObjectSharedPtr pGrfObj;
749 : :
750 : 0 : ::Size aBmpSize( aBmpEx.GetSizePixel() );
751 : :
752 : : // setup alpha modulation
753 : 0 : if( bModulateColors )
754 : : {
755 : 0 : const double nAlphaModulation( renderState.DeviceColor[3] );
756 : :
757 : : // TODO(F1): Note that the GraphicManager has a
758 : : // subtle difference in how it calculates the
759 : : // resulting alpha value: it's using the inverse
760 : : // alpha values (i.e. 'transparency'), and
761 : : // calculates transOrig + transModulate, instead
762 : : // of transOrig + transModulate -
763 : : // transOrig*transModulate (which would be
764 : : // equivalent to the origAlpha*modulateAlpha the
765 : : // DX canvas performs)
766 : : aGrfAttr.SetTransparency(
767 : : static_cast< sal_uInt8 >(
768 : 0 : ::basegfx::fround( 255.0*( 1.0 - nAlphaModulation ) ) ) );
769 : : }
770 : :
771 : 0 : if( ::basegfx::fTools::equalZero( nShearX ) )
772 : : {
773 : : // no shear, GraphicObject is enough (the
774 : : // GraphicObject only supports scaling, rotation
775 : : // and translation)
776 : :
777 : : // #i75339# don't apply mirror flags, having
778 : : // negative size values is enough to make
779 : : // GraphicObject flip the bitmap
780 : :
781 : : // The angle has to be mapped from radian to tenths of
782 : : // degress with the orientation reversed: [0,2Pi) ->
783 : : // (3600,0]. Note that the original angle may have
784 : : // values outside the [0,2Pi) interval.
785 : 0 : const double nAngleInTenthOfDegrees (3600.0 - nRotate * 3600.0 / (2*M_PI));
786 : 0 : aGrfAttr.SetRotation( static_cast< sal_uInt16 >(::basegfx::fround(nAngleInTenthOfDegrees)) );
787 : :
788 : 0 : pGrfObj.reset( new GraphicObject( aBmpEx ) );
789 : : }
790 : : else
791 : : {
792 : : // modify output position, to account for the fact
793 : : // that transformBitmap() always normalizes its output
794 : : // bitmap into the smallest enclosing box.
795 : 0 : ::basegfx::B2DRectangle aDestRect;
796 : : ::canvas::tools::calcTransformedRectBounds( aDestRect,
797 : : ::basegfx::B2DRectangle(0,
798 : : 0,
799 : 0 : aBmpSize.Width(),
800 : 0 : aBmpSize.Height()),
801 : 0 : aMatrix );
802 : :
803 : 0 : aOutputPos.setX( aDestRect.getMinX() );
804 : 0 : aOutputPos.setY( aDestRect.getMinY() );
805 : :
806 : : // complex transformation, use generic affine bitmap
807 : : // transformation
808 : : aBmpEx = tools::transformBitmap( aBmpEx,
809 : : aMatrix,
810 : : renderState.DeviceColor,
811 : 0 : tools::MODULATE_NONE );
812 : :
813 : 0 : pGrfObj.reset( new GraphicObject( aBmpEx ) );
814 : :
815 : : // clear scale values, generated bitmap already
816 : : // contains scaling
817 : 0 : aScale.setX( 1.0 ); aScale.setY( 1.0 );
818 : :
819 : : // update bitmap size, bitmap has changed above.
820 : 0 : aBmpSize = aBmpEx.GetSizePixel();
821 : : }
822 : :
823 : : // output GraphicObject
824 : 0 : const ::Point aPt( ::vcl::unotools::pointFromB2DPoint( aOutputPos ) );
825 : 0 : const ::Size aSz( ::basegfx::fround( aScale.getX() * aBmpSize.Width() ),
826 : 0 : ::basegfx::fround( aScale.getY() * aBmpSize.Height() ) );
827 : :
828 : 0 : pGrfObj->Draw( &mpOutDev->getOutDev(),
829 : : aPt,
830 : : aSz,
831 : 0 : &aGrfAttr );
832 : :
833 : 0 : if( mp2ndOutDev )
834 : 0 : pGrfObj->Draw( &mp2ndOutDev->getOutDev(),
835 : : aPt,
836 : : aSz,
837 : 0 : &aGrfAttr );
838 : :
839 : : // created GraphicObject, which possibly cached
840 : : // display bitmap - return cache object, to retain
841 : : // that information.
842 : : return uno::Reference< rendering::XCachedPrimitive >(
843 : : new CachedBitmap( pGrfObj,
844 : : aPt,
845 : : aSz,
846 : : aGrfAttr,
847 : : viewState,
848 : : renderState,
849 : : // cast away const, need to
850 : : // change refcount (as this is
851 : : // ~invisible to client code,
852 : : // still logically const)
853 : 0 : const_cast< rendering::XCanvas* >(pCanvas)) );
854 : 0 : }
855 : : }
856 : :
857 : : // Nothing rendered
858 : 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
859 : : }
860 : :
861 : 0 : uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* pCanvas,
862 : : const uno::Reference< rendering::XBitmap >& xBitmap,
863 : : const rendering::ViewState& viewState,
864 : : const rendering::RenderState& renderState )
865 : : {
866 : : return implDrawBitmap( pCanvas,
867 : : xBitmap,
868 : : viewState,
869 : : renderState,
870 : 0 : false );
871 : : }
872 : :
873 : 0 : uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas,
874 : : const uno::Reference< rendering::XBitmap >& xBitmap,
875 : : const rendering::ViewState& viewState,
876 : : const rendering::RenderState& renderState )
877 : : {
878 : : return implDrawBitmap( pCanvas,
879 : : xBitmap,
880 : : viewState,
881 : : renderState,
882 : 0 : true );
883 : : }
884 : :
885 : 0 : uno::Reference< rendering::XGraphicDevice > CanvasHelper::getDevice()
886 : : {
887 : : // cast away const, need to change refcount (as this is
888 : : // ~invisible to client code, still logically const)
889 : 0 : return uno::Reference< rendering::XGraphicDevice >(mpDevice);
890 : : }
891 : :
892 : 0 : void CanvasHelper::copyRect( const rendering::XCanvas* ,
893 : : const uno::Reference< rendering::XBitmapCanvas >& ,
894 : : const geometry::RealRectangle2D& ,
895 : : const rendering::ViewState& ,
896 : : const rendering::RenderState& ,
897 : : const geometry::RealRectangle2D& ,
898 : : const rendering::ViewState& ,
899 : : const rendering::RenderState& )
900 : : {
901 : : // TODO(F1)
902 : 0 : }
903 : :
904 : 0 : geometry::IntegerSize2D CanvasHelper::getSize()
905 : : {
906 : 0 : if( !mpOutDev.get() )
907 : 0 : return geometry::IntegerSize2D(); // we're disposed
908 : :
909 : 0 : return ::vcl::unotools::integerSize2DFromSize( mpOutDev->getOutDev().GetOutputSizePixel() );
910 : : }
911 : :
912 : 0 : uno::Reference< rendering::XBitmap > CanvasHelper::getScaledBitmap( const geometry::RealSize2D& newSize,
913 : : sal_Bool beFast )
914 : : {
915 : 0 : if( !mpOutDev.get() || !mpDevice )
916 : 0 : return uno::Reference< rendering::XBitmap >(); // we're disposed
917 : :
918 : 0 : OutputDevice& rOutDev( mpOutDev->getOutDev() );
919 : :
920 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
921 : 0 : rOutDev.EnableMapMode( sal_False );
922 : :
923 : : // TODO(F2): Support alpha vdev canvas here
924 : 0 : const Point aEmptyPoint(0,0);
925 : 0 : const Size aBmpSize( rOutDev.GetOutputSizePixel() );
926 : :
927 : 0 : Bitmap aBitmap( rOutDev.GetBitmap(aEmptyPoint, aBmpSize) );
928 : :
929 : : aBitmap.Scale( ::vcl::unotools::sizeFromRealSize2D(newSize),
930 : 0 : beFast ? BMP_SCALE_FAST : BMP_SCALE_DEFAULT );
931 : :
932 : : return uno::Reference< rendering::XBitmap >(
933 : 0 : new CanvasBitmap( aBitmap, *mpDevice, mpOutDev ) );
934 : : }
935 : :
936 : 0 : uno::Sequence< sal_Int8 > CanvasHelper::getData( rendering::IntegerBitmapLayout& rLayout,
937 : : const geometry::IntegerRectangle2D& rect )
938 : : {
939 : 0 : if( !mpOutDev.get() )
940 : 0 : return uno::Sequence< sal_Int8 >(); // we're disposed
941 : :
942 : 0 : rLayout = getMemoryLayout();
943 : :
944 : : // TODO(F2): Support alpha canvas here
945 : 0 : const Rectangle aRect( ::vcl::unotools::rectangleFromIntegerRectangle2D(rect) );
946 : :
947 : 0 : OutputDevice& rOutDev( mpOutDev->getOutDev() );
948 : :
949 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
950 : 0 : rOutDev.EnableMapMode( sal_False );
951 : :
952 : : Bitmap aBitmap( rOutDev.GetBitmap(aRect.TopLeft(),
953 : 0 : aRect.GetSize()) );
954 : :
955 : 0 : Bitmap::ScopedReadAccess pReadAccess( aBitmap );
956 : :
957 : 0 : ENSURE_OR_THROW( pReadAccess.get() != NULL,
958 : : "Could not acquire read access to OutDev bitmap" );
959 : :
960 : 0 : const sal_Int32 nWidth( rect.X2 - rect.X1 );
961 : 0 : const sal_Int32 nHeight( rect.Y2 - rect.Y1 );
962 : :
963 : 0 : rLayout.ScanLines = nHeight;
964 : 0 : rLayout.ScanLineBytes = nWidth*4;
965 : 0 : rLayout.ScanLineStride = rLayout.ScanLineBytes;
966 : :
967 : 0 : uno::Sequence< sal_Int8 > aRes( 4*nWidth*nHeight );
968 : 0 : sal_Int8* pRes = aRes.getArray();
969 : :
970 : 0 : int nCurrPos(0);
971 : 0 : for( int y=0; y<nHeight; ++y )
972 : : {
973 : 0 : for( int x=0; x<nWidth; ++x )
974 : : {
975 : 0 : pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed();
976 : 0 : pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen();
977 : 0 : pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue();
978 : 0 : pRes[ nCurrPos++ ] = -1;
979 : : }
980 : : }
981 : :
982 : 0 : return aRes;
983 : : }
984 : :
985 : 0 : void CanvasHelper::setData( const uno::Sequence< sal_Int8 >& data,
986 : : const rendering::IntegerBitmapLayout& aLayout,
987 : : const geometry::IntegerRectangle2D& rect )
988 : : {
989 : 0 : if( !mpOutDev.get() )
990 : 0 : return; // we're disposed
991 : :
992 : 0 : const rendering::IntegerBitmapLayout aRefLayout( getMemoryLayout() );
993 : 0 : ENSURE_ARG_OR_THROW( aRefLayout.PlaneStride != aLayout.PlaneStride ||
994 : : aRefLayout.ColorSpace != aLayout.ColorSpace ||
995 : : aRefLayout.Palette != aLayout.Palette ||
996 : : aRefLayout.IsMsbFirst != aLayout.IsMsbFirst,
997 : : "Mismatching memory layout" );
998 : :
999 : 0 : OutputDevice& rOutDev( mpOutDev->getOutDev() );
1000 : :
1001 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
1002 : 0 : rOutDev.EnableMapMode( sal_False );
1003 : :
1004 : 0 : const Rectangle aRect( ::vcl::unotools::rectangleFromIntegerRectangle2D(rect) );
1005 : : const sal_uInt16 nBitCount( ::std::min( (sal_uInt16)24U,
1006 : 0 : (sal_uInt16)rOutDev.GetBitCount() ) );
1007 : 0 : const BitmapPalette* pPalette = NULL;
1008 : :
1009 : 0 : if( nBitCount <= 8 )
1010 : : {
1011 : : // TODO(Q1): Extract this to a common place, e.g. GraphicDevice
1012 : :
1013 : : // try to determine palette from output device (by
1014 : : // extracting a 1,1 bitmap, and querying it)
1015 : 0 : const Point aEmptyPoint;
1016 : 0 : const Size aSize(1,1);
1017 : : Bitmap aTmpBitmap( rOutDev.GetBitmap( aEmptyPoint,
1018 : 0 : aSize ) );
1019 : :
1020 : 0 : Bitmap::ScopedReadAccess pReadAccess( aTmpBitmap );
1021 : :
1022 : 0 : pPalette = &pReadAccess->GetPalette();
1023 : : }
1024 : :
1025 : : // TODO(F2): Support alpha canvas here
1026 : 0 : Bitmap aBitmap( aRect.GetSize(), nBitCount, pPalette );
1027 : :
1028 : 0 : bool bCopyBack( false ); // only copy something back, if we
1029 : : // actually changed some pixel
1030 : : {
1031 : 0 : Bitmap::ScopedWriteAccess pWriteAccess( aBitmap );
1032 : :
1033 : 0 : ENSURE_OR_THROW( pWriteAccess.get() != NULL,
1034 : : "Could not acquire write access to OutDev bitmap" );
1035 : :
1036 : : // for the time being, always read as RGB
1037 : 0 : const sal_Int32 nWidth( rect.X2 - rect.X1 );
1038 : 0 : const sal_Int32 nHeight( rect.Y2 - rect.Y1 );
1039 : 0 : int x, y, nCurrPos(0);
1040 : 0 : for( y=0; y<nHeight; ++y )
1041 : : {
1042 : 0 : switch( pWriteAccess->GetScanlineFormat() )
1043 : : {
1044 : : case BMP_FORMAT_8BIT_PAL:
1045 : : {
1046 : 0 : Scanline pScan = pWriteAccess->GetScanline( y );
1047 : :
1048 : 0 : for( x=0; x<nWidth; ++x )
1049 : : {
1050 : 0 : *pScan++ = (sal_uInt8)pWriteAccess->GetBestPaletteIndex(
1051 : 0 : BitmapColor( data[ nCurrPos ],
1052 : 0 : data[ nCurrPos+1 ],
1053 : 0 : data[ nCurrPos+2 ] ) );
1054 : :
1055 : 0 : nCurrPos += 4;
1056 : : }
1057 : : }
1058 : 0 : break;
1059 : :
1060 : : case BMP_FORMAT_24BIT_TC_BGR:
1061 : : {
1062 : 0 : Scanline pScan = pWriteAccess->GetScanline( y );
1063 : :
1064 : 0 : for( x=0; x<nWidth; ++x )
1065 : : {
1066 : 0 : *pScan++ = data[ nCurrPos+2 ];
1067 : 0 : *pScan++ = data[ nCurrPos+1 ];
1068 : 0 : *pScan++ = data[ nCurrPos ];
1069 : :
1070 : 0 : nCurrPos += 4;
1071 : : }
1072 : : }
1073 : 0 : break;
1074 : :
1075 : : case BMP_FORMAT_24BIT_TC_RGB:
1076 : : {
1077 : 0 : Scanline pScan = pWriteAccess->GetScanline( y );
1078 : :
1079 : 0 : for( x=0; x<nWidth; ++x )
1080 : : {
1081 : 0 : *pScan++ = data[ nCurrPos ];
1082 : 0 : *pScan++ = data[ nCurrPos+1 ];
1083 : 0 : *pScan++ = data[ nCurrPos+2 ];
1084 : :
1085 : 0 : nCurrPos += 4;
1086 : : }
1087 : : }
1088 : 0 : break;
1089 : :
1090 : : default:
1091 : : {
1092 : 0 : for( x=0; x<nWidth; ++x )
1093 : : {
1094 : 0 : pWriteAccess->SetPixel( y, x, BitmapColor( data[ nCurrPos ],
1095 : 0 : data[ nCurrPos+1 ],
1096 : 0 : data[ nCurrPos+2 ] ) );
1097 : 0 : nCurrPos += 4;
1098 : : }
1099 : : }
1100 : 0 : break;
1101 : : }
1102 : : }
1103 : :
1104 : 0 : bCopyBack = true;
1105 : : }
1106 : :
1107 : : // copy back only here, since the BitmapAccessors must be
1108 : : // destroyed beforehand
1109 : 0 : if( bCopyBack )
1110 : : {
1111 : : // TODO(F2): Support alpha canvas here
1112 : 0 : rOutDev.DrawBitmap(aRect.TopLeft(), aBitmap);
1113 : 0 : }
1114 : : }
1115 : :
1116 : 0 : void CanvasHelper::setPixel( const uno::Sequence< sal_Int8 >& color,
1117 : : const rendering::IntegerBitmapLayout& rLayout,
1118 : : const geometry::IntegerPoint2D& pos )
1119 : : {
1120 : 0 : if( !mpOutDev.get() )
1121 : 0 : return; // we're disposed
1122 : :
1123 : 0 : OutputDevice& rOutDev( mpOutDev->getOutDev() );
1124 : :
1125 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
1126 : 0 : rOutDev.EnableMapMode( sal_False );
1127 : :
1128 : 0 : const Size aBmpSize( rOutDev.GetOutputSizePixel() );
1129 : :
1130 : 0 : ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aBmpSize.Width(),
1131 : : "X coordinate out of bounds" );
1132 : 0 : ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aBmpSize.Height(),
1133 : : "Y coordinate out of bounds" );
1134 : 0 : ENSURE_ARG_OR_THROW( color.getLength() > 3,
1135 : : "not enough color components" );
1136 : :
1137 : 0 : const rendering::IntegerBitmapLayout aRefLayout( getMemoryLayout() );
1138 : 0 : ENSURE_ARG_OR_THROW( aRefLayout.PlaneStride != rLayout.PlaneStride ||
1139 : : aRefLayout.ColorSpace != rLayout.ColorSpace ||
1140 : : aRefLayout.Palette != rLayout.Palette ||
1141 : : aRefLayout.IsMsbFirst != rLayout.IsMsbFirst,
1142 : : "Mismatching memory layout" );
1143 : :
1144 : : // TODO(F2): Support alpha canvas here
1145 : : rOutDev.DrawPixel( ::vcl::unotools::pointFromIntegerPoint2D( pos ),
1146 : 0 : ::canvas::tools::stdIntSequenceToColor( color ));
1147 : : }
1148 : :
1149 : 0 : uno::Sequence< sal_Int8 > CanvasHelper::getPixel( rendering::IntegerBitmapLayout& rLayout,
1150 : : const geometry::IntegerPoint2D& pos )
1151 : : {
1152 : 0 : if( !mpOutDev.get() )
1153 : 0 : return uno::Sequence< sal_Int8 >(); // we're disposed
1154 : :
1155 : 0 : rLayout = getMemoryLayout();
1156 : 0 : rLayout.ScanLines = 1;
1157 : 0 : rLayout.ScanLineBytes = 4;
1158 : 0 : rLayout.ScanLineStride = rLayout.ScanLineBytes;
1159 : :
1160 : 0 : OutputDevice& rOutDev( mpOutDev->getOutDev() );
1161 : :
1162 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
1163 : 0 : rOutDev.EnableMapMode( sal_False );
1164 : :
1165 : 0 : const Size aBmpSize( rOutDev.GetOutputSizePixel() );
1166 : :
1167 : 0 : ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aBmpSize.Width(),
1168 : : "X coordinate out of bounds" );
1169 : 0 : ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aBmpSize.Height(),
1170 : : "Y coordinate out of bounds" );
1171 : :
1172 : : // TODO(F2): Support alpha canvas here
1173 : : return ::canvas::tools::colorToStdIntSequence(
1174 : : rOutDev.GetPixel(
1175 : 0 : ::vcl::unotools::pointFromIntegerPoint2D( pos )));
1176 : : }
1177 : :
1178 : 0 : rendering::IntegerBitmapLayout CanvasHelper::getMemoryLayout()
1179 : : {
1180 : 0 : if( !mpOutDev.get() )
1181 : 0 : return rendering::IntegerBitmapLayout(); // we're disposed
1182 : :
1183 : 0 : return ::canvas::tools::getStdMemoryLayout(getSize());
1184 : : }
1185 : :
1186 : 0 : int CanvasHelper::setupOutDevState( const rendering::ViewState& viewState,
1187 : : const rendering::RenderState& renderState,
1188 : : ColorType eColorType ) const
1189 : : {
1190 : 0 : ENSURE_OR_THROW( mpOutDev.get(),
1191 : : "outdev null. Are we disposed?" );
1192 : :
1193 : : ::canvas::tools::verifyInput( renderState,
1194 : : BOOST_CURRENT_FUNCTION,
1195 : : mpDevice,
1196 : : 2,
1197 : 0 : eColorType == IGNORE_COLOR ? 0 : 3 );
1198 : :
1199 : 0 : OutputDevice& rOutDev( mpOutDev->getOutDev() );
1200 : 0 : OutputDevice* p2ndOutDev = NULL;
1201 : :
1202 : 0 : rOutDev.EnableMapMode( sal_False );
1203 : :
1204 : 0 : if( mp2ndOutDev )
1205 : 0 : p2ndOutDev = &mp2ndOutDev->getOutDev();
1206 : :
1207 : 0 : int nTransparency(0);
1208 : :
1209 : : // TODO(P2): Don't change clipping all the time, maintain current clip
1210 : : // state and change only when update is necessary
1211 : :
1212 : : // accumulate non-empty clips into one region
1213 : : // ==========================================
1214 : :
1215 : 0 : Region aClipRegion( REGION_NULL );
1216 : :
1217 : 0 : if( viewState.Clip.is() )
1218 : : {
1219 : : ::basegfx::B2DPolyPolygon aClipPoly(
1220 : 0 : ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(viewState.Clip) );
1221 : :
1222 : 0 : if( aClipPoly.count() )
1223 : : {
1224 : : // setup non-empty clipping
1225 : 0 : ::basegfx::B2DHomMatrix aMatrix;
1226 : : aClipPoly.transform(
1227 : : ::basegfx::unotools::homMatrixFromAffineMatrix( aMatrix,
1228 : 0 : viewState.AffineTransform ) );
1229 : :
1230 : 0 : aClipRegion = Region::GetRegionFromPolyPolygon( ::PolyPolygon( aClipPoly ) );
1231 : : }
1232 : : else
1233 : : {
1234 : : // clip polygon is empty
1235 : 0 : aClipRegion.SetEmpty();
1236 : 0 : }
1237 : : }
1238 : :
1239 : 0 : if( renderState.Clip.is() )
1240 : : {
1241 : : ::basegfx::B2DPolyPolygon aClipPoly(
1242 : 0 : ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(renderState.Clip) );
1243 : :
1244 : 0 : ::basegfx::B2DHomMatrix aMatrix;
1245 : : aClipPoly.transform(
1246 : : ::canvas::tools::mergeViewAndRenderTransform( aMatrix,
1247 : : viewState,
1248 : 0 : renderState ) );
1249 : :
1250 : 0 : if( aClipPoly.count() )
1251 : : {
1252 : : // setup non-empty clipping
1253 : 0 : Region aRegion = Region::GetRegionFromPolyPolygon( ::PolyPolygon( aClipPoly ) );
1254 : 0 : aClipRegion.Intersect( aRegion );
1255 : : }
1256 : : else
1257 : : {
1258 : : // clip polygon is empty
1259 : 0 : aClipRegion.SetEmpty();
1260 : 0 : }
1261 : : }
1262 : :
1263 : : // setup accumulated clip region. Note that setting an
1264 : : // empty clip region denotes "clip everything" on the
1265 : : // OutputDevice (which is why we translate that into
1266 : : // SetClipRegion() here). When both view and render clip
1267 : : // are empty, aClipRegion remains default-constructed,
1268 : : // i.e. empty, too.
1269 : 0 : if( aClipRegion.IsNull() )
1270 : : {
1271 : 0 : rOutDev.SetClipRegion();
1272 : :
1273 : 0 : if( p2ndOutDev )
1274 : 0 : p2ndOutDev->SetClipRegion();
1275 : : }
1276 : : else
1277 : : {
1278 : 0 : rOutDev.SetClipRegion( aClipRegion );
1279 : :
1280 : 0 : if( p2ndOutDev )
1281 : 0 : p2ndOutDev->SetClipRegion( aClipRegion );
1282 : : }
1283 : :
1284 : 0 : Color aColor( COL_WHITE );
1285 : :
1286 : 0 : if( renderState.DeviceColor.getLength() > 2 )
1287 : : {
1288 : : aColor = ::vcl::unotools::stdColorSpaceSequenceToColor(
1289 : 0 : renderState.DeviceColor );
1290 : : }
1291 : :
1292 : : // extract alpha, and make color opaque
1293 : : // afterwards. Otherwise, OutputDevice won't draw anything
1294 : 0 : nTransparency = aColor.GetTransparency();
1295 : 0 : aColor.SetTransparency(0);
1296 : :
1297 : 0 : if( eColorType != IGNORE_COLOR )
1298 : : {
1299 : 0 : switch( eColorType )
1300 : : {
1301 : : case LINE_COLOR:
1302 : 0 : rOutDev.SetLineColor( aColor );
1303 : 0 : rOutDev.SetFillColor();
1304 : :
1305 : 0 : if( p2ndOutDev )
1306 : : {
1307 : 0 : p2ndOutDev->SetLineColor( aColor );
1308 : 0 : p2ndOutDev->SetFillColor();
1309 : : }
1310 : 0 : break;
1311 : :
1312 : : case FILL_COLOR:
1313 : 0 : rOutDev.SetFillColor( aColor );
1314 : 0 : rOutDev.SetLineColor();
1315 : :
1316 : 0 : if( p2ndOutDev )
1317 : : {
1318 : 0 : p2ndOutDev->SetFillColor( aColor );
1319 : 0 : p2ndOutDev->SetLineColor();
1320 : : }
1321 : 0 : break;
1322 : :
1323 : : case TEXT_COLOR:
1324 : 0 : rOutDev.SetTextColor( aColor );
1325 : :
1326 : 0 : if( p2ndOutDev )
1327 : 0 : p2ndOutDev->SetTextColor( aColor );
1328 : 0 : break;
1329 : :
1330 : : default:
1331 : 0 : ENSURE_OR_THROW( false,
1332 : : "Unexpected color type");
1333 : : break;
1334 : : }
1335 : : }
1336 : :
1337 : 0 : return nTransparency;
1338 : : }
1339 : :
1340 : 0 : bool CanvasHelper::setupTextOutput( ::Point& o_rOutPos,
1341 : : const rendering::ViewState& viewState,
1342 : : const rendering::RenderState& renderState,
1343 : : const uno::Reference< rendering::XCanvasFont >& xFont ) const
1344 : : {
1345 : 0 : ENSURE_OR_THROW( mpOutDev.get(),
1346 : : "outdev null. Are we disposed?" );
1347 : :
1348 : 0 : setupOutDevState( viewState, renderState, TEXT_COLOR );
1349 : :
1350 : 0 : OutputDevice& rOutDev( mpOutDev->getOutDev() );
1351 : :
1352 : 0 : ::Font aVCLFont;
1353 : :
1354 : 0 : CanvasFont* pFont = dynamic_cast< CanvasFont* >( xFont.get() );
1355 : :
1356 : 0 : ENSURE_ARG_OR_THROW( pFont,
1357 : : "Font not compatible with this canvas" );
1358 : :
1359 : 0 : aVCLFont = pFont->getVCLFont();
1360 : :
1361 : 0 : Color aColor( COL_BLACK );
1362 : :
1363 : 0 : if( renderState.DeviceColor.getLength() > 2 )
1364 : : {
1365 : : aColor = ::vcl::unotools::stdColorSpaceSequenceToColor(
1366 : 0 : renderState.DeviceColor );
1367 : : }
1368 : :
1369 : : // setup font color
1370 : 0 : aVCLFont.SetColor( aColor );
1371 : 0 : aVCLFont.SetFillColor( aColor );
1372 : :
1373 : : // no need to replicate this for mp2ndOutDev, we're modifying only aVCLFont here.
1374 : 0 : if( !tools::setupFontTransform( o_rOutPos, aVCLFont, viewState, renderState, rOutDev ) )
1375 : 0 : return false;
1376 : :
1377 : 0 : rOutDev.SetFont( aVCLFont );
1378 : :
1379 : 0 : if( mp2ndOutDev )
1380 : 0 : mp2ndOutDev->getOutDev().SetFont( aVCLFont );
1381 : :
1382 : 0 : return true;
1383 : : }
1384 : :
1385 : 0 : bool CanvasHelper::repaint( const GraphicObjectSharedPtr& rGrf,
1386 : : const rendering::ViewState& viewState,
1387 : : const rendering::RenderState& renderState,
1388 : : const ::Point& rPt,
1389 : : const ::Size& rSz,
1390 : : const GraphicAttr& rAttr ) const
1391 : : {
1392 : 0 : ENSURE_OR_RETURN_FALSE( rGrf,
1393 : : "Invalid Graphic" );
1394 : :
1395 : 0 : if( !mpOutDev )
1396 : 0 : return false; // disposed
1397 : : else
1398 : : {
1399 : 0 : tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
1400 : 0 : setupOutDevState( viewState, renderState, IGNORE_COLOR );
1401 : :
1402 : 0 : if( !rGrf->Draw( &mpOutDev->getOutDev(), rPt, rSz, &rAttr ) )
1403 : 0 : return false;
1404 : :
1405 : : // #i80779# Redraw also into mask outdev
1406 : 0 : if( mp2ndOutDev )
1407 : 0 : return rGrf->Draw( &mp2ndOutDev->getOutDev(), rPt, rSz, &rAttr );
1408 : :
1409 : 0 : return true;
1410 : : }
1411 : : }
1412 : :
1413 : 0 : void CanvasHelper::flush() const
1414 : : {
1415 : 0 : if( mpOutDev && mpOutDev->getOutDev().GetOutDevType() == OUTDEV_WINDOW )
1416 : : {
1417 : : // TODO(Q3): Evil downcast. And what's more, Window::Flush is
1418 : : // not even const. Wah.
1419 : 0 : static_cast<Window&>(mpOutDev->getOutDev()).Flush();
1420 : : }
1421 : :
1422 : 0 : if( mp2ndOutDev && mp2ndOutDev->getOutDev().GetOutDevType() == OUTDEV_WINDOW )
1423 : : {
1424 : : // TODO(Q3): Evil downcast. And what's more, Window::Flush is
1425 : : // not even const. Wah.
1426 : 0 : static_cast<Window&>(mp2ndOutDev->getOutDev()).Flush();
1427 : : }
1428 : 0 : }
1429 : :
1430 : 0 : }
1431 : :
1432 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|