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