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