|
1 // Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // Unless stated otherwise, all co-ordinates are recorded and calculated using |
|
15 // Symbian graphics format: origin at top left of image, 'y' increasing towards bottom |
|
16 // of image. Note that OpenVG places the origin at an image's bottom left corner, with |
|
17 // 'y' increasing towards the top of the image. |
|
18 // |
|
19 // |
|
20 |
|
21 #include "vgengine.h" |
|
22 #include "directgdiadapter.h" |
|
23 #include "directgdidriverimpl.h" |
|
24 #include "directgdiimagetargetimpl.h" |
|
25 #include "directgdiimagesourceimpl.h" |
|
26 #include "confighelper.h" |
|
27 #include <graphics/directgdiimagetarget.h> |
|
28 #include <graphics/directgdidrawablesource.h> |
|
29 #include <fbs.h> |
|
30 #include <e32cmn.h> |
|
31 #include <e32math.h> |
|
32 #include <s32strm.h> |
|
33 #include <pixelformats.h> |
|
34 #include <bitdraworigin.h> |
|
35 #include <bitdrawinterfaceid.h> |
|
36 |
|
37 #include "blendingalgorithms.inl" |
|
38 |
|
39 /** |
|
40 Pre-calculation for normalising a 0-255 colour channel to 0..1. |
|
41 */ |
|
42 const VGfloat KColorConversion = 1.0f/255.0f; |
|
43 |
|
44 |
|
45 inline TInt Scale(TInt aValue,TInt aNum,TInt aDen) |
|
46 { |
|
47 if (aNum==aDen) |
|
48 return aValue; |
|
49 TInt64 result(aValue); |
|
50 result=(result*aNum+(aDen/2))/aDen; |
|
51 return I64INT(result); |
|
52 } |
|
53 |
|
54 /** |
|
55 Maps TDisplayMode onto a supported VGImageFormat. Only compatible formats are returned, |
|
56 anything else results in VG_IMAGE_FORMAT_INVALID being returned. |
|
57 |
|
58 @param aDisplayMode Mode to convert from. |
|
59 |
|
60 @return The VGImageFormat which matches the specified TDisplayMode pixel format exactly. |
|
61 If no exact match exists, then VG_IMAGE_FORMAT_INVALID is returned. |
|
62 */ |
|
63 static VGImageFormat MapToVgDisplayMode(TDisplayMode aDisplayMode) |
|
64 { |
|
65 switch(aDisplayMode) |
|
66 { |
|
67 case ENone: |
|
68 return VG_IMAGE_FORMAT_INVALID; |
|
69 case EGray2: |
|
70 return VG_BW_1; |
|
71 case EGray4: |
|
72 case EGray16: |
|
73 return VG_IMAGE_FORMAT_INVALID; |
|
74 case EGray256: |
|
75 return VG_sL_8; |
|
76 case EColor16: |
|
77 case EColor256: |
|
78 case EColor4K: |
|
79 return VG_IMAGE_FORMAT_INVALID; |
|
80 case EColor64K: |
|
81 return VG_sRGB_565; |
|
82 case EColor16M: |
|
83 case ERgb: |
|
84 return VG_IMAGE_FORMAT_INVALID; |
|
85 case EColor16MU: |
|
86 return VG_sXRGB_8888; |
|
87 case EColor16MA: |
|
88 return VG_sARGB_8888; |
|
89 case EColor16MAP: |
|
90 return VG_sARGB_8888_PRE; |
|
91 case EColorLast: |
|
92 return VG_IMAGE_FORMAT_INVALID; |
|
93 } |
|
94 |
|
95 return VG_IMAGE_FORMAT_INVALID; |
|
96 } |
|
97 |
|
98 /** |
|
99 Destructor |
|
100 */ |
|
101 CVgEngine::~CVgEngine() |
|
102 { |
|
103 iRegionManager.Close(); |
|
104 |
|
105 if (iBrushPatternUser != VG_INVALID_HANDLE) |
|
106 vgDestroyImage(iBrushPatternUser); |
|
107 |
|
108 if (iBrushPatternStandard != VG_INVALID_HANDLE) |
|
109 vgDestroyImage(iBrushPatternStandard); |
|
110 |
|
111 if (iBrushPatternStandardRegion != VG_INVALID_HANDLE) |
|
112 vgDestroyImage(iBrushPatternStandardRegion); |
|
113 |
|
114 if (iBrushPatternNonZeroOrigin != VG_INVALID_HANDLE) |
|
115 vgDestroyImage(iBrushPatternNonZeroOrigin); |
|
116 |
|
117 User::Free(iPathCommands); |
|
118 User::Free(iPathCoords); |
|
119 |
|
120 Deactivate(); |
|
121 } |
|
122 |
|
123 /** |
|
124 Constructor |
|
125 */ |
|
126 CVgEngine::CVgEngine(CDirectGdiDriverImpl& aDriver) |
|
127 :iDriver(aDriver), |
|
128 iPen(VG_INVALID_HANDLE), |
|
129 iBrush(VG_INVALID_HANDLE), |
|
130 iClearBrush(VG_INVALID_HANDLE), |
|
131 iVgPath(VG_INVALID_HANDLE), |
|
132 iTextBrush(VG_INVALID_HANDLE), |
|
133 iBrushPatternUser(VG_INVALID_HANDLE), |
|
134 iBrushPatternStandard(VG_INVALID_HANDLE), |
|
135 iBrushPatternStandardRegion(VG_INVALID_HANDLE), |
|
136 iBrushPatternNonZeroOrigin(VG_INVALID_HANDLE) |
|
137 { |
|
138 // Set the default paint mode for drawing and filling shapes to |
|
139 // just draw the pen and not the brush as default (to match BitGdi) |
|
140 iPaintMode = VG_STROKE_PATH; |
|
141 |
|
142 //cache interface to use every time glyph gets drawn |
|
143 GetInterface(TUid::Uid(KDirectGdiGetGlyphStorageUid), (TAny*&) iFontGlyphImageStorage); |
|
144 } |
|
145 |
|
146 /** |
|
147 Applies the engine's state to OpenVG, re-applying all the VgEngine settings. It is called by |
|
148 Activate() as well as when the engine is being made current. |
|
149 */ |
|
150 void CVgEngine::SetVgState() |
|
151 { |
|
152 ResetVgMatrix(); |
|
153 vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_NONANTIALIASED); |
|
154 vgSeti(VG_IMAGE_QUALITY, VG_IMAGE_QUALITY_NONANTIALIASED); |
|
155 vgSeti(VG_STROKE_CAP_STYLE, VG_CAP_ROUND); |
|
156 vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND); |
|
157 vgSetPaint(iPen, VG_STROKE_PATH); |
|
158 vgSetPaint(iBrush, VG_FILL_PATH); |
|
159 SetDrawMode(iDrawMode); |
|
160 SetPenColor(iPenColor); |
|
161 SetPenStyle(iPenStyle); |
|
162 SetPenSize(iPenSize); |
|
163 SetBrushColor(iBrushColor); |
|
164 SetBrushStyle(iBrushStyle); |
|
165 SetBrushOrigin(iBrushOrigin); |
|
166 vgSetParameteri(iClearBrush, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); |
|
167 vgSetParameteri(iTextBrush, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); |
|
168 } |
|
169 |
|
170 /** |
|
171 Binds a rendering target to this OpenVG rendering engine. |
|
172 Activates the target, which increments the reference count of target as it can be shared across many |
|
173 DirectGDI contexts. |
|
174 |
|
175 The rendering target, for this implementation, specifies an Embedded OpenVG rendering surface. |
|
176 |
|
177 @see MDirectGdiEngine::Activate() |
|
178 @see Deactivate() |
|
179 |
|
180 @panic DGDIAdapter 34, if the passed target has a NULL handle. |
|
181 @panic DGDIAdapter 39, if target associated with the handle is NULL. |
|
182 */ |
|
183 TInt CVgEngine::Activate(RDirectGdiImageTarget& aTarget) |
|
184 { |
|
185 TInt result = KErrNone; |
|
186 GRAPHICS_ASSERT_ALWAYS(aTarget.Handle(), EDirectGdiPanicActivateWithNullHandle); |
|
187 CDirectGdiImageTargetImpl* target = iDriver.GetImageTargetFromHandle(aTarget.Handle()); |
|
188 GRAPHICS_ASSERT_ALWAYS(target, EDirectGdiPanicNullTargetActivate); |
|
189 if (target == iRenderingTarget) |
|
190 { |
|
191 return KErrNone; |
|
192 } |
|
193 |
|
194 Deactivate(); |
|
195 |
|
196 iDriver.Activate(target); |
|
197 iRenderingTarget = target; |
|
198 iDriver.SetCurrentEngine(this); |
|
199 iDriver.SetCurrentTarget(target); |
|
200 iSize = iRenderingTarget->Size(); |
|
201 iTargetRegion.Clear(); |
|
202 iTargetRegion.AddRect(iSize); |
|
203 |
|
204 iRegionManager.Initialize(vgGeti(VG_MAX_SCISSOR_RECTS), iSize); |
|
205 |
|
206 // Set the origin to top-left for path and image rendering. iIdentityMatrix is set so that we have a |
|
207 // record of the "identity" transform. After modifying the transform for offsets, rotations, etc, etc |
|
208 // just set to iIdentityMatrix to get back to "normal". |
|
209 // |
|
210 vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); |
|
211 vgLoadIdentity(); |
|
212 vgScale(1, -1); |
|
213 vgTranslate(0, -iSize.iHeight); |
|
214 vgGetMatrix(iIdentityMatrix); |
|
215 // Set the origin to top-left for image matrix. |
|
216 vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); |
|
217 vgLoadMatrix(iIdentityMatrix); |
|
218 |
|
219 // Create the path. |
|
220 if (!PreparePath(iVgPath, 1) || (iVgPath == VG_INVALID_HANDLE)) |
|
221 result = KErrNoMemory; |
|
222 |
|
223 // Create the pen object and set it as the object for stroking (OpenVG equivalent of a pen). |
|
224 if (result == KErrNone) |
|
225 { |
|
226 iPen = vgCreatePaint(); |
|
227 if (iPen != VG_INVALID_HANDLE) |
|
228 vgSetPaint(iPen, VG_STROKE_PATH); |
|
229 else |
|
230 result = KErrNoMemory; |
|
231 } |
|
232 |
|
233 if (result == KErrNone) |
|
234 { |
|
235 // Create the brush object |
|
236 // This only need to be done the first time the context is activated |
|
237 iBrush = vgCreatePaint(); |
|
238 if (iBrush != VG_INVALID_HANDLE) |
|
239 vgSetPaint(iBrush, VG_FILL_PATH); |
|
240 else |
|
241 result = KErrNoMemory; |
|
242 } |
|
243 |
|
244 if (result == KErrNone) |
|
245 { |
|
246 // Create the brush object for Clear operations. |
|
247 // This only need to be done the first time the context is activated |
|
248 iClearBrush = vgCreatePaint(); |
|
249 if (iClearBrush == VG_INVALID_HANDLE) |
|
250 { |
|
251 result = KErrNoMemory; |
|
252 } |
|
253 } |
|
254 |
|
255 if (result == KErrNone) |
|
256 { |
|
257 iTextBrush = vgCreatePaint(); |
|
258 if (iTextBrush == VG_INVALID_HANDLE) |
|
259 { |
|
260 result = KErrNoMemory; |
|
261 } |
|
262 } |
|
263 |
|
264 if (result == KErrNone) |
|
265 { |
|
266 result = iDriver.PreAllocateFontGlyphImages(); |
|
267 } |
|
268 |
|
269 if (result == KErrNone) |
|
270 { |
|
271 SetVgState(); |
|
272 } |
|
273 |
|
274 return result; |
|
275 } |
|
276 |
|
277 /** |
|
278 @see MDirectGdiEngine::Deactivate() |
|
279 @see Activate() |
|
280 */ |
|
281 void CVgEngine::Deactivate() |
|
282 { |
|
283 if (iPen != VG_INVALID_HANDLE) |
|
284 { |
|
285 vgDestroyPaint(iPen); |
|
286 } |
|
287 if (iBrush != VG_INVALID_HANDLE) |
|
288 { |
|
289 vgDestroyPaint(iBrush); |
|
290 } |
|
291 if (iClearBrush != VG_INVALID_HANDLE) |
|
292 { |
|
293 vgDestroyPaint(iClearBrush); |
|
294 } |
|
295 if (iTextBrush != VG_INVALID_HANDLE) |
|
296 { |
|
297 vgDestroyPaint(iTextBrush); |
|
298 } |
|
299 if (iVgPath != VG_INVALID_HANDLE) |
|
300 { |
|
301 vgDestroyPath(iVgPath); |
|
302 } |
|
303 iPen = VG_INVALID_HANDLE; |
|
304 iBrush = VG_INVALID_HANDLE; |
|
305 iClearBrush = VG_INVALID_HANDLE; |
|
306 iVgPath = VG_INVALID_HANDLE; |
|
307 iTextBrush = VG_INVALID_HANDLE; |
|
308 |
|
309 if (iRenderingTarget) |
|
310 { |
|
311 // Deactivating the render target could potentially unbind the current EGL context |
|
312 // which would make the above vgDestroy() calls do nothing, therefore call Deactivate() last. |
|
313 iDriver.Deactivate(iRenderingTarget); |
|
314 } |
|
315 } |
|
316 |
|
317 /** |
|
318 Checks whether the engine and target are current. If current, then nothing is done, else all the OpenVG settings |
|
319 and EGL context are reapplied. This function is called in every drawing function to ensure that the engine and |
|
320 target are current. |
|
321 |
|
322 If this function returns EFalse, it means any subsequent setting of OpenVG state may be invalid |
|
323 and should be avoided as it is setting a null EGL context. |
|
324 |
|
325 @pre None. |
|
326 @post Applies the current state to OpenVG and is made the active EGL context, if the engine or target is |
|
327 not current. |
|
328 @return ETrue if as a result of calling this function, the underlying OpenVG context is now current. This |
|
329 effectively means whether we have a target or not. EFalse is returned otherwise. |
|
330 */ |
|
331 TBool CVgEngine::MakeEngineCurrent() |
|
332 { |
|
333 TBool vgCurrent = iRenderingTarget!=NULL; |
|
334 if(!iDriver.IsCurrentEngine(this) || !iDriver.IsCurrentTarget(iRenderingTarget)) |
|
335 { |
|
336 iDriver.SetCurrentEngine(this); |
|
337 iDriver.SetCurrentTarget(iRenderingTarget); |
|
338 // Must reactivate the target (i.e. make it current to EGL) before resetting the OpenVG parameters. |
|
339 if (iRenderingTarget) |
|
340 { |
|
341 iDriver.Reactivate(iRenderingTarget); |
|
342 SetVgState(); |
|
343 } |
|
344 else |
|
345 vgCurrent = EFalse; |
|
346 } |
|
347 |
|
348 return vgCurrent; |
|
349 } |
|
350 |
|
351 /** |
|
352 @see MDirectGdiEngine::SetOrigin() |
|
353 */ |
|
354 void CVgEngine::SetOrigin(const TPoint& aOrigin) |
|
355 { |
|
356 iOrigin = aOrigin + iDrawOrigin; |
|
357 |
|
358 if (!MakeEngineCurrent()) |
|
359 return; |
|
360 |
|
361 ResetVgMatrix(); |
|
362 } |
|
363 |
|
364 /** |
|
365 @see MDrawDeviceOrigin::Set() |
|
366 */ |
|
367 TInt CVgEngine::Set(const TPoint& aDrawOrigin) |
|
368 { |
|
369 TPoint moveOrigin=aDrawOrigin; |
|
370 moveOrigin-=iDrawOrigin; |
|
371 iOrigin+=moveOrigin; |
|
372 |
|
373 //offset clipping region |
|
374 TInt result = KErrNone; |
|
375 RRegion clippingRegion; |
|
376 clippingRegion.Copy(iRegionManager.ClippingRegion()); |
|
377 if(!clippingRegion.CheckError()) |
|
378 { |
|
379 clippingRegion.Offset(moveOrigin); |
|
380 result = iRegionManager.SetClippingRegion(clippingRegion); |
|
381 } |
|
382 else |
|
383 { |
|
384 result = KErrNoMemory; |
|
385 } |
|
386 |
|
387 if(result != KErrNone) |
|
388 { |
|
389 iDriver.SetError(result); |
|
390 } |
|
391 |
|
392 clippingRegion.Close(); |
|
393 |
|
394 iDrawOrigin = aDrawOrigin; |
|
395 return result; |
|
396 } |
|
397 |
|
398 /** |
|
399 @see MDrawDeviceOrigin::Get() |
|
400 */ |
|
401 void CVgEngine::Get(TPoint& aDrawOrigin) |
|
402 { |
|
403 aDrawOrigin=iDrawOrigin; |
|
404 } |
|
405 |
|
406 /** |
|
407 @see MDirectGdiEngine::SetClippingRegion(const TRegion&) |
|
408 */ |
|
409 void CVgEngine::SetClippingRegion(const TRegion& aRegion) |
|
410 { |
|
411 TInt result = KErrNone; |
|
412 TRect boundingRect=iTargetRegion.BoundingRect(); |
|
413 boundingRect.iTl-=iDrawOrigin; |
|
414 boundingRect.iBr-=iDrawOrigin; |
|
415 if (!aRegion.IsContainedBy(boundingRect)) |
|
416 { |
|
417 result = KErrArgument; |
|
418 } |
|
419 else |
|
420 { |
|
421 RRegion clippingRegion; |
|
422 clippingRegion.Copy(aRegion); |
|
423 if(!clippingRegion.CheckError()) |
|
424 { |
|
425 clippingRegion.Offset(iDrawOrigin); |
|
426 result = iRegionManager.SetClippingRegion (clippingRegion); |
|
427 } |
|
428 else |
|
429 { |
|
430 result = KErrNoMemory; |
|
431 } |
|
432 clippingRegion.Close(); |
|
433 } |
|
434 |
|
435 if (result != KErrNone) |
|
436 { |
|
437 iDriver.SetError(result); |
|
438 } |
|
439 } |
|
440 |
|
441 /** |
|
442 @see MDirectGdiEngine::ResetClippingRegion() |
|
443 */ |
|
444 void CVgEngine::ResetClippingRegion() |
|
445 { |
|
446 iRegionManager.Reset(); |
|
447 |
|
448 if (!MakeEngineCurrent()) |
|
449 return; |
|
450 |
|
451 vgSeti(VG_SCISSORING, VG_FALSE); |
|
452 } |
|
453 |
|
454 /** |
|
455 @see MDirectGdiEngine::SetDrawMode() |
|
456 |
|
457 The generic layer has already checked whether the draw mode is already aMode. |
|
458 Draw mode is referred to as "blend" mode in OpenVG. |
|
459 Note that only EDrawModePEN and EDrawModeWriteAlpha style blending are supported by OpenVG. |
|
460 The default OpenVG blend mode is VG_BLEND_SRC_OVER. |
|
461 */ |
|
462 void CVgEngine::SetDrawMode(DirectGdi::TDrawMode aMode) |
|
463 { |
|
464 iDrawMode = aMode; |
|
465 |
|
466 if (!MakeEngineCurrent()) |
|
467 return; |
|
468 |
|
469 // Invalid modes are filtered out in the generic layer. |
|
470 if (aMode == DirectGdi::EDrawModePEN) |
|
471 { |
|
472 // Blend the destination with the source using the source alpha for blending if |
|
473 // alpha is available. |
|
474 vgSeti(VG_BLEND_MODE, VG_BLEND_SRC_OVER); |
|
475 } |
|
476 else // DirectGdi::EDrawModeWriteAlpha |
|
477 { |
|
478 // Destination colors and alpha are overwritten with source colors and alpha. |
|
479 vgSeti(VG_BLEND_MODE, VG_BLEND_SRC); |
|
480 } |
|
481 } |
|
482 |
|
483 /** |
|
484 @see MDirectGdiEngine::SetPenColor() |
|
485 */ |
|
486 void CVgEngine::SetPenColor(const TRgb& aColor) |
|
487 { |
|
488 iPenColor = aColor; |
|
489 |
|
490 if (!MakeEngineCurrent()) |
|
491 return; |
|
492 |
|
493 // Make sure our pen is the current selected pen for stroking before we set the color |
|
494 vgSetPaint(iPen, VG_STROKE_PATH); |
|
495 |
|
496 // Set the color |
|
497 vgSetParameteri(iPen, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); |
|
498 SetVgPaintColor(iPen, iPenColor); |
|
499 SetVgPaintColor(iTextBrush, iPenColor); |
|
500 } |
|
501 |
|
502 /** |
|
503 Set the current pen style for drawing lines. This corresponds to setting the "stroke dash" pattern in OpenVG. |
|
504 |
|
505 @see MDirectGdiEngine::SetPenStyle() |
|
506 */ |
|
507 void CVgEngine::SetPenStyle(DirectGdi::TPenStyle aStyle) |
|
508 { |
|
509 iPenStyle = aStyle; |
|
510 |
|
511 if (!MakeEngineCurrent()) |
|
512 return; |
|
513 |
|
514 iPaintMode = iPaintMode | VG_STROKE_PATH; |
|
515 |
|
516 switch (aStyle) |
|
517 { |
|
518 case DirectGdi::ENullPen: |
|
519 { |
|
520 iPaintMode = iPaintMode & (~VG_STROKE_PATH); // Unset stroke bit |
|
521 vgSetfv(VG_STROKE_DASH_PATTERN, 0, NULL); |
|
522 vgSetf(VG_STROKE_DASH_PHASE, 0.f); |
|
523 break; |
|
524 } |
|
525 |
|
526 case DirectGdi::ESolidPen: |
|
527 { |
|
528 vgSetfv(VG_STROKE_DASH_PATTERN, 0, NULL); |
|
529 vgSetf(VG_STROKE_DASH_PHASE, 0.f); |
|
530 break; |
|
531 } |
|
532 |
|
533 case DirectGdi::EDottedPen: |
|
534 { |
|
535 VGfloat offset = (iPenSize.iWidth > 1) ? 2.f : 0.f; |
|
536 VGfloat dashPattern[2] = {(1*iPenSize.iWidth)-offset, (3*iPenSize.iWidth)+offset}; |
|
537 vgSetfv(VG_STROKE_DASH_PATTERN, 2, dashPattern); |
|
538 vgSetf(VG_STROKE_DASH_PHASE, 1.f); |
|
539 break; |
|
540 } |
|
541 |
|
542 case DirectGdi::EDashedPen: |
|
543 { |
|
544 VGfloat offset = (iPenSize.iWidth > 1) ? 2.f : 0.f; |
|
545 VGfloat dashPattern[2] = {(3*iPenSize.iWidth)-offset, (3*iPenSize.iWidth)+offset}; |
|
546 vgSetfv(VG_STROKE_DASH_PATTERN, 2, dashPattern); |
|
547 vgSetf(VG_STROKE_DASH_PHASE, (iPenSize.iWidth != 1) ? 1.f : 0.f); |
|
548 break; |
|
549 } |
|
550 |
|
551 case DirectGdi::EDotDashPen: |
|
552 { |
|
553 VGfloat offset = (iPenSize.iWidth > 1) ? 2.f : 0.f; |
|
554 VGfloat dashPattern[4] = {(1*iPenSize.iWidth)-offset, (3*iPenSize.iWidth)+offset, (3*iPenSize.iWidth)-offset, (3*iPenSize.iWidth)+offset}; |
|
555 vgSetfv(VG_STROKE_DASH_PATTERN, 4, dashPattern); |
|
556 vgSetf(VG_STROKE_DASH_PHASE, 1.f); |
|
557 break; |
|
558 } |
|
559 |
|
560 case DirectGdi::EDotDotDashPen: |
|
561 { |
|
562 VGfloat offset = (iPenSize.iWidth > 1) ? 2.f : 0.f; |
|
563 VGfloat dashPattern[6] = {(1*iPenSize.iWidth)-offset, (3*iPenSize.iWidth)+offset, (1*iPenSize.iWidth)-offset, (3*iPenSize.iWidth)+offset, (3*iPenSize.iWidth)-offset, (3*iPenSize.iWidth)+offset}; |
|
564 vgSetfv(VG_STROKE_DASH_PATTERN, 6, dashPattern); |
|
565 vgSetf(VG_STROKE_DASH_PHASE, (iPenSize.iWidth != 1) ? 1.f : 0.f); |
|
566 break; |
|
567 } |
|
568 |
|
569 default: |
|
570 { |
|
571 // Copy BitGdi behaviour here and draw a solid line for any unknown pen style |
|
572 vgSetfv(VG_STROKE_DASH_PATTERN, 0, NULL); |
|
573 vgSetf(VG_STROKE_DASH_PHASE, 0.f); |
|
574 break; |
|
575 } |
|
576 } |
|
577 } |
|
578 |
|
579 |
|
580 /** |
|
581 @see MDirectGdiEngine::SetPenSize() |
|
582 */ |
|
583 void CVgEngine::SetPenSize(const TSize& aSize) |
|
584 { |
|
585 iPenSize = aSize; |
|
586 |
|
587 if (!MakeEngineCurrent()) |
|
588 return; |
|
589 |
|
590 // VG_STROKE_LINE_WIDTH expects a float |
|
591 // Note that we set the pen size using just the width in the assumption that the pen width |
|
592 // and height are normally the same. For the special cases where the pen width and height |
|
593 // are different, the pen size is set to (1,1) then scaled to give the effect of a pen with |
|
594 // different width and height. This is done for all functions in this file that draws shapes, |
|
595 // see Plot(), DrawLine(), DrawArc(), DrawRect() etc. |
|
596 vgSetf(VG_STROKE_LINE_WIDTH, aSize.iWidth); |
|
597 |
|
598 // Re-set the pen style as the pen size has changed, SetPenStyle() uses the pen size to set |
|
599 // the dotted line styles. |
|
600 SetPenStyle(iPenStyle); |
|
601 } |
|
602 |
|
603 /** |
|
604 @see MDirectGdiEngine::SetTextShadowColor() |
|
605 */ |
|
606 void CVgEngine::SetTextShadowColor(const TRgb& aColor) |
|
607 { |
|
608 iTextShadowColor = aColor; //just cache this color |
|
609 } |
|
610 |
|
611 |
|
612 /** |
|
613 @see MDirectGdiEngine::SetBrushColor() |
|
614 */ |
|
615 void CVgEngine::SetBrushColor(const TRgb& aColor) |
|
616 { |
|
617 iBrushColor = aColor; |
|
618 |
|
619 if (!MakeEngineCurrent()) |
|
620 return; |
|
621 |
|
622 // Convert the color components as that they are between 0.0f and 1.0f |
|
623 VGfloat color[4]; |
|
624 color[0] = aColor.Red() * KColorConversion; |
|
625 color[1] = aColor.Green() * KColorConversion; |
|
626 color[2] = aColor.Blue() * KColorConversion; |
|
627 color[3] = aColor.Alpha() * KColorConversion; |
|
628 |
|
629 // Make sure our brush is the current selected brush for filling before we set the color |
|
630 if (iBrushStyle != DirectGdi::ENullBrush) |
|
631 vgSetPaint(iBrush, VG_FILL_PATH); |
|
632 |
|
633 vgSetParameteri(iBrush, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); |
|
634 vgSetParameterfv(iBrush, VG_PAINT_COLOR, 4, color); |
|
635 |
|
636 // Set the clear color and the tile fill color as well as these should both |
|
637 // be the same as the brush color |
|
638 vgSetParameterfv(iClearBrush, VG_PAINT_COLOR, 4, color); |
|
639 vgSetfv(VG_CLEAR_COLOR, 4, color); |
|
640 vgSetfv(VG_TILE_FILL_COLOR, 4, color); |
|
641 |
|
642 // If using a patterned brush, need to reconstruct it as the colours may be out of sync. |
|
643 if ((iBrushStyle != DirectGdi::ENullBrush) && (iBrushStyle != DirectGdi::ESolidBrush)) |
|
644 { |
|
645 SetBrushStyle(iBrushStyle); |
|
646 } |
|
647 } |
|
648 |
|
649 |
|
650 /** |
|
651 The DirectGDI brush styles do not map directly to OpenVG, so brushes for styles > DirectGdi::EPatternedBrush |
|
652 are created as bitmaps before being set. |
|
653 |
|
654 @see MDirectGdiEngine::SetBrushColor() |
|
655 @see CreateStandardBrush() |
|
656 */ |
|
657 void CVgEngine::SetBrushStyle(DirectGdi::TBrushStyle aStyle) |
|
658 { |
|
659 iBrushStyle = aStyle; |
|
660 |
|
661 if (!MakeEngineCurrent()) |
|
662 return; |
|
663 |
|
664 TInt standardBrushErr = KErrNone; |
|
665 const TInt standardBrushSize = 3; |
|
666 const TInt standardBrushArea = standardBrushSize*standardBrushSize; |
|
667 const TInt diamondCrossHatchBrushSize = 4; |
|
668 const TInt diamondCrossHatchBrushArea = diamondCrossHatchBrushSize*diamondCrossHatchBrushSize; |
|
669 |
|
670 // Select the brush for drawing any style that is not ENullBrush |
|
671 iPaintMode = iPaintMode | VG_FILL_PATH; |
|
672 if (aStyle != DirectGdi::ENullBrush) |
|
673 vgSetPaint(iBrush, VG_FILL_PATH); |
|
674 |
|
675 // Paint using a pattern for all styles that are not ENullBrush or ESolidBrush |
|
676 if ((aStyle != DirectGdi::ENullBrush) && (aStyle != DirectGdi::ESolidBrush)) |
|
677 { |
|
678 vgSetParameteri(iBrush, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN); |
|
679 vgSetParameteri(iBrush, VG_PAINT_PATTERN_TILING_MODE, VG_TILE_REPEAT); |
|
680 } |
|
681 |
|
682 switch (aStyle) |
|
683 { |
|
684 case DirectGdi::ENullBrush: |
|
685 iPaintMode = iPaintMode & (~VG_FILL_PATH); |
|
686 |
|
687 // Clear the current brush so that no brush color is drawn |
|
688 vgSetPaint(VG_INVALID_HANDLE, VG_FILL_PATH); |
|
689 break; |
|
690 |
|
691 case DirectGdi::ESolidBrush: |
|
692 // Paint using a solid color |
|
693 vgSetParameteri(iBrush, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); |
|
694 break; |
|
695 |
|
696 case DirectGdi::EPatternedBrush: |
|
697 // Make sure the user has set a patterned brush for use |
|
698 GRAPHICS_ASSERT_ALWAYS(iBrushPatternUser != VG_INVALID_HANDLE, EDirectGdiPanicPatternedBrushNotSet); |
|
699 |
|
700 if (NonZeroBrushPatternOrigin()) |
|
701 { |
|
702 // The brush origin is non-zero, update the non-zero origin brush |
|
703 // with the user brush and use that instead of the user brush |
|
704 CopyCurrentBrushPatternForNonZeroOrigin(); |
|
705 } |
|
706 else |
|
707 { |
|
708 // Set the brush to fill with the user selected bitmap pattern |
|
709 vgPaintPattern(iBrush, iBrushPatternUser); |
|
710 } |
|
711 break; |
|
712 |
|
713 case DirectGdi::EVerticalHatchBrush: |
|
714 { |
|
715 // The brush fills with vertical hatching lines going from top to bottom |
|
716 TSize size(standardBrushSize, standardBrushSize); |
|
717 VGbyte brushPattern[standardBrushArea] = {0,0,1, 0,0,1, 0,0,1}; |
|
718 standardBrushErr = CreateStandardBrush(size, brushPattern); |
|
719 break; |
|
720 } |
|
721 |
|
722 case DirectGdi::EForwardDiagonalHatchBrush: |
|
723 { |
|
724 // The brush fills with diagonal hatching lines going from bottom left to top right |
|
725 TSize size(standardBrushSize, standardBrushSize); |
|
726 VGbyte brushPattern[standardBrushArea] = {1,0,0, 0,0,1, 0,1,0}; |
|
727 standardBrushErr = CreateStandardBrush(size, brushPattern); |
|
728 break; |
|
729 } |
|
730 |
|
731 case DirectGdi::EHorizontalHatchBrush: |
|
732 { |
|
733 // The brush fills with horizontal hatching lines going from left to right |
|
734 TSize size(standardBrushSize, standardBrushSize); |
|
735 VGbyte brushPattern[standardBrushArea] = {0,0,0, 0,0,0, 1,1,1}; |
|
736 standardBrushErr = CreateStandardBrush(size, brushPattern); |
|
737 break; |
|
738 } |
|
739 |
|
740 case DirectGdi::ERearwardDiagonalHatchBrush: |
|
741 { |
|
742 // The brush fills with rearward diagonal hatching lines going from top left to bottom right |
|
743 TSize size(standardBrushSize, standardBrushSize); |
|
744 VGbyte brushPattern[standardBrushArea] = {1,0,0, 0,1,0, 0,0,1}; |
|
745 standardBrushErr = CreateStandardBrush(size, brushPattern); |
|
746 break; |
|
747 } |
|
748 |
|
749 case DirectGdi::ESquareCrossHatchBrush: |
|
750 { |
|
751 // The brush fills with horizontal and vertical hatching lines going from left to right |
|
752 // plus lines going from top to bottom giving the effect of a grid of small squares |
|
753 TSize size(standardBrushSize, standardBrushSize); |
|
754 VGbyte brushPattern[standardBrushArea] = {0,0,1, 0,0,1, 1,1,1}; |
|
755 standardBrushErr = CreateStandardBrush(size, brushPattern); |
|
756 break; |
|
757 } |
|
758 |
|
759 case DirectGdi::EDiamondCrossHatchBrush: |
|
760 { |
|
761 // The brush fills with forward diagonal and rearward diagonal hatching lines going from |
|
762 // bottom left to top right plus lines going from top left to bottom right giving the effect |
|
763 // of a grid of small diamonds |
|
764 // The brush fills with diagonal hatching lines going from bottom left to top right |
|
765 TSize size(diamondCrossHatchBrushSize, diamondCrossHatchBrushSize); |
|
766 VGbyte brushPattern[diamondCrossHatchBrushArea] = {0,0,1,0, 0,1,0,1, 1,0,0,0, 0,1,0,1}; |
|
767 standardBrushErr = CreateStandardBrush(size, brushPattern); |
|
768 break; |
|
769 } |
|
770 } |
|
771 |
|
772 // Select the standard brush for all styles > EPatternedBrush |
|
773 if (aStyle > DirectGdi::EPatternedBrush) |
|
774 { |
|
775 if (standardBrushErr == KErrNone) |
|
776 { |
|
777 if (NonZeroBrushPatternOrigin()) |
|
778 { |
|
779 // The brush origin is non-zero, update the non-zero origin brush |
|
780 // with the standard brush and use that instead of the standard brush |
|
781 CopyCurrentBrushPatternForNonZeroOrigin(); |
|
782 } |
|
783 else |
|
784 { |
|
785 // Use the standard brush region |
|
786 vgPaintPattern(iBrush, iBrushPatternStandardRegion); |
|
787 } |
|
788 } |
|
789 else |
|
790 { |
|
791 iDriver.SetError(standardBrushErr); |
|
792 } |
|
793 } |
|
794 else |
|
795 { |
|
796 vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL); |
|
797 } |
|
798 } |
|
799 |
|
800 /** |
|
801 @see MDirectGdiEngine::SetBrushOrigin() |
|
802 */ |
|
803 void CVgEngine::SetBrushOrigin(const TPoint& aOrigin) |
|
804 { |
|
805 iBrushOrigin = aOrigin; |
|
806 if (NonZeroBrushPatternOrigin()) |
|
807 { |
|
808 // Copy the current brush pattern into iBrushPatternNonZeroOrigin, but shift it to |
|
809 // take into account the current non-zero brush origin. |
|
810 CopyCurrentBrushPatternForNonZeroOrigin(); |
|
811 } |
|
812 } |
|
813 |
|
814 |
|
815 /** |
|
816 @see MDirectGdiEngine::SetBrushPattern() |
|
817 */ |
|
818 TInt CVgEngine::SetBrushPattern(const CFbsBitmap& aPattern) |
|
819 { |
|
820 if (aPattern.ExtendedBitmapType() != KNullUid) |
|
821 { |
|
822 return KErrNotSupported; // Not supported for extended bitmaps |
|
823 } |
|
824 |
|
825 // Destroy any previously set brush pattern |
|
826 MakeEngineCurrent(); |
|
827 ResetBrushPattern(); |
|
828 iBrushPatternUser = CreateSourceVGImage(aPattern); |
|
829 if (iBrushPatternUser == VG_INVALID_HANDLE) |
|
830 { |
|
831 return KErrNoMemory; |
|
832 } |
|
833 |
|
834 iBrushPatternUserSize = aPattern.SizeInPixels(); |
|
835 iBrushPatternUserBitmapHandle = aPattern.Handle(); |
|
836 return KErrNone; |
|
837 } |
|
838 |
|
839 |
|
840 /** |
|
841 @see MDirectGdiEngine::ResetBrushPattern() |
|
842 */ |
|
843 void CVgEngine::ResetBrushPattern() |
|
844 { |
|
845 MakeEngineCurrent(); |
|
846 if (iBrushPatternUser != VG_INVALID_HANDLE) |
|
847 { |
|
848 vgDestroyImage(iBrushPatternUser); |
|
849 iBrushPatternUser = VG_INVALID_HANDLE; |
|
850 iBrushPatternUserBitmapHandle = KNullHandle; |
|
851 iBrushPatternUserSize = TSize(0,0); |
|
852 } |
|
853 } |
|
854 |
|
855 /** |
|
856 @see MDirectGdiEngine::SetFont() |
|
857 */ |
|
858 void CVgEngine::SetFont(TUint32 aFontId) |
|
859 { |
|
860 iFontId = aFontId; |
|
861 } |
|
862 |
|
863 |
|
864 /** |
|
865 @see MDirectGdiEngine::ResetFont() |
|
866 */ |
|
867 void CVgEngine::ResetFont() |
|
868 { |
|
869 iFontId = 0; |
|
870 } |
|
871 |
|
872 /** |
|
873 Reset all the VgEngine-specific settings. Generic settings such as paint colour and pen colour |
|
874 are set by calls from the CDirectGdiContext. |
|
875 |
|
876 @see MDirectGdiEngine::Reset() |
|
877 |
|
878 @post All VgEngine-specific settings have been reset to their default values. |
|
879 */ |
|
880 void CVgEngine::Reset() |
|
881 { |
|
882 if (!MakeEngineCurrent()) |
|
883 return; |
|
884 |
|
885 ResetVgMatrix(); |
|
886 } |
|
887 |
|
888 |
|
889 |
|
890 /** |
|
891 @see MDirectGdiEngine::Clear(const TRect&) |
|
892 |
|
893 @panic DGDIAdapter 62, if the brush for clearing is not valid (debug-only). |
|
894 */ |
|
895 void CVgEngine::Clear(const TRect& aRect) |
|
896 { |
|
897 MakeEngineCurrent(); |
|
898 |
|
899 if (255 == iBrushColor.Alpha()) |
|
900 { |
|
901 const TPoint rectOrigin = ConvertToVgCoords(aRect); |
|
902 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
903 vgClear(rectOrigin.iX, rectOrigin.iY, aRect.Width(), aRect.Height()); |
|
904 } |
|
905 else |
|
906 { |
|
907 // If blending is enabled, we cannot use vgClear as it ignores the current blendmode and performs a WriteAlpha. |
|
908 // Therefore a clear is done by a filled rectangle. |
|
909 |
|
910 // The Clear brush should always be a solid brush. |
|
911 GRAPHICS_ASSERT_DEBUG(vgGetParameteri(iClearBrush, VG_PAINT_TYPE) == VG_PAINT_TYPE_COLOR, EDirectGdiPanicClearBrushInvalid); |
|
912 |
|
913 if (PreparePath(iVgPath, 5)) |
|
914 { |
|
915 // Before any vgu command, call SetError() as this stores the current OpenVG error state (if any) |
|
916 // on the driver. Some implementations of vgu clears error state so we'd lose error codes otherwise. |
|
917 iDriver.SetError(KErrNone); |
|
918 |
|
919 VGUErrorCode err = vguRect(iVgPath, aRect.iTl.iX, aRect.iTl.iY, aRect.Width(), aRect.Height()); |
|
920 if (err == VGU_NO_ERROR) |
|
921 { |
|
922 // Temporarily set the brush to the clear brush and fill the path. |
|
923 vgSetPaint(iClearBrush, VG_FILL_PATH); |
|
924 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
925 vgDrawPath(iVgPath, VG_FILL_PATH); |
|
926 vgSetPaint(iBrush, VG_FILL_PATH); |
|
927 } |
|
928 else |
|
929 { |
|
930 SetVguError(err); |
|
931 } |
|
932 } |
|
933 } |
|
934 } |
|
935 |
|
936 /** |
|
937 @see MDirectGdiEngine::Clear() |
|
938 */ |
|
939 void CVgEngine::Clear() |
|
940 { |
|
941 Clear(TRect(iSize)); |
|
942 } |
|
943 |
|
944 /** |
|
945 @see MDirectGdiEngine::MoveTo() |
|
946 */ |
|
947 void CVgEngine::MoveTo(const TPoint& aPoint) |
|
948 { |
|
949 iLinePos = aPoint; |
|
950 } |
|
951 |
|
952 /** |
|
953 @see MDirectGdiEngine::MoveBy() |
|
954 */ |
|
955 void CVgEngine::MoveBy(const TPoint& aVector) |
|
956 { |
|
957 iLinePos += aVector; |
|
958 } |
|
959 |
|
960 /** |
|
961 @see MDirectGdiEngine::Plot() |
|
962 */ |
|
963 void CVgEngine::Plot(const TPoint& aPoint) |
|
964 { |
|
965 MakeEngineCurrent(); |
|
966 GRAPHICS_ASSERT_DEBUG(vgGeti(VG_STROKE_CAP_STYLE) == VG_CAP_ROUND, EDirectGdiPanicPenEndCapStyleNotRound); |
|
967 |
|
968 |
|
969 // If the pen width and height are equal just plot as normal. |
|
970 if (iPenSize.iWidth == iPenSize.iHeight) |
|
971 { |
|
972 if (PreparePath(iVgPath, 2)) |
|
973 { |
|
974 // A point is plotted by drawing a line with start and end points the same. |
|
975 AppendPathCommand(VG_MOVE_TO_ABS, aPoint.iX + 0.5f, aPoint.iY + 0.5f); |
|
976 AppendPathCommand(VG_HLINE_TO_REL, 0.f); |
|
977 FinishPath(iVgPath); |
|
978 |
|
979 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
980 vgDrawPath(iVgPath, iPaintMode&VG_STROKE_PATH); |
|
981 } |
|
982 } |
|
983 else if ((iPenSize.iWidth != 0) && (iPenSize.iHeight != 0)) |
|
984 { |
|
985 // Setting a pen with different width and height is not available on OpenVG, so we need to scale |
|
986 // the coordinates we are drawing and apply a scaling matrix that scales by the width and height |
|
987 // of the pen to get the effect of a pen width different width and height. |
|
988 if (PreparePath(iVgPath, 2)) |
|
989 { |
|
990 TSize penSize = iPenSize; |
|
991 SetPenSize(TSize(1, 1)); |
|
992 vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); |
|
993 vgScale(penSize.iWidth, penSize.iHeight); |
|
994 |
|
995 // A point is plotted by drawing a line with start and end points the same. |
|
996 AppendPathCommand(VG_MOVE_TO_ABS, (aPoint.iX + 0.5f)/(float)penSize.iWidth, (aPoint.iY + 0.5f)/(float)penSize.iHeight); |
|
997 AppendPathCommand(VG_HLINE_TO_REL, 0.f); |
|
998 FinishPath(iVgPath); |
|
999 |
|
1000 vgDrawPath(iVgPath, iPaintMode&VG_STROKE_PATH); |
|
1001 |
|
1002 ResetVgMatrix(VG_MATRIX_PATH_USER_TO_SURFACE); |
|
1003 SetPenSize(penSize); |
|
1004 } |
|
1005 } |
|
1006 } |
|
1007 |
|
1008 /** |
|
1009 @see MDirectGdiEngine::DrawLine() |
|
1010 */ |
|
1011 void CVgEngine::DrawLine(const TPoint& aStart, const TPoint& aEnd) |
|
1012 { |
|
1013 MakeEngineCurrent(); |
|
1014 GRAPHICS_ASSERT_DEBUG(vgGeti(VG_STROKE_CAP_STYLE) == VG_CAP_ROUND, EDirectGdiPanicPenEndCapStyleNotRound); |
|
1015 |
|
1016 if (iPaintMode == 0) |
|
1017 { |
|
1018 return; |
|
1019 } |
|
1020 |
|
1021 if (PreparePath(iVgPath, 2)) |
|
1022 { |
|
1023 // If the pen width and height are the same then draw as normal |
|
1024 if (iPenSize.iWidth == iPenSize.iHeight) |
|
1025 { |
|
1026 // 0.5 is appended to all OpenVG drawing co-ordinates as when specifying them, the spec says |
|
1027 // co-ordinates are relative to pixel boundaries, not pixel centres, so 0,0 is the top left of the |
|
1028 // top pixel. We need to add 0.5 to specify the centre of pixels. |
|
1029 |
|
1030 AppendPathCommand(VG_MOVE_TO_ABS, aStart.iX + 0.5f, aStart.iY + 0.5f); |
|
1031 if (aStart.iX == aEnd.iX) |
|
1032 { |
|
1033 AppendPathCommand(VG_VLINE_TO_ABS, aEnd.iY + 0.5f); |
|
1034 } |
|
1035 else if (aStart.iY == aEnd.iY) |
|
1036 { |
|
1037 AppendPathCommand(VG_HLINE_TO_ABS, aEnd.iX + 0.5f); |
|
1038 } |
|
1039 else |
|
1040 { |
|
1041 AppendPathCommand(VG_LINE_TO_ABS, aEnd.iX + 0.5f, aEnd.iY + 0.5f); |
|
1042 } |
|
1043 FinishPath(iVgPath); |
|
1044 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
1045 vgDrawPath(iVgPath, iPaintMode); |
|
1046 } |
|
1047 else if ((iPenSize.iWidth != 0) && (iPenSize.iHeight != 0)) |
|
1048 { |
|
1049 // Setting a pen with different width and height is not available on OpenVG, so we need to scale |
|
1050 // the coordinates we are drawing and apply a scaling matrix that scales by the width and height |
|
1051 // of the pen to get the effect of a pen width different width and height. |
|
1052 TSize penSize = iPenSize; |
|
1053 SetPenSize(TSize(1, 1)); |
|
1054 vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); |
|
1055 vgScale(penSize.iWidth, penSize.iHeight); |
|
1056 |
|
1057 VGfloat scaleX = 1.0f/(float)penSize.iWidth; |
|
1058 VGfloat scaleY = 1.0f/(float)penSize.iHeight; |
|
1059 AppendPathCommand(VG_MOVE_TO_ABS, (aStart.iX + 0.5f)*scaleX, (aStart.iY + 0.5f)*scaleY); |
|
1060 if (aStart.iX == aEnd.iX) |
|
1061 { |
|
1062 AppendPathCommand(VG_VLINE_TO_ABS, (aEnd.iY + 0.5f)*scaleY); |
|
1063 } |
|
1064 else if (aStart.iY == aEnd.iY) |
|
1065 { |
|
1066 AppendPathCommand(VG_HLINE_TO_ABS, (aEnd.iX + 0.5f)*scaleX); |
|
1067 } |
|
1068 else |
|
1069 { |
|
1070 AppendPathCommand(VG_LINE_TO_ABS, (aEnd.iX + 0.5f)*scaleX, (aEnd.iY + 0.5f)*scaleY); |
|
1071 } |
|
1072 FinishPath(iVgPath); |
|
1073 vgDrawPath(iVgPath, iPaintMode); |
|
1074 |
|
1075 ResetVgMatrix(VG_MATRIX_PATH_USER_TO_SURFACE); |
|
1076 SetPenSize(penSize); |
|
1077 } |
|
1078 } |
|
1079 |
|
1080 iLinePos = aEnd; |
|
1081 } |
|
1082 |
|
1083 /** |
|
1084 @see MDirectGdiEngine::DrawLineTo() |
|
1085 */ |
|
1086 void CVgEngine::DrawLineTo(const TPoint& aPoint) |
|
1087 { |
|
1088 DrawLine(iLinePos, aPoint); |
|
1089 } |
|
1090 |
|
1091 /** |
|
1092 @see MDirectGdiEngine::DrawLineBy() |
|
1093 */ |
|
1094 void CVgEngine::DrawLineBy(const TPoint& aVector) |
|
1095 { |
|
1096 DrawLine(iLinePos, iLinePos + aVector); |
|
1097 } |
|
1098 |
|
1099 /** |
|
1100 @see MDirectGdiEngine::DrawRect() |
|
1101 */ |
|
1102 void CVgEngine::DrawRect(const TRect& aRect) |
|
1103 { |
|
1104 MakeEngineCurrent(); |
|
1105 |
|
1106 // Only draw if we are not painting with a NULL pen and a NULL brush |
|
1107 if (iPaintMode == 0) |
|
1108 return; |
|
1109 |
|
1110 // If the pen size is larger then 1, make sure we are using the ESolidPen |
|
1111 // pen style (to match BitGdi behaviour) |
|
1112 DirectGdi::TPenStyle savedPenStyle = iPenStyle; |
|
1113 if (((iPenSize.iWidth > 1) || (iPenSize.iHeight > 1)) && (iPenStyle > DirectGdi::ESolidPen)) |
|
1114 SetPenStyle(DirectGdi::ESolidPen); |
|
1115 |
|
1116 // Create a copy of the rect. If the pen style is not null, we have to shrink the |
|
1117 // width and height of the rect by one pixel at the bottom left corner for conformance. |
|
1118 TRect copyRect(aRect.iTl.iX, aRect.iTl.iY, aRect.iBr.iX, aRect.iBr.iY-1); |
|
1119 if (iPenStyle != DirectGdi::ENullPen) |
|
1120 { |
|
1121 --copyRect.iBr.iX; |
|
1122 } |
|
1123 else |
|
1124 { |
|
1125 --copyRect.iTl.iY; |
|
1126 } |
|
1127 |
|
1128 const TBool symmetricalPenSize = iPenSize.iWidth == iPenSize.iHeight; |
|
1129 |
|
1130 // If the pen is so thick it covers the entire area of the rect, don't do the fill, as per BitGdi. |
|
1131 const TBool penThickerThanRect = (iPenSize.iWidth >= copyRect.Width()) || (iPenSize.iHeight >= copyRect.Height()); |
|
1132 |
|
1133 // If the pen width and height are the same then draw as normal. If they are different but we should be filling |
|
1134 // this shape we need to draw the filled area only as normal (not the outline). The outline of the shape is drawn |
|
1135 // in the block of code below to allow the effect of a different width and height pen to be applied. |
|
1136 if (!penThickerThanRect || (iPenStyle == DirectGdi::ENullPen)) |
|
1137 { |
|
1138 if (symmetricalPenSize || (iPaintMode & VG_FILL_PATH)) |
|
1139 { |
|
1140 if (PreparePath(iVgPath, 5)) |
|
1141 { |
|
1142 vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_MITER); |
|
1143 AppendPathCommand(VG_MOVE_TO_ABS, copyRect.iTl.iX + 0.5f, copyRect.iTl.iY + 0.5f); |
|
1144 AppendPathCommand(VG_HLINE_TO_ABS, copyRect.iBr.iX + 0.5f); |
|
1145 AppendPathCommand(VG_VLINE_TO_ABS, copyRect.iBr.iY + 0.5f); |
|
1146 AppendPathCommand(VG_HLINE_TO_ABS, copyRect.iTl.iX + 0.5f); |
|
1147 AppendPathCommand(VG_CLOSE_PATH); |
|
1148 FinishPath(iVgPath); |
|
1149 |
|
1150 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
1151 vgDrawPath(iVgPath, symmetricalPenSize ? iPaintMode : VG_FILL_PATH); |
|
1152 vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND); |
|
1153 } |
|
1154 } |
|
1155 } |
|
1156 if((penThickerThanRect && (iPenStyle != DirectGdi::ENullPen)) || //we shouldn't draw if pen style is null |
|
1157 (!symmetricalPenSize && (iPaintMode & VG_STROKE_PATH) && (iPenSize.iWidth != 0) && (iPenSize.iHeight != 0))) |
|
1158 { |
|
1159 // Setting a pen with different width and height is not available on OpenVG, so we need to scale |
|
1160 // the coordinates we are drawing and apply a scaling matrix that scales by the width and height |
|
1161 // of the pen to get the effect of a pen width different width and height. |
|
1162 TSize penSize = iPenSize; |
|
1163 SetPenSize(TSize(1, 1)); |
|
1164 vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); |
|
1165 vgScale(penSize.iWidth, penSize.iHeight); |
|
1166 |
|
1167 if (PreparePath(iVgPath, 5)) |
|
1168 { |
|
1169 vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_MITER); |
|
1170 VGfloat scaleX = 1.0f/(float)penSize.iWidth; |
|
1171 VGfloat scaleY = 1.0f/(float)penSize.iHeight; |
|
1172 AppendPathCommand(VG_MOVE_TO_ABS, (copyRect.iTl.iX + 0.5f)*scaleX, (copyRect.iTl.iY + 0.5f)*scaleY); |
|
1173 AppendPathCommand(VG_HLINE_TO_ABS, (copyRect.iBr.iX + 0.5f)*scaleX); |
|
1174 AppendPathCommand(VG_VLINE_TO_ABS, (copyRect.iBr.iY + 0.5f)*scaleY); |
|
1175 AppendPathCommand(VG_HLINE_TO_ABS, (copyRect.iTl.iX + 0.5f)*scaleX); |
|
1176 AppendPathCommand(VG_CLOSE_PATH); |
|
1177 FinishPath(iVgPath); |
|
1178 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
1179 vgDrawPath(iVgPath, VG_STROKE_PATH); |
|
1180 vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND); |
|
1181 } |
|
1182 |
|
1183 ResetVgMatrix(VG_MATRIX_PATH_USER_TO_SURFACE); |
|
1184 SetPenSize(penSize); |
|
1185 } |
|
1186 |
|
1187 // Reset the pen style if we changed it above |
|
1188 if (savedPenStyle != iPenStyle) |
|
1189 SetPenStyle(savedPenStyle); |
|
1190 } |
|
1191 |
|
1192 /** |
|
1193 @see MDirectGdiEngine::DrawRoundRect() |
|
1194 */ |
|
1195 void CVgEngine::DrawRoundRect(const TRect& aRect, const TSize& aCornerSize) |
|
1196 { |
|
1197 MakeEngineCurrent(); |
|
1198 |
|
1199 // Only draw if we are not painting with a NULL pen and a NULL brush |
|
1200 if (iPaintMode == 0) |
|
1201 return; |
|
1202 |
|
1203 // Create a copy of the rect. If the pen style is not null, we have to shrink the |
|
1204 // width and height of the rect by one pixel at the bottom left corner for conformance. |
|
1205 TRect copyRect(aRect.iTl.iX, aRect.iTl.iY, aRect.iBr.iX, aRect.iBr.iY-1); |
|
1206 if (iPenStyle != DirectGdi::ENullPen) |
|
1207 { |
|
1208 --copyRect.iBr.iX; |
|
1209 } |
|
1210 //If the penstyle is null and brush style is not null, then reduce the width and height by |
|
1211 //two pixels for conformation. |
|
1212 else if(iBrushStyle != DirectGdi::ENullBrush) |
|
1213 { |
|
1214 ----copyRect.iBr.iX; |
|
1215 --copyRect.iBr.iY; |
|
1216 } |
|
1217 else if(iPenStyle == DirectGdi::ENullPen) |
|
1218 { |
|
1219 --copyRect.iTl.iY; |
|
1220 } |
|
1221 |
|
1222 // check that the corner size is less than the rectangle size |
|
1223 if ((aRect.Width() > aCornerSize.iWidth) || (aRect.Height() > aCornerSize.iHeight)) |
|
1224 { |
|
1225 // Before any vgu command, call SetError() as this stores the current vg error state (if any) in the |
|
1226 // driver. Some implementations of vgu clears error state so we'd lose error codes otherwise. |
|
1227 iDriver.SetError(KErrNone); |
|
1228 |
|
1229 // If the pen width and height are the same then draw as normal. If they are different but we should be filling |
|
1230 // this shape we need to draw the filled area only as normal (not the outline). The outline of the shape is drawn |
|
1231 // in the block of code below to allow the effect of a different width and height pen to be applied. |
|
1232 if ((iPenSize.iWidth == iPenSize.iHeight) || (iPaintMode & VG_FILL_PATH)) |
|
1233 { |
|
1234 if (PreparePath(iVgPath, 10)) |
|
1235 { |
|
1236 VGUErrorCode err = vguRoundRect(iVgPath, |
|
1237 copyRect.iTl.iX + 0.5f, |
|
1238 copyRect.iTl.iY + 0.5f, |
|
1239 copyRect.Width(), |
|
1240 copyRect.Height(), |
|
1241 aCornerSize.iWidth * 2, |
|
1242 aCornerSize.iHeight * 2); |
|
1243 |
|
1244 if (err == VGU_NO_ERROR) |
|
1245 { |
|
1246 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
1247 vgDrawPath(iVgPath, (iPenSize.iWidth == iPenSize.iHeight) ? iPaintMode : VG_FILL_PATH); |
|
1248 } |
|
1249 else |
|
1250 { |
|
1251 SetVguError(err); |
|
1252 } |
|
1253 } |
|
1254 } |
|
1255 |
|
1256 if ((iPenSize.iWidth != iPenSize.iHeight) |
|
1257 && (iPaintMode & VG_STROKE_PATH) |
|
1258 && (iPenSize.iWidth != 0) && (iPenSize.iHeight != 0)) |
|
1259 { |
|
1260 // Setting a pen with different width and height is not available on OpenVG, so we need to scale |
|
1261 // the coordinates we are drawing and apply a scaling matrix that scales by the width and height |
|
1262 // of the pen to get the effect of a pen width different width and height. |
|
1263 TSize penSize = iPenSize; |
|
1264 SetPenSize(TSize(1, 1)); |
|
1265 vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); |
|
1266 vgScale(penSize.iWidth, penSize.iHeight); |
|
1267 |
|
1268 if (PreparePath(iVgPath, 10)) |
|
1269 { |
|
1270 VGfloat scaleX = 1.0f/(float)penSize.iWidth; |
|
1271 VGfloat scaleY = 1.0f/(float)penSize.iHeight; |
|
1272 VGUErrorCode err = vguRoundRect(iVgPath, |
|
1273 (copyRect.iTl.iX + 0.5f) * scaleX, |
|
1274 (copyRect.iTl.iY + 0.5f) * scaleY, |
|
1275 copyRect.Width() * scaleX, |
|
1276 copyRect.Height() * scaleY, |
|
1277 (aCornerSize.iWidth * 2) * scaleX, |
|
1278 (aCornerSize.iHeight * 2) * scaleY); |
|
1279 if (err == VGU_NO_ERROR) |
|
1280 { |
|
1281 vgDrawPath(iVgPath, VG_STROKE_PATH); |
|
1282 } |
|
1283 else |
|
1284 { |
|
1285 SetVguError(err); |
|
1286 } |
|
1287 } |
|
1288 |
|
1289 ResetVgMatrix(VG_MATRIX_PATH_USER_TO_SURFACE); |
|
1290 SetPenSize(penSize); |
|
1291 } |
|
1292 } |
|
1293 else |
|
1294 { |
|
1295 // Draw an ellipse as the corner size is greater than or equal to the rectangle size |
|
1296 DrawEllipse(copyRect); |
|
1297 } |
|
1298 } |
|
1299 |
|
1300 /** |
|
1301 @see MDirectGdiEngine::DrawPolyLine() |
|
1302 |
|
1303 @panic DGDIAdapter 27, if the passed point list has zero points (debug-only). |
|
1304 */ |
|
1305 void CVgEngine::DrawPolyLine(const TArray<TPoint>& aPointList) |
|
1306 { |
|
1307 DrawPolyLineNoEndPoint(aPointList); |
|
1308 |
|
1309 // Do a plot at the end point to improve the appearance. For larger pen-sizes, a plot |
|
1310 // improves the correlation with BitGDI polylines, giving a subtly more rounded finish. |
|
1311 if (DirectGdi::ESolidPen == iPenStyle) |
|
1312 { |
|
1313 Plot(iLinePos); |
|
1314 } |
|
1315 } |
|
1316 |
|
1317 /** |
|
1318 @see MDirectGdiEngine::DrawPolyLineNoEndPoint() |
|
1319 |
|
1320 @panic DGDIAdapter 27, if the passed point list has zero points (debug-only). |
|
1321 */ |
|
1322 void CVgEngine::DrawPolyLineNoEndPoint(const TArray<TPoint>& aPointList) |
|
1323 { |
|
1324 MakeEngineCurrent(); |
|
1325 |
|
1326 GRAPHICS_ASSERT_DEBUG(aPointList.Count() > 0, EDirectGdiPanicInvalidPointArray); |
|
1327 |
|
1328 DoDrawPoly(aPointList, EFalse); |
|
1329 } |
|
1330 |
|
1331 /** |
|
1332 @see MDirectGdiEngine::DrawPolygon() |
|
1333 |
|
1334 @panic DGDIAdapter 26, if the passed fill-rule is invalid (debug-only). |
|
1335 */ |
|
1336 void CVgEngine::DrawPolygon(const TArray<TPoint>& aPoints, DirectGdi::TFillRule aRule) |
|
1337 { |
|
1338 MakeEngineCurrent(); |
|
1339 |
|
1340 GRAPHICS_ASSERT_DEBUG(aPoints.Count() > 0, EDirectGdiPanicInvalidPointArray); |
|
1341 |
|
1342 switch(aRule) |
|
1343 { |
|
1344 case DirectGdi::EAlternate: |
|
1345 vgSeti(VG_FILL_RULE, VG_EVEN_ODD); |
|
1346 break; |
|
1347 case DirectGdi::EWinding: |
|
1348 vgSeti(VG_FILL_RULE, VG_NON_ZERO); |
|
1349 break; |
|
1350 default: |
|
1351 GRAPHICS_PANIC_DEBUG(EDirectGdiPanicInvalidFillRule); |
|
1352 return; |
|
1353 }; |
|
1354 |
|
1355 DoDrawPoly(aPoints, ETrue); |
|
1356 } |
|
1357 |
|
1358 /** |
|
1359 Helper function to assist with drawing polygons with DrawPolygon()/DrawPolyLine(). It takes care of |
|
1360 drawing the array of points given to it. It sets the internal drawing poisition to the last TPoint |
|
1361 in the array. |
|
1362 |
|
1363 @see DrawPolyLine() |
|
1364 @see DrawPolygon() |
|
1365 |
|
1366 @param aPoints Array of points specifying the vertices of the polygon. There must be at least one |
|
1367 vertex. |
|
1368 @param aClosed If ETrue, the start and end points are joined, and the polygon filled using current |
|
1369 brush settings, otherwise just a polyline is drawn. |
|
1370 */ |
|
1371 void CVgEngine::DoDrawPoly(const TArray<TPoint>& aPoints, TBool aClosed) |
|
1372 { |
|
1373 GRAPHICS_ASSERT_DEBUG(aPoints.Count() > 0, EDirectGdiPanicInvalidPointArray); |
|
1374 const TInt numPoints = aPoints.Count(); |
|
1375 |
|
1376 // Set drawing position to last point in the array (regardless of whether the poly is open/closed) |
|
1377 iLinePos = aPoints[numPoints - 1]; |
|
1378 |
|
1379 // Set the paint mode depending on whether we are drawing a line (not closed) or a poly (closed) |
|
1380 VGbitfield paintMode = iPaintMode; |
|
1381 if (!aClosed) |
|
1382 { |
|
1383 paintMode &= ~VG_FILL_PATH; |
|
1384 } |
|
1385 |
|
1386 if (!paintMode) |
|
1387 { |
|
1388 return; |
|
1389 } |
|
1390 |
|
1391 // If the pen width and height are the same then draw as normal. If they are different but we should be filling |
|
1392 // this shape we need to draw the filled area only as normal (not the outline). The outline of the shape is drawn |
|
1393 // in the block of code below to allow the effect of a different width and height pen to be applied. |
|
1394 if ((iPenSize.iWidth == iPenSize.iHeight) || (paintMode & VG_FILL_PATH)) |
|
1395 { |
|
1396 if (PreparePath(iVgPath, aClosed ? numPoints + 1 : numPoints)) |
|
1397 { |
|
1398 AppendPathCommand(VG_MOVE_TO_ABS, aPoints[0].iX + 0.5f, aPoints[0].iY + 0.5f); |
|
1399 for (TInt point = 0; point < numPoints; ++point) |
|
1400 { |
|
1401 AppendPathCommand(VG_LINE_TO_ABS, aPoints[point].iX + 0.5f, aPoints[point].iY + 0.5f); |
|
1402 } |
|
1403 if (aClosed) |
|
1404 { |
|
1405 AppendPathCommand(VG_CLOSE_PATH); |
|
1406 } |
|
1407 FinishPath(iVgPath); |
|
1408 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
1409 vgDrawPath(iVgPath, (iPenSize.iWidth == iPenSize.iHeight) ? paintMode : VG_FILL_PATH); |
|
1410 } |
|
1411 } |
|
1412 |
|
1413 if ((iPenSize.iWidth != iPenSize.iHeight) |
|
1414 && (paintMode & VG_STROKE_PATH) |
|
1415 && (iPenSize.iWidth != 0) && (iPenSize.iHeight != 0)) |
|
1416 { |
|
1417 // Setting a pen with different width and height is not available on OpenVG, so we need to scale |
|
1418 // the coordinates we are drawing and apply a scaling matrix that scales by the width and height |
|
1419 // of the pen to get the effect of a pen width different width and height. |
|
1420 TSize penSize = iPenSize; |
|
1421 SetPenSize(TSize(1, 1)); |
|
1422 vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); |
|
1423 vgScale(penSize.iWidth, penSize.iHeight); |
|
1424 |
|
1425 if (PreparePath(iVgPath, aClosed ? numPoints + 1 : numPoints)) |
|
1426 { |
|
1427 AppendPathCommand(VG_MOVE_TO_ABS, (aPoints[0].iX + 0.5f)/(float)penSize.iWidth, (aPoints[0].iY + 0.5f)/(float)penSize.iHeight); |
|
1428 for (TInt point = 0; point < numPoints; ++point) |
|
1429 { |
|
1430 AppendPathCommand(VG_LINE_TO_ABS, (aPoints[point].iX + 0.5f)/(float)penSize.iWidth, (aPoints[point].iY + 0.5f)/(float)penSize.iHeight); |
|
1431 } |
|
1432 if (aClosed) |
|
1433 { |
|
1434 AppendPathCommand(VG_CLOSE_PATH); |
|
1435 } |
|
1436 FinishPath(iVgPath); |
|
1437 vgDrawPath(iVgPath, VG_STROKE_PATH); |
|
1438 } |
|
1439 |
|
1440 ResetVgMatrix(VG_MATRIX_PATH_USER_TO_SURFACE); |
|
1441 SetPenSize(penSize); |
|
1442 } |
|
1443 |
|
1444 } |
|
1445 |
|
1446 /** |
|
1447 @see MDirectGdiEngine::DrawArc() |
|
1448 @see DrawPie() |
|
1449 @see DrawArc(const TRect&, const TPoint&, const TPoint&, VGUArcType) |
|
1450 */ |
|
1451 void CVgEngine::DrawArc(const TRect& aRect, const TPoint& aStart, const TPoint& aEnd) |
|
1452 { |
|
1453 DoDrawArc(aRect, aStart, aEnd, VGU_ARC_OPEN); |
|
1454 } |
|
1455 |
|
1456 /** |
|
1457 @see MDirectGdiEngine::DrawPie() |
|
1458 @see DrawArc(const TRect&, const TPoint&, const TPoint&) |
|
1459 @see DrawArc(const TRect&, const TPoint&, const TPoint&, VGUArcType) |
|
1460 */ |
|
1461 void CVgEngine::DrawPie(const TRect& aRect, const TPoint& aStart, const TPoint& aEnd) |
|
1462 { |
|
1463 DoDrawArc(aRect, aStart, aEnd, VGU_ARC_PIE); |
|
1464 } |
|
1465 |
|
1466 /** |
|
1467 @see MDirectGdiEngine::DrawEllipse() |
|
1468 */ |
|
1469 void CVgEngine::DrawEllipse(const TRect& aRect) |
|
1470 { |
|
1471 // Null brush and pen, draw nothing. |
|
1472 if (iPaintMode == 0) |
|
1473 return; |
|
1474 |
|
1475 MakeEngineCurrent(); |
|
1476 VGfloat x = (aRect.iTl.iX + aRect.iBr.iX) * 0.5; |
|
1477 VGfloat y = (aRect.iTl.iY + aRect.iBr.iY) * 0.5; |
|
1478 |
|
1479 // Before any vgu command, call SetError() as this stores the current vg error state (if any) in the |
|
1480 // driver. Some implementations of vgu clears error state so we'd lose error codes otherwise. |
|
1481 iDriver.SetError(KErrNone); |
|
1482 |
|
1483 TInt width = aRect.Width(); |
|
1484 TInt height = aRect.Height(); |
|
1485 |
|
1486 //If the penstyle is null and brush style is not null, then reduce the width and height by |
|
1487 //two pixels for conformation. |
|
1488 if(iPenStyle == DirectGdi::ENullPen && iBrushStyle != DirectGdi::ENullBrush) |
|
1489 { |
|
1490 width = aRect.Width() > 2 ? aRect.Width() - 2 : 1; |
|
1491 height = aRect.Height() > 2 ? aRect.Height() - 2 : 1; |
|
1492 } |
|
1493 |
|
1494 // If the pen width and height are the same then draw as normal. If they are different but we should be filling |
|
1495 // this shape we need to draw the filled area only as normal (not the outline). The outline of the shape is drawn |
|
1496 // in the block of code below to allow the effect of a different width and height pen to be applied. |
|
1497 if ((iPenSize.iWidth == iPenSize.iHeight) || (iPaintMode & VG_FILL_PATH)) |
|
1498 { |
|
1499 if (PreparePath(iVgPath, 4)) |
|
1500 { |
|
1501 VGUErrorCode err = vguEllipse(iVgPath, x, y, width, height); |
|
1502 if (err == VGU_NO_ERROR) |
|
1503 { |
|
1504 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
1505 vgDrawPath(iVgPath, (iPenSize.iWidth == iPenSize.iHeight) ? iPaintMode : VG_FILL_PATH); |
|
1506 } |
|
1507 else |
|
1508 { |
|
1509 SetVguError(err); |
|
1510 } |
|
1511 } |
|
1512 } |
|
1513 |
|
1514 if ((iPenSize.iWidth != iPenSize.iHeight) |
|
1515 && (iPaintMode & VG_STROKE_PATH) |
|
1516 && (iPenSize.iWidth != 0) && (iPenSize.iHeight != 0)) |
|
1517 { |
|
1518 // Setting a pen with different width and height is not available on OpenVG, so we need to scale |
|
1519 // the coordinates we are drawing and apply a scaling matrix that scales by the width and height |
|
1520 // of the pen to get the effect of a pen width different width and height. |
|
1521 TSize penSize = iPenSize; |
|
1522 SetPenSize(TSize(1, 1)); |
|
1523 vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); |
|
1524 vgScale(penSize.iWidth, penSize.iHeight); |
|
1525 |
|
1526 VGfloat scaleX = 1.0f/(float)penSize.iWidth; |
|
1527 VGfloat scaleY = 1.0f/(float)penSize.iHeight; |
|
1528 |
|
1529 if (PreparePath(iVgPath, 4)) |
|
1530 { |
|
1531 VGUErrorCode err = vguEllipse(iVgPath, x*scaleX, y*scaleY, (float)width*scaleX, (float)height*scaleY); |
|
1532 if (err == VGU_NO_ERROR) |
|
1533 { |
|
1534 vgDrawPath(iVgPath, VG_STROKE_PATH); |
|
1535 } |
|
1536 else |
|
1537 { |
|
1538 SetVguError(err); |
|
1539 } |
|
1540 } |
|
1541 |
|
1542 ResetVgMatrix(VG_MATRIX_PATH_USER_TO_SURFACE); |
|
1543 SetPenSize(penSize); |
|
1544 } |
|
1545 } |
|
1546 |
|
1547 /** |
|
1548 Given a TDisplayMode, returns the closest TDisplayMode that is pixel-for-pixel-compatible |
|
1549 with an OpenVG format, such that the given TDisplayMode may be converted into the result |
|
1550 without loss of colour information. |
|
1551 |
|
1552 @param aDisplayMode Pixel format to find a match for. |
|
1553 |
|
1554 @return Closest TDisplayMode for which there is a OpenVG-compatible match. |
|
1555 */ |
|
1556 static TDisplayMode ClosestVgCompatibleDisplayMode(TDisplayMode aDisplayMode) |
|
1557 { |
|
1558 switch (aDisplayMode) |
|
1559 { |
|
1560 case EGray2: |
|
1561 case EGray4: |
|
1562 case EGray16: |
|
1563 return EGray256; |
|
1564 |
|
1565 case EColor16: |
|
1566 case EColor256: |
|
1567 case EColor4K: |
|
1568 return EColor64K; |
|
1569 |
|
1570 case EColor16M: |
|
1571 return EColor16MU; |
|
1572 |
|
1573 default: |
|
1574 return aDisplayMode; |
|
1575 } |
|
1576 } |
|
1577 |
|
1578 /** |
|
1579 Converts a CFbsBitmap into a VGImage. |
|
1580 If the CFbsBitmap is not a volatile bitmap, the VGImage created will be stored in the thread-wide |
|
1581 VGImage cache. If the CFbsBitmap has been touched (i.e. its data has been changed since it was last |
|
1582 used), a new VGImage will be created and will replace that currently stored in the cache. An untouched |
|
1583 bitmap will store the created VGImage in the cache upon first use, and on subsequent use (if it is |
|
1584 still untouched), will just retrieve the VGImage stored in the cache. |
|
1585 |
|
1586 @param aBitmap The CFbsBitmap to create a VGImage from. |
|
1587 @param aImage Returns the VGImage created from the CFbsBitmap. |
|
1588 @param aIsMask True if the CFbsBitmap is to be used as a mask. |
|
1589 @param aOrigin Position of the first pixel in the mask bitmap. |
|
1590 |
|
1591 @return ETrue if the VGimage has been stored in the cache, EFalse if not. |
|
1592 */ |
|
1593 TBool CVgEngine::ConvertBitmapToVgImage(const CFbsBitmap& aBitmap, VGImage& aImage, TBool aIsMask, const TPoint& aOrigin) |
|
1594 { |
|
1595 TBool createImage = EFalse; |
|
1596 TBool storeImageInCache = EFalse; |
|
1597 TBool imageCached = EFalse; |
|
1598 // Set the options createImage, storeImageInCache and imageCached depending on |
|
1599 // whether the bitmap is volatile, has been touched since last used, |
|
1600 // and whether it already exists in the cache |
|
1601 if (aBitmap.IsVolatile()) |
|
1602 { |
|
1603 // Source bitmap is volatile and so should not be stored in cache. |
|
1604 // Therefore create image only. |
|
1605 createImage = ETrue; |
|
1606 } |
|
1607 else //bitmap not volatile |
|
1608 { |
|
1609 // Source bitmap has not changed since last used. |
|
1610 // Retrieve from cache the image created from the bitmap and the touchCount of that image. |
|
1611 aImage = iDriver.GetVgImageFromCache(aBitmap, aOrigin); |
|
1612 // If the source bitmap already has an associated VGImage stored in the cache, |
|
1613 // just use that VGImage. Otherwise, need to create a VGImage and add it to the cache. |
|
1614 if (aImage == VG_INVALID_HANDLE) |
|
1615 { |
|
1616 // VGImage not in cache |
|
1617 // Create image, and store in cache |
|
1618 createImage = ETrue; |
|
1619 storeImageInCache = ETrue; |
|
1620 } |
|
1621 else |
|
1622 { |
|
1623 // Image already in cache |
|
1624 imageCached = ETrue; |
|
1625 } |
|
1626 } |
|
1627 |
|
1628 // Create a new VGImage if needed |
|
1629 if (createImage) |
|
1630 { |
|
1631 aImage = CreateSourceVGImage(aBitmap, aIsMask, aOrigin); |
|
1632 // Error set on creation of VGImage if aImage == VG_INVALID_HANDLE. |
|
1633 } |
|
1634 // Store the VGImage in the cache if appropriate |
|
1635 if (storeImageInCache && aImage != VG_INVALID_HANDLE) |
|
1636 { |
|
1637 imageCached = iDriver.AddVgImageToCache(aBitmap, aImage, aOrigin); |
|
1638 } |
|
1639 return imageCached; |
|
1640 } |
|
1641 |
|
1642 |
|
1643 /** |
|
1644 Transforms coordinates for a TRect from Symbian Graphics to OpenVG surface coordinates. |
|
1645 This is required for OpenVG operations which are not subject to user-to-surface |
|
1646 coordinate system transformations. |
|
1647 |
|
1648 OpenVG coordinates locate the BOTTOM LEFT corner of the object relative to an origin |
|
1649 at the BOTTOM LEFT of the rendering area. |
|
1650 |
|
1651 Symbian Graphics coordinates locate the TOP LEFT corner of the object relative to an |
|
1652 origin located at the TOP LEFT of the rendering area. |
|
1653 |
|
1654 @param aCoord Top-left of the rectangle, in Symbian graphics coordinates. |
|
1655 @param aWidth The width of the desired rectangle. |
|
1656 @param aHeight The height of the desired rectangle. |
|
1657 @return A TRect in OpenVG coordinates which describes a rectangle at aCoord with aWidth and aHeight. |
|
1658 */ |
|
1659 TRect CVgEngine::SgMetricsToVgTRect(const TPoint& aCoord, const TInt aWidth, const TInt aHeight) const |
|
1660 { |
|
1661 return TRect (TPoint (aCoord.iX + iOrigin.iX, iSize.iHeight - (aCoord.iY + aHeight) - iOrigin.iY), TSize (aWidth, aHeight)); |
|
1662 } |
|
1663 |
|
1664 /** |
|
1665 @see MDirectGdiEngine::BitBlt() |
|
1666 @see BitBltMasked(const TPoint&, const CFbsBitmap&, const TRect&, const CFbsBitmap&, TBool) |
|
1667 @see BitBltMasked(const TPoint&, const CFbsBitmap&, const TRect&, const CFbsBitmap&, const TPoint&) |
|
1668 */ |
|
1669 void CVgEngine::BitBlt(const TPoint& aDestPos, const CFbsBitmap& aSourceBitmap, const TRect& aSourceRect) |
|
1670 { |
|
1671 if (aSourceBitmap.ExtendedBitmapType() != KNullUid) |
|
1672 { |
|
1673 iDriver.SetError(KErrNotSupported); // Not supported for extended bitmaps |
|
1674 return; |
|
1675 } |
|
1676 |
|
1677 DoVgImageDraw (TRect (aDestPos, aSourceRect.Size()), aSourceBitmap, aSourceRect); |
|
1678 } |
|
1679 |
|
1680 /** |
|
1681 @see MDirectGdiEngine::DrawBitmap() |
|
1682 @see DrawBitmapMasked() |
|
1683 */ |
|
1684 void CVgEngine::DrawBitmap(const TRect& aDestRect, const CFbsBitmap& aSourceBitmap, const TRect& aSourceRect) |
|
1685 { |
|
1686 if (aSourceBitmap.ExtendedBitmapType() != KNullUid) |
|
1687 { |
|
1688 iDriver.SetError(KErrNotSupported); // Not supported for extended bitmaps |
|
1689 return; |
|
1690 } |
|
1691 |
|
1692 DoVgImageDraw (aDestRect, aSourceBitmap, aSourceRect); |
|
1693 } |
|
1694 |
|
1695 |
|
1696 /** |
|
1697 Helper method to perform basic VgDrawImage operations, explictly optimised for the case where |
|
1698 the extents of the source image equal the specified source region. |
|
1699 |
|
1700 @pre aSource image is a valid VG image handle. |
|
1701 @pre Destination position and/or scaling has already been set in OpenVG. |
|
1702 |
|
1703 @param aDestRect Destination rectangle to draw to. |
|
1704 @param aSourceBitmap Source bitmap to draw. |
|
1705 @param aSourceRect Source rectangle to render. |
|
1706 */ |
|
1707 void CVgEngine::DoVgImageDraw (const TRect& aDestRect, const CFbsBitmap& aSourceBitmap, const TRect& aSourceRect) |
|
1708 { |
|
1709 MakeEngineCurrent(); |
|
1710 TRect destRect(aDestRect); |
|
1711 TRect srcRect(aSourceRect); |
|
1712 if (!IntersectsClippingRegion (TRect(iOrigin, destRect.Size()))) |
|
1713 return; |
|
1714 |
|
1715 VGImage sourceImage = VG_INVALID_HANDLE; |
|
1716 TBool imageCached = ConvertBitmapToVgImage(aSourceBitmap, sourceImage); |
|
1717 // Error set on creation of VGImage. |
|
1718 if (sourceImage == VG_INVALID_HANDLE) return; |
|
1719 |
|
1720 vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); |
|
1721 vgTranslate(destRect.iTl.iX, destRect.iTl.iY); |
|
1722 |
|
1723 if (aDestRect.Size() != aSourceRect.Size()) |
|
1724 vgScale((VGfloat)destRect.Width()/aSourceRect.Width(), (VGfloat)destRect.Height()/aSourceRect.Height()); |
|
1725 |
|
1726 if(aSourceBitmap.SizeInPixels() == aSourceRect.Size()) |
|
1727 { |
|
1728 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
1729 vgDrawImage(sourceImage); |
|
1730 } |
|
1731 else |
|
1732 { |
|
1733 VGImage sourceImageRegion = |
|
1734 vgChildImage( |
|
1735 sourceImage, |
|
1736 srcRect.iTl.iX, |
|
1737 srcRect.iTl.iY, |
|
1738 srcRect.Width(), |
|
1739 srcRect.Height()); |
|
1740 |
|
1741 if (sourceImageRegion != VG_INVALID_HANDLE) |
|
1742 { |
|
1743 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
1744 vgDrawImage(sourceImageRegion); |
|
1745 |
|
1746 vgDestroyImage(sourceImageRegion); |
|
1747 } |
|
1748 } |
|
1749 |
|
1750 if (!imageCached) vgDestroyImage (sourceImage); |
|
1751 ResetVgMatrix(VG_MATRIX_IMAGE_USER_TO_SURFACE); // Reset the transform matrix. |
|
1752 } |
|
1753 |
|
1754 /** |
|
1755 @see MDirectGdiEngine::BitBltMasked() |
|
1756 @see BitBltMasked(const TPoint&, const CFbsBitmap&, const TRect&, const CFbsBitmap&, TBool) |
|
1757 @see BitBlt() |
|
1758 */ |
|
1759 void CVgEngine::BitBltMasked( |
|
1760 const TPoint& aDestPos, |
|
1761 const CFbsBitmap& aBitmap, |
|
1762 const TRect& aSourceRect, |
|
1763 const CFbsBitmap& aMask, |
|
1764 const TPoint& aMaskPt) |
|
1765 { |
|
1766 if ((aBitmap.ExtendedBitmapType() != KNullUid) || (aMask.ExtendedBitmapType() != KNullUid)) |
|
1767 { |
|
1768 iDriver.SetError(KErrNotSupported); // Not supported for extended bitmaps |
|
1769 return; |
|
1770 } |
|
1771 |
|
1772 DoBitBltMasked(aDestPos, aBitmap, aSourceRect, aMask, EFalse, aMaskPt); |
|
1773 } |
|
1774 |
|
1775 /** |
|
1776 @see MDirectGdiEngine::BitBlt() |
|
1777 @see BitBltMasked(const TPoint&, const CFbsBitmap&, const TRect&, const CFbsBitmap&, const TPoint&) |
|
1778 @see BitBlt() |
|
1779 */ |
|
1780 void CVgEngine::BitBltMasked( |
|
1781 const TPoint& aDestPos, |
|
1782 const CFbsBitmap& aSourceBitmap, |
|
1783 const TRect& aSourceRect, |
|
1784 const CFbsBitmap& aMaskBitmap, |
|
1785 TBool aInvertMask) |
|
1786 { |
|
1787 if ((aSourceBitmap.ExtendedBitmapType() != KNullUid) || (aMaskBitmap.ExtendedBitmapType() != KNullUid)) |
|
1788 { |
|
1789 iDriver.SetError(KErrNotSupported); // Not supported for extended bitmaps |
|
1790 return; |
|
1791 } |
|
1792 |
|
1793 DoBitBltMasked(aDestPos, aSourceBitmap, aSourceRect, aMaskBitmap, aInvertMask, TPoint(0, 0)); |
|
1794 } |
|
1795 |
|
1796 /** |
|
1797 Helper method for performing BitBltMasked(). |
|
1798 Note that aInvertMask is ignored if aMaskPos is not at 0,0. |
|
1799 |
|
1800 @see CDirectGdiContext::BitBlt(const TPoint& aPoint, const CFbsBitmap& aBitmap, const TRect& aSourceRect); |
|
1801 @see CDirectGdiContext::BitBltMasked(const TPoint&, const CFbsBitmap&,const TRect&, const CFbsBitmap&, const TPoint&); |
|
1802 |
|
1803 @param aDestPos The destination for the top left corner of the transferred bitmap. |
|
1804 It is relative to the top left corner of the destination bitmap, which may be the screen. |
|
1805 @param aSourceBitmap A memory-resident source bitmap. |
|
1806 @param aSourceRect A rectangle defining the piece of the bitmap to be drawn, |
|
1807 with co-ordinates relative to the top left corner of the bitmap. |
|
1808 @param aMaskBitmap Mask bitmap. |
|
1809 @param aInvertMask If EFalse, a source pixel that is masked by a black pixel is not transferred to |
|
1810 the destination rectangle. If ETrue, then a source pixel that is masked by a |
|
1811 white pixel is not transferred to the destination rectangle. If alpha blending |
|
1812 is used instead of masking, this flag is ignored and no inversion takes place. |
|
1813 Note that this parameter is ignored if aMaskPos does not equal TPoint(0,0). |
|
1814 @param aMaskPos The point on the mask bitmap to use as the top left corner |
|
1815 */ |
|
1816 void CVgEngine::DoBitBltMasked ( |
|
1817 const TPoint& aDestPos, |
|
1818 const CFbsBitmap& aSourceBitmap, |
|
1819 const TRect& aSourceRect, |
|
1820 const CFbsBitmap& aMaskBitmap, |
|
1821 TBool aInvertMask, |
|
1822 const TPoint& aMaskPos) |
|
1823 { |
|
1824 MakeEngineCurrent(); |
|
1825 ResetVgMatrix(VG_MATRIX_IMAGE_USER_TO_SURFACE); |
|
1826 if (!IntersectsClippingRegion (TRect (aDestPos+iOrigin, aSourceRect.Size()))) |
|
1827 return; |
|
1828 |
|
1829 VGImage sourceImage = VG_INVALID_HANDLE; |
|
1830 TBool imageCached = ConvertBitmapToVgImage(aSourceBitmap, sourceImage); |
|
1831 if (sourceImage == VG_INVALID_HANDLE) |
|
1832 { |
|
1833 // Error set on creation of VGImage. |
|
1834 return; |
|
1835 } |
|
1836 |
|
1837 VGImage maskImage = VG_INVALID_HANDLE; |
|
1838 TBool maskImageCached = ConvertBitmapToVgImage(aMaskBitmap, maskImage, ETrue, aMaskPos); |
|
1839 if (maskImage == VG_INVALID_HANDLE) |
|
1840 { |
|
1841 // Error set on creation of VGImage. |
|
1842 if (!imageCached) |
|
1843 { |
|
1844 vgDestroyImage(sourceImage); |
|
1845 } |
|
1846 return; |
|
1847 } |
|
1848 |
|
1849 DoVgMaskedImageDraw( |
|
1850 aDestPos, |
|
1851 sourceImage, |
|
1852 aSourceBitmap.SizeInPixels(), |
|
1853 aSourceRect, |
|
1854 maskImage, |
|
1855 aMaskBitmap.SizeInPixels(), |
|
1856 aSourceRect.Size(), |
|
1857 aInvertMask); |
|
1858 |
|
1859 if (!maskImageCached) |
|
1860 { |
|
1861 vgDestroyImage(maskImage); |
|
1862 } |
|
1863 if (!imageCached) |
|
1864 { |
|
1865 vgDestroyImage(sourceImage); |
|
1866 } |
|
1867 } |
|
1868 |
|
1869 |
|
1870 /** |
|
1871 This implementation stretches the mask first, and then performs mask tiling. Another approach is to |
|
1872 tile first and then perform stretching. The latter method requires more memory and stretches |
|
1873 once. Results between these methods are different. When stretching first, all tiles will be completely |
|
1874 uniform. When stretching last, different tiles are affected differently, based on the tile's position |
|
1875 and stretch factor. |
|
1876 |
|
1877 @see MDirectGdiEngine::DrawBitmapMasked() |
|
1878 @see DrawBitmap() |
|
1879 */ |
|
1880 void CVgEngine::DrawBitmapMasked( |
|
1881 const TRect& aDestRect, |
|
1882 const CFbsBitmap& aSourceBitmap, |
|
1883 const TRect& aSourceRect, |
|
1884 const CFbsBitmap& aMaskBitmap, |
|
1885 TBool aInvertMask) |
|
1886 { |
|
1887 if ((aSourceBitmap.ExtendedBitmapType() != KNullUid) || (aMaskBitmap.ExtendedBitmapType() != KNullUid)) |
|
1888 { |
|
1889 iDriver.SetError(KErrNotSupported); // Not supported for extended bitmaps |
|
1890 return; |
|
1891 } |
|
1892 |
|
1893 MakeEngineCurrent(); |
|
1894 TRect destRect(aDestRect); |
|
1895 if (!IntersectsClippingRegion (TRect(iOrigin, destRect.Size()))) |
|
1896 { |
|
1897 return; |
|
1898 } |
|
1899 |
|
1900 // Create source image |
|
1901 VGImage sourceImage = VG_INVALID_HANDLE; |
|
1902 TBool imageCached = ConvertBitmapToVgImage(aSourceBitmap, sourceImage); |
|
1903 // Return if VGImage failed to be created (error set on creation of VGImage). |
|
1904 if (sourceImage == VG_INVALID_HANDLE) |
|
1905 { |
|
1906 return; |
|
1907 } |
|
1908 |
|
1909 // Convert aMask to a VGImage. |
|
1910 VGImage maskImage = VG_INVALID_HANDLE; |
|
1911 TBool maskImageCached = ConvertBitmapToVgImage(aMaskBitmap, maskImage, ETrue); |
|
1912 // Error set on creation of VGImage if mask == VG_INVALID_HANDLE |
|
1913 |
|
1914 if (maskImage != VG_INVALID_HANDLE) |
|
1915 { |
|
1916 TSize destSize = destRect.Size(); |
|
1917 TSize sourceSize = aSourceRect.Size(); |
|
1918 if ((destSize.iWidth == sourceSize.iWidth) && (destSize.iHeight == sourceSize.iHeight)) |
|
1919 { |
|
1920 // No scaling of masked bitmap involved |
|
1921 DoVgMaskedImageDraw( |
|
1922 destRect.iTl, |
|
1923 sourceImage, |
|
1924 aSourceBitmap.SizeInPixels(), |
|
1925 aSourceRect, |
|
1926 maskImage, |
|
1927 aMaskBitmap.SizeInPixels(), |
|
1928 destSize, |
|
1929 aInvertMask); |
|
1930 } |
|
1931 else |
|
1932 { |
|
1933 // Unfortunately, the current implementation of VG does not support |
|
1934 // mask scaling. So, we render the mask into a VGImage pbuffer surface, |
|
1935 // and apply user to surface scaling to get a stretch. The stretched |
|
1936 // mask is then used for rendering. |
|
1937 |
|
1938 // Generate the VGImage to act as a pbuffer surface and receive the stretched mask. |
|
1939 const TSize maskSizeInPixels = aMaskBitmap.SizeInPixels(); |
|
1940 const VGImageFormat vgFormat = MapToVgDisplayMode(ClosestVgCompatibleDisplayMode(aMaskBitmap.DisplayMode())); |
|
1941 TInt scaledMaskWidth = Scale(maskSizeInPixels.iWidth,destSize.iWidth,sourceSize.iWidth); |
|
1942 TInt scaledMaskHeight = Scale(maskSizeInPixels.iHeight,destSize.iHeight,sourceSize.iHeight); |
|
1943 VGImage stretchedMask = DoVgCreateImage(vgFormat, |
|
1944 scaledMaskWidth, |
|
1945 scaledMaskHeight, |
|
1946 VG_IMAGE_QUALITY_NONANTIALIASED); |
|
1947 |
|
1948 if (stretchedMask != VG_INVALID_HANDLE) |
|
1949 { |
|
1950 // Get a configuration handle that is compatible with the mask pixel format. |
|
1951 EGLConfig utilConfig = 0; |
|
1952 TInt err = TConfigHelper::GetConfigForFbsBitmap (aMaskBitmap, utilConfig); |
|
1953 if (err == KErrNone) |
|
1954 { |
|
1955 TBool eglSuccess = EFalse; |
|
1956 EGLDisplay eglDisplay = iDriver.EglDisplay(); |
|
1957 EGLSurface lastSurface = eglGetCurrentSurface(EGL_DRAW); |
|
1958 |
|
1959 // Create a Pbuffer surface from the stretched mask VGImage. |
|
1960 EGLSurface utilSurface = eglCreatePbufferFromClientBuffer( |
|
1961 eglDisplay, |
|
1962 EGL_OPENVG_IMAGE, |
|
1963 static_cast<EGLClientBuffer>(stretchedMask), |
|
1964 utilConfig, |
|
1965 NULL); |
|
1966 |
|
1967 if (utilSurface != EGL_NO_SURFACE) |
|
1968 { |
|
1969 EGLContext lastContext = eglGetCurrentContext(); |
|
1970 // Create config. compatible context. |
|
1971 EGLContext utilContext = eglCreateContext(eglDisplay, utilConfig, lastContext, NULL); |
|
1972 |
|
1973 if (utilContext != EGL_NO_CONTEXT) |
|
1974 { |
|
1975 // Make the utility surface and context current, and stretch the mask. |
|
1976 if (eglMakeCurrent(eglDisplay, utilSurface, utilSurface, utilContext) == EGL_TRUE) |
|
1977 { |
|
1978 // Set up the scaling transform for the current surface. |
|
1979 vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); |
|
1980 // Scaling factors for x and y. |
|
1981 VGfloat xScale = ((VGfloat)destSize.iWidth/sourceSize.iWidth); |
|
1982 VGfloat yScale = ((VGfloat)destSize.iHeight/sourceSize.iHeight); |
|
1983 vgScale(xScale, yScale); |
|
1984 |
|
1985 // Render the stretched mask. |
|
1986 vgDrawImage(maskImage); |
|
1987 ResetVgMatrix(VG_MATRIX_IMAGE_USER_TO_SURFACE); |
|
1988 |
|
1989 // All done, make current the pre-existing rendering state. |
|
1990 eglMakeCurrent(eglDisplay, lastSurface, lastSurface, lastContext); |
|
1991 |
|
1992 eglSuccess = ETrue; |
|
1993 } |
|
1994 eglDestroyContext(eglDisplay, utilContext); |
|
1995 } |
|
1996 eglDestroySurface(eglDisplay, utilSurface); |
|
1997 } |
|
1998 |
|
1999 if (eglSuccess) |
|
2000 { |
|
2001 DoVgMaskedImageDraw(destRect.iTl, sourceImage, aSourceBitmap.SizeInPixels(), |
|
2002 aSourceRect, stretchedMask, maskSizeInPixels, destSize, aInvertMask); |
|
2003 } |
|
2004 else |
|
2005 { |
|
2006 // coverity[check_return] |
|
2007 // coverity[unchecked_value] |
|
2008 LogEglError(); |
|
2009 } |
|
2010 } |
|
2011 else |
|
2012 { |
|
2013 iDriver.SetError(err); |
|
2014 } |
|
2015 vgDestroyImage (stretchedMask); |
|
2016 } |
|
2017 if (!maskImageCached) vgDestroyImage (maskImage); |
|
2018 } |
|
2019 } |
|
2020 if (!imageCached) vgDestroyImage(sourceImage); |
|
2021 } |
|
2022 |
|
2023 /** |
|
2024 Helper method that implements the core blitting functionality. |
|
2025 |
|
2026 @param aDestPos The destination for the top left corner of the transferred bitmap. |
|
2027 It is relative to the top left corner of the destination bitmap, which may be the screen. |
|
2028 @param aSourceImage A valid VGImage to draw. |
|
2029 @param aSourceImageSize Extents of source bitmap. |
|
2030 @param aSourceRect A rectangle defining the piece of the bitmap to be drawn, |
|
2031 with co-ordinates relative to the top left corner of the bitmap. |
|
2032 @param aScaledMaskImage A valid mask VGImage - pre-scaled, ready for rendering. |
|
2033 @param aSrcMaskSize The size of the (unscaled) mask image. |
|
2034 @param aXscale Scaling factor to apply to x axis (already applied to mask). |
|
2035 @param aYscale Scaling factor to apply to y axis (already applied to mask). |
|
2036 @param aDestSize The size of the destination (used for calculating any scaling) |
|
2037 @param aInvertMask If EFalse, a source pixel that is masked by a black pixel |
|
2038 is not transferred to the destination rectangle. If ETrue, then a source |
|
2039 pixel that is masked by a white pixel is not transferred to the destination |
|
2040 rectangle. |
|
2041 |
|
2042 @pre The rendering target has been activated. aBitmap and aMaskBitmap are not NULL and hold Handles. |
|
2043 Destination rectangle extents must intersect clipping region. |
|
2044 aSourceImage is a valid VGImage handle. |
|
2045 @post Request to draw the masked bitmap content has been accepted. |
|
2046 There is no guarantee that the request has been processed when the method returns. |
|
2047 */ |
|
2048 void CVgEngine::DoVgMaskedImageDraw( |
|
2049 const TPoint& aDestPos, |
|
2050 VGImage aSourceImage, |
|
2051 const TRect& aSourceImageSize, |
|
2052 const TRect& aSourceRect, |
|
2053 const VGImage aScaledMaskImage, |
|
2054 const TSize& aSrcMaskSize, |
|
2055 const TSize& aDestSize, |
|
2056 TBool aInvertMask) |
|
2057 { |
|
2058 TBool destroySourceImageAtEnd = EFalse; |
|
2059 |
|
2060 if(aSourceImageSize != aSourceRect) |
|
2061 { |
|
2062 aSourceImage = |
|
2063 vgChildImage( |
|
2064 aSourceImage, |
|
2065 aSourceRect.iTl.iX, |
|
2066 aSourceRect.iTl.iY, |
|
2067 aSourceRect.Width(), |
|
2068 aSourceRect.Height()); |
|
2069 |
|
2070 if (aSourceImage == VG_INVALID_HANDLE) return; |
|
2071 destroySourceImageAtEnd = ETrue; |
|
2072 } |
|
2073 |
|
2074 TBool maskOK = EFalse; |
|
2075 TSize sourceSize = aSourceRect.Size(); |
|
2076 TRect destRect(aDestPos, TSize(Scale(aSourceRect.Width(),aDestSize.iWidth,sourceSize.iWidth), |
|
2077 Scale(aSourceRect.Height(),aDestSize.iHeight,sourceSize.iHeight))); |
|
2078 // VG does not provide mask tiling...we currently perform multiple |
|
2079 // vgMask operations to implement tiling. It should be possible to use |
|
2080 // pattern tiling to render the mask to a surface, convert surface region |
|
2081 // to VGImage and apply that as a mask (to take advantage of native VG |
|
2082 // tiling), though cost/benefit is has not yet been determined. |
|
2083 // NOTE: It may be worth optimising for cases where xScale or yScale equal one. |
|
2084 |
|
2085 TRect destVgRect = SgMetricsToVgTRect(aDestPos, destRect.Width(), destRect.Height()); |
|
2086 |
|
2087 if (aScaledMaskImage != VG_INVALID_HANDLE) |
|
2088 { |
|
2089 const TSize scaledMaskSize(Scale(aSrcMaskSize.iWidth,aDestSize.iWidth,sourceSize.iWidth), |
|
2090 Scale(aSrcMaskSize.iHeight,aDestSize.iHeight,sourceSize.iHeight)); |
|
2091 if (scaledMaskSize.iHeight > 0 && scaledMaskSize.iWidth > 0) |
|
2092 { |
|
2093 maskOK = ETrue; |
|
2094 // Determine mask image offset for rendering. |
|
2095 TInt scaledMaskXOffset = Scale(aSourceRect.iTl.iX%aSrcMaskSize.iWidth,aDestSize.iWidth,sourceSize.iWidth); |
|
2096 |
|
2097 // Sg coordinates are relative to top left, Vg are reletive to bottom left. As we |
|
2098 // tile from the bottom up we subtract from maskSize.iHeight to get the bottom edge |
|
2099 // offset. |
|
2100 TInt scaledMaskYOffset = (aSourceRect.iTl.iY + aSourceRect.Height()) % aSrcMaskSize.iHeight; |
|
2101 if (scaledMaskYOffset != 0) |
|
2102 { |
|
2103 scaledMaskYOffset = Scale(aSrcMaskSize.iHeight-scaledMaskYOffset,aDestSize.iHeight,sourceSize.iHeight); |
|
2104 } |
|
2105 |
|
2106 // If inverting the mask, we use a difference operation against the existing mask, so |
|
2107 // we need to ensure the existing mask is set to the correct state. |
|
2108 // Fill the existing mask so that it is completly transparent (set to all ones). |
|
2109 if(aInvertMask) |
|
2110 { |
|
2111 vgMask( |
|
2112 VG_INVALID_HANDLE, |
|
2113 VG_FILL_MASK, |
|
2114 destVgRect.iTl.iX, |
|
2115 destVgRect.iTl.iY, |
|
2116 destVgRect.Width(), |
|
2117 destVgRect.Height()); |
|
2118 } |
|
2119 |
|
2120 VGMaskOperation vgMaskOp = aInvertMask ? VG_SUBTRACT_MASK : VG_SET_MASK; |
|
2121 // NOTE: in VG destVgRect.iTl is physically at the bottom and destVgRect.iBr at the top |
|
2122 for ( |
|
2123 TInt maskY = destVgRect.iTl.iY - scaledMaskYOffset; |
|
2124 maskY < destVgRect.iBr.iY; |
|
2125 maskY += scaledMaskSize.iHeight) |
|
2126 { |
|
2127 for ( |
|
2128 TInt maskX = destVgRect.iTl.iX - scaledMaskXOffset; |
|
2129 maskX < destVgRect.iBr.iX; |
|
2130 maskX += scaledMaskSize.iWidth) |
|
2131 { |
|
2132 vgMask( |
|
2133 aScaledMaskImage, |
|
2134 vgMaskOp, |
|
2135 maskX, |
|
2136 maskY, |
|
2137 scaledMaskSize.iWidth, |
|
2138 scaledMaskSize.iHeight); |
|
2139 } |
|
2140 } |
|
2141 } |
|
2142 } |
|
2143 |
|
2144 // Set up translation and scale for the current surface - note that translation must |
|
2145 // occur first, as is unscaled. |
|
2146 vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); |
|
2147 vgTranslate(aDestPos.iX, aDestPos.iY); |
|
2148 VGfloat xScale = ((VGfloat)aDestSize.iWidth/sourceSize.iWidth); |
|
2149 VGfloat yScale = ((VGfloat)aDestSize.iHeight/sourceSize.iHeight); |
|
2150 vgScale(xScale, yScale); |
|
2151 |
|
2152 // Rather than bracketing vgDrawImage with VG_MASKING on/off we may want to always enable masking, |
|
2153 // and remove the mask when finished: |
|
2154 // vgMask(VG_INVALID_HANDLE, VG_CLEAR_MASK, 0, 0, iRenderingTarget->Size().iWidth, iRenderingTarget->Size().iHeight); |
|
2155 // If the mask is not removed in some way, then subsequent rendering operations which intersect with the |
|
2156 // masking region will be affected. |
|
2157 if (maskOK) |
|
2158 { |
|
2159 vgSeti (VG_MASKING, VG_TRUE); |
|
2160 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
2161 vgDrawImage(aSourceImage); |
|
2162 vgSeti (VG_MASKING, VG_FALSE); |
|
2163 } |
|
2164 else |
|
2165 { |
|
2166 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
2167 vgDrawImage(aSourceImage); |
|
2168 } |
|
2169 // Reset the transform matrix. |
|
2170 ResetVgMatrix(VG_MATRIX_IMAGE_USER_TO_SURFACE); |
|
2171 |
|
2172 if (destroySourceImageAtEnd) |
|
2173 { |
|
2174 vgDestroyImage (aSourceImage); |
|
2175 } |
|
2176 } |
|
2177 |
|
2178 /** |
|
2179 @see MDirectGdiEngine::DrawResource(const TPoint&, const RDirectGdiDrawableSource&, DirectGdi::TGraphicsRotation) |
|
2180 @see DrawResource(const TRect&, const RDirectGdiDrawableSource&, const TRect&, DirectGdi::TGraphicsRotation) |
|
2181 @see DrawResource(const TRect&, const RDirectGdiDrawableSource&, const TDesC8&) |
|
2182 */ |
|
2183 void CVgEngine::DrawResource(const TPoint& aPos, const RDirectGdiDrawableSource& aSource, DirectGdi::TGraphicsRotation aRotation) |
|
2184 { |
|
2185 MakeEngineCurrent(); |
|
2186 |
|
2187 CDirectGdiImageSourceImpl* source = iDriver.GetImageSourceFromHandle(aSource.Handle()); |
|
2188 if (source) |
|
2189 { |
|
2190 const TSize sourceSize = source->Size(); |
|
2191 |
|
2192 if ((sourceSize.iWidth > 0) && (sourceSize.iHeight > 0)) |
|
2193 { |
|
2194 VGImage vgImage = source->VgImage(); |
|
2195 if (vgImage != VG_INVALID_HANDLE) |
|
2196 { |
|
2197 vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); |
|
2198 |
|
2199 TPoint pos(aPos); |
|
2200 |
|
2201 |
|
2202 if (aRotation == DirectGdi::EGraphicsRotationNone || |
|
2203 aRotation == DirectGdi::EGraphicsRotation180) |
|
2204 { |
|
2205 // Pixel-data in EGLImages appears to be upside down due to the Y-inversion |
|
2206 // effect of the Identity matrix. Therefore must undo the Y-inversion here |
|
2207 // and adjust destination rect accordingly. |
|
2208 pos.iY = iSize.iHeight - aPos.iY - sourceSize.iHeight - iOrigin.iY; |
|
2209 pos.iX += iOrigin.iX; |
|
2210 vgLoadIdentity(); |
|
2211 } |
|
2212 else |
|
2213 { |
|
2214 // But if rotation is 90 or 270 degrees we need to mirror in the X-axis |
|
2215 // and adjust destination translation accordingly. |
|
2216 vgScale(-1, 1); |
|
2217 } |
|
2218 |
|
2219 switch (aRotation) |
|
2220 { |
|
2221 case DirectGdi::EGraphicsRotation90: |
|
2222 vgTranslate(-pos.iX, pos.iY); |
|
2223 vgRotate(90.0f); |
|
2224 break; |
|
2225 case DirectGdi::EGraphicsRotation180: |
|
2226 vgTranslate(pos.iX+sourceSize.iWidth, pos.iY+sourceSize.iHeight); |
|
2227 vgRotate(180.0f); |
|
2228 break; |
|
2229 case DirectGdi::EGraphicsRotation270: |
|
2230 vgTranslate(-pos.iX-sourceSize.iHeight, pos.iY+sourceSize.iWidth); |
|
2231 vgRotate(270.0f); |
|
2232 break; |
|
2233 default: |
|
2234 // No rotation |
|
2235 vgTranslate(pos.iX, pos.iY); |
|
2236 break; |
|
2237 } |
|
2238 |
|
2239 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
2240 vgDrawImage(vgImage); |
|
2241 |
|
2242 ResetVgMatrix(VG_MATRIX_IMAGE_USER_TO_SURFACE); |
|
2243 } |
|
2244 } |
|
2245 } |
|
2246 } |
|
2247 |
|
2248 /** |
|
2249 @see MDirectGdiEngine::DrawResource(const TRect&, const RDirectGdiDrawableSource&, DirectGdi::TGraphicsRotation) |
|
2250 */ |
|
2251 void CVgEngine::DrawResource(const TRect& aDestRect, |
|
2252 const RDirectGdiDrawableSource& aSource, |
|
2253 DirectGdi::TGraphicsRotation aRotation) |
|
2254 { |
|
2255 CDirectGdiImageSourceImpl* source = iDriver.GetImageSourceFromHandle(aSource.Handle()); |
|
2256 if (source) |
|
2257 { |
|
2258 TRect srcRect(TPoint(0, 0), source->Size()); |
|
2259 DoDrawResource(aDestRect, source, srcRect, aRotation); |
|
2260 } |
|
2261 } |
|
2262 |
|
2263 /** |
|
2264 @see MDirectGdiEngine::DrawResource(const TRect&, const RDirectGdiDrawableSource&, const TRect&, DirectGdi::TGraphicsRotation) |
|
2265 */ |
|
2266 void CVgEngine::DrawResource(const TRect& aDestRect, |
|
2267 const RDirectGdiDrawableSource& aSource, |
|
2268 const TRect& aSourceRect, |
|
2269 DirectGdi::TGraphicsRotation aRotation) |
|
2270 { |
|
2271 CDirectGdiImageSourceImpl* source = iDriver.GetImageSourceFromHandle(aSource.Handle()); |
|
2272 if (source) |
|
2273 { |
|
2274 // check source rectangle is fully contained within the image resource extent before drawing |
|
2275 TSize size = source->Size(); |
|
2276 if ((aSourceRect.iTl.iX < 0) |
|
2277 || (aSourceRect.iTl.iY < 0) |
|
2278 || (aSourceRect.iBr.iX > size.iWidth) |
|
2279 || (aSourceRect.iBr.iY > size.iHeight) |
|
2280 || (aSourceRect.Width() <= 0) |
|
2281 || (aSourceRect.Height() <= 0)) |
|
2282 { |
|
2283 iDriver.SetError(KErrArgument); |
|
2284 return; |
|
2285 } |
|
2286 |
|
2287 if (((aSourceRect.Width() == aDestRect.Width()) |
|
2288 && (aSourceRect.Height() == aDestRect.Height()) |
|
2289 && (aRotation == DirectGdi::EGraphicsRotationNone || aRotation == DirectGdi::EGraphicsRotation180)) |
|
2290 || |
|
2291 ((aSourceRect.Width() == aDestRect.Height()) |
|
2292 && (aSourceRect.Height() == aDestRect.Width()) |
|
2293 && (aRotation == DirectGdi::EGraphicsRotation90 || aRotation == DirectGdi::EGraphicsRotation270))) |
|
2294 { |
|
2295 // No scaling |
|
2296 DrawResource(TPoint(aDestRect.iTl.iX, aDestRect.iTl.iY), aSource, aRotation); |
|
2297 } |
|
2298 else |
|
2299 { |
|
2300 MakeEngineCurrent(); |
|
2301 DoDrawResource(aDestRect, source, aSourceRect, aRotation); |
|
2302 } |
|
2303 } |
|
2304 } |
|
2305 |
|
2306 /** |
|
2307 This method only supports drawing of image sources as Drawables. An attempt to draw |
|
2308 a Drawable that is not an image will result in a panic. |
|
2309 |
|
2310 @see MDirectGdiEngine::DrawResource(const TRect&, const RDirectGdiDrawableSource&, const TDesC8&) |
|
2311 |
|
2312 @panic DGDIAdapter 1, if an attempt is made to draw a drawable that is not an image source. |
|
2313 */ |
|
2314 void CVgEngine::DrawResource( |
|
2315 const TRect& aDestRect, |
|
2316 const RDirectGdiDrawableSource& aSource, |
|
2317 const TDesC8& /*aParam*/) |
|
2318 { |
|
2319 MakeEngineCurrent(); |
|
2320 |
|
2321 // Check to see if the passed drawable is actually an image as we only support drawing of images at present |
|
2322 if (iDriver.IsImageSource(aSource.Handle())) |
|
2323 { |
|
2324 CDirectGdiImageSourceImpl* source = iDriver.GetImageSourceFromHandle(aSource.Handle()); |
|
2325 if (source) |
|
2326 { |
|
2327 DoDrawResource(aDestRect, source, DirectGdi::EGraphicsRotationNone); |
|
2328 } |
|
2329 } |
|
2330 else |
|
2331 { |
|
2332 // This method only supports drawing image sources at present |
|
2333 GRAPHICS_ASSERT_ALWAYS(0, EDirectGdiPanicNotImplemented); |
|
2334 } |
|
2335 } |
|
2336 |
|
2337 /** |
|
2338 @see MDirectGdiEngine::BeginDrawGlyph() |
|
2339 |
|
2340 Sets the necessary OpenVG and engine state ready for receiving DrawGlyph() commands. |
|
2341 Any OpenVG state that is common for all DrawGlyph() operations are set. |
|
2342 */ |
|
2343 void CVgEngine::BeginDrawGlyph() |
|
2344 { |
|
2345 MakeEngineCurrent(); |
|
2346 vgSetPaint(iTextBrush, VG_FILL_PATH); |
|
2347 vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); |
|
2348 #ifdef DRAWGLYPH_MULTIPLY_MODE |
|
2349 vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_MULTIPLY); |
|
2350 #else |
|
2351 vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_STENCIL); |
|
2352 #endif |
|
2353 |
|
2354 #ifdef DRAWGLYPH_BUFFERED |
|
2355 iDrawGlyphCount = 0; |
|
2356 #endif |
|
2357 } |
|
2358 |
|
2359 /** |
|
2360 Draws Glyph image to the given position and orientation. |
|
2361 The function crops the output image to the clipping rectangle specified as a parameter. |
|
2362 If the clipping region is defined on the current context, it will also be taken into consideration |
|
2363 |
|
2364 @param aScrPos Position to start drawing glyph after rotation has been applied (if necessary). |
|
2365 @param aChar Character being drawn. Signifies general Unicode character value. |
|
2366 @param aGlyphImage Pointer to the glyph image data. |
|
2367 @param aGlyphBitmapType Type of bitmap format. |
|
2368 @param aGlyphImageSize Glyph image size. |
|
2369 @param aScrClipRect Clipping rect. |
|
2370 @param aRotation Rotation specifying how the glyph will be drawn. The number can only be divisible by 90 degrees, |
|
2371 i.e. horizontal and vertical rotation. |
|
2372 |
|
2373 @see CFont::TTextDirection |
|
2374 @see MDirectGdiEngine::DrawGlyph() |
|
2375 |
|
2376 @pre The rendering target has been activated. |
|
2377 @post Request to draw the Glyph has been accepted. |
|
2378 @panic DGDIAdapter 8, if aClipRect is empty (debug-only). |
|
2379 @panic DGDIAdapter 61, if font glyph image storage does not exist (debug-only). |
|
2380 */ |
|
2381 void CVgEngine::DrawGlyph( |
|
2382 const TPoint& aScrPos, |
|
2383 const TChar aChar, |
|
2384 const TUint8* aGlyphImage, |
|
2385 const TGlyphBitmapType aGlyphBitmapType, |
|
2386 const TSize& aGlyphImageSize, |
|
2387 const TRect& aScrClipRect, |
|
2388 const DirectGdi::TGraphicsRotation aRotation) |
|
2389 { |
|
2390 GRAPHICS_ASSERT_DEBUG(!aScrClipRect.IsEmpty(), EDirectGdiPanicInvalidRegion); |
|
2391 GRAPHICS_ASSERT_DEBUG(iFontGlyphImageStorage, EDirectGdiPanicGlyphImageStorageNotCreated); |
|
2392 |
|
2393 TPoint pos = aScrPos; |
|
2394 pos += iDrawOrigin; |
|
2395 TRect clipRect = aScrClipRect; |
|
2396 clipRect.iTl += iDrawOrigin; |
|
2397 clipRect.iBr += iDrawOrigin; |
|
2398 |
|
2399 if( aGlyphImageSize.iHeight <= 0 || aGlyphImageSize.iWidth <= 0 || |
|
2400 !clipRect.Intersects(iTargetRegion.BoundingRect()) || !iRegionManager.Intersects(clipRect)) |
|
2401 { |
|
2402 // Just leave silently, as spaces could be passed with empty size. |
|
2403 return; |
|
2404 } |
|
2405 |
|
2406 // Clip the glyph against the target and the clipping rects. |
|
2407 // Calculate the axis-aligned bounding box of the glyph. |
|
2408 TRect glyphBoundingBox; |
|
2409 switch(aRotation) |
|
2410 { |
|
2411 case DirectGdi::EGraphicsRotation90: |
|
2412 glyphBoundingBox = TRect(TPoint(1+pos.iX-aGlyphImageSize.iHeight, pos.iY), TSize(aGlyphImageSize.iHeight, aGlyphImageSize.iWidth)); |
|
2413 break; |
|
2414 case DirectGdi::EGraphicsRotation180: |
|
2415 glyphBoundingBox = TRect(TPoint(pos.iX-aGlyphImageSize.iHeight, pos.iY-aGlyphImageSize.iWidth), aGlyphImageSize); |
|
2416 break; |
|
2417 case DirectGdi::EGraphicsRotation270: |
|
2418 glyphBoundingBox = TRect(TPoint(pos.iX, 1+pos.iY-aGlyphImageSize.iWidth), TSize(aGlyphImageSize.iHeight, aGlyphImageSize.iWidth)); |
|
2419 break; |
|
2420 default: |
|
2421 glyphBoundingBox = TRect(pos, aGlyphImageSize); |
|
2422 break; |
|
2423 } |
|
2424 if (!glyphBoundingBox.Intersects(iTargetRegion.BoundingRect()) || !iRegionManager.Intersects(glyphBoundingBox)) |
|
2425 { |
|
2426 return; |
|
2427 } |
|
2428 #ifdef DRAWGLYPH_BUFFERED |
|
2429 iDrawGlyphCommand[iDrawGlyphCount].pos = pos; |
|
2430 iDrawGlyphCommand[iDrawGlyphCount].aChar = aChar; |
|
2431 iDrawGlyphCommand[iDrawGlyphCount].aGlyphBitmapType = aGlyphBitmapType; |
|
2432 iDrawGlyphCommand[iDrawGlyphCount].aGlyphImageSize = aGlyphImageSize; |
|
2433 iDrawGlyphCommand[iDrawGlyphCount].aClipRect = clipRect; |
|
2434 iDrawGlyphCommand[iDrawGlyphCount].aRotation = aRotation; |
|
2435 iDrawGlyphCommand[iDrawGlyphCount].aGlyphImage = const_cast<TUint8*>(aGlyphImage); |
|
2436 ++iDrawGlyphCount; |
|
2437 if (iDrawGlyphCount == KMaxGlyphs) |
|
2438 { |
|
2439 FlushDrawGlyphs(); |
|
2440 iDrawGlyphCount = 0; |
|
2441 } |
|
2442 #else |
|
2443 VGImage foreground = VG_INVALID_HANDLE; |
|
2444 VGImage outline = VG_INVALID_HANDLE; |
|
2445 VGImage shadow = VG_INVALID_HANDLE; |
|
2446 |
|
2447 TInt err = iFontGlyphImageStorage -> GlyphImage(iFontId, aChar, aGlyphBitmapType, aGlyphImage, aGlyphImageSize, &foreground, &shadow, &outline); |
|
2448 if(err != KErrNone) |
|
2449 { |
|
2450 iDriver.SetError(err); |
|
2451 if(err == KErrNoMemory) |
|
2452 { |
|
2453 if((foreground == VG_INVALID_HANDLE) || |
|
2454 ((aGlyphBitmapType == EFourColourBlendGlyphBitmap) && ((shadow == VG_INVALID_HANDLE) || (outline == VG_INVALID_HANDLE)))) |
|
2455 { |
|
2456 return; |
|
2457 } |
|
2458 } |
|
2459 else |
|
2460 { |
|
2461 return; |
|
2462 } |
|
2463 } |
|
2464 |
|
2465 RRegion oldClippingRegion; |
|
2466 oldClippingRegion.Copy(iRegionManager.ClippingRegion()); |
|
2467 iRegionManager.ClipTo(clipRect); |
|
2468 |
|
2469 // Load the matrix which converts Symbian coordinate system to OpenVG coordinate system |
|
2470 vgLoadMatrix(Identity()); |
|
2471 if(aRotation == DirectGdi::EGraphicsRotation90) |
|
2472 { |
|
2473 vgTranslate(pos.iX+1, pos.iY); |
|
2474 vgRotate(90.0f); |
|
2475 } |
|
2476 else if(aRotation == DirectGdi::EGraphicsRotation270) |
|
2477 { |
|
2478 vgTranslate(pos.iX, pos.iY+1); |
|
2479 vgRotate(270.0f); |
|
2480 } |
|
2481 else |
|
2482 { |
|
2483 vgTranslate(pos.iX, pos.iY); |
|
2484 } |
|
2485 |
|
2486 switch(aGlyphBitmapType) |
|
2487 { |
|
2488 case EMonochromeGlyphBitmap: |
|
2489 case EAntiAliasedGlyphBitmap: |
|
2490 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
2491 vgDrawImage(foreground); |
|
2492 break; |
|
2493 case EFourColourBlendGlyphBitmap: |
|
2494 { |
|
2495 SetVgPaintColor(iTextBrush, iBrushColor); |
|
2496 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
2497 vgDrawImage(foreground); |
|
2498 |
|
2499 SetVgPaintColor(iTextBrush, iTextShadowColor); |
|
2500 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
2501 vgDrawImage(shadow); |
|
2502 |
|
2503 SetVgPaintColor(iTextBrush, iPenColor); |
|
2504 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
2505 vgDrawImage(outline); |
|
2506 break; |
|
2507 } |
|
2508 } |
|
2509 |
|
2510 // Restore the clipping manager to its previous state. |
|
2511 iRegionManager.SetClippingRegion(oldClippingRegion); |
|
2512 oldClippingRegion.Close(); |
|
2513 #endif |
|
2514 } |
|
2515 |
|
2516 /** |
|
2517 @see MDirectGdiEngine::EndDrawGlyph() |
|
2518 |
|
2519 Undoes any OpenVG or engine state changes made in BeginDrawGlyph(). |
|
2520 */ |
|
2521 void CVgEngine::EndDrawGlyph() |
|
2522 { |
|
2523 #ifdef DRAWGLYPH_BUFFERED |
|
2524 FlushDrawGlyphs(); |
|
2525 iDrawGlyphCount = 0; |
|
2526 #endif |
|
2527 vgLoadMatrix(Identity()); |
|
2528 if (iOrigin != TPoint(0,0)) |
|
2529 { |
|
2530 vgTranslate(iOrigin.iX, iOrigin.iY); |
|
2531 } |
|
2532 |
|
2533 vgSetPaint(iBrush, VG_FILL_PATH); |
|
2534 vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL); |
|
2535 } |
|
2536 |
|
2537 #ifdef DRAWGLYPH_BUFFERED |
|
2538 void CVgEngine::FlushDrawGlyphs() |
|
2539 { |
|
2540 VGImage foreground[KMaxGlyphs]; |
|
2541 Mem::FillZ(foreground, iDrawGlyphCount*sizeof(VGImage)); |
|
2542 VGImage outline[KMaxGlyphs]; |
|
2543 VGImage shadow[KMaxGlyphs]; |
|
2544 |
|
2545 for (TInt glyph = 0; glyph < iDrawGlyphCount; ++glyph) |
|
2546 { |
|
2547 if (foreground[glyph] == 0) |
|
2548 { |
|
2549 TSize aGlyphImageSize = iDrawGlyphCommand[glyph].aGlyphImageSize; |
|
2550 TChar aChar = iDrawGlyphCommand[glyph].aChar; |
|
2551 TGlyphBitmapType aGlyphBitmapType = iDrawGlyphCommand[glyph].aGlyphBitmapType; |
|
2552 TUint8* aGlyphImage = iDrawGlyphCommand[glyph].aGlyphImage; |
|
2553 |
|
2554 VGImage foreground1 = VG_INVALID_HANDLE; |
|
2555 VGImage outline1 = VG_INVALID_HANDLE; |
|
2556 VGImage shadow1 = VG_INVALID_HANDLE; |
|
2557 |
|
2558 TInt err = iFontGlyphImageStorage -> GlyphImage(iFontId, aChar, aGlyphBitmapType, aGlyphImage, aGlyphImageSize, &foreground1, &shadow1, &outline1); |
|
2559 if(err != KErrNone) |
|
2560 { |
|
2561 iDriver.SetError(err); |
|
2562 if(err == KErrNoMemory) |
|
2563 { |
|
2564 if((foreground1 == VG_INVALID_HANDLE) || |
|
2565 ((aGlyphBitmapType == EFourColourBlendGlyphBitmap) && ((shadow1 == VG_INVALID_HANDLE) || (outline1 == VG_INVALID_HANDLE)))) |
|
2566 { |
|
2567 return; |
|
2568 } |
|
2569 } |
|
2570 else |
|
2571 { |
|
2572 return; |
|
2573 } |
|
2574 } |
|
2575 |
|
2576 foreground[glyph] = foreground1; |
|
2577 outline[glyph] = outline1; |
|
2578 shadow[glyph] = shadow1; |
|
2579 |
|
2580 for (TInt nextGlyph = glyph+1; nextGlyph < iDrawGlyphCount; nextGlyph++) |
|
2581 { |
|
2582 if (foreground[nextGlyph] == 0) |
|
2583 { |
|
2584 if (iDrawGlyphCommand[glyph].SameGlyph(iDrawGlyphCommand[nextGlyph])) |
|
2585 { |
|
2586 foreground[nextGlyph] = foreground[glyph]; |
|
2587 outline[nextGlyph] = outline[glyph]; |
|
2588 shadow[nextGlyph] = shadow[glyph]; |
|
2589 } |
|
2590 } |
|
2591 } |
|
2592 } |
|
2593 } |
|
2594 |
|
2595 RRegion oldClippingRegion; |
|
2596 oldClippingRegion.Copy(iRegionManager.ClippingRegion()); |
|
2597 vgLoadMatrix(Identity()); |
|
2598 TPoint lastPos; |
|
2599 for (TInt glyph = 0; glyph < iDrawGlyphCount; ++glyph) |
|
2600 { |
|
2601 TGlyphBitmapType aGlyphBitmapType = iDrawGlyphCommand[glyph].aGlyphBitmapType; |
|
2602 TRect aClipRect = iDrawGlyphCommand[glyph].aClipRect; |
|
2603 DirectGdi::TGraphicsRotation aRotation = iDrawGlyphCommand[glyph].aRotation; |
|
2604 TPoint aPos = iDrawGlyphCommand[glyph].pos; |
|
2605 |
|
2606 iRegionManager.ClipTo(aClipRect); |
|
2607 |
|
2608 // Load the matrix which converts Symbian coordinate system to OpenVG coordinate system |
|
2609 |
|
2610 if(aRotation == DirectGdi::EGraphicsRotation90) |
|
2611 { |
|
2612 vgTranslate(aPos.iX+1, aPos.iY); |
|
2613 vgRotate(90.0f); |
|
2614 } |
|
2615 else if(aRotation == DirectGdi::EGraphicsRotation270) |
|
2616 { |
|
2617 vgTranslate(aPos.iX, aPos.iY+1); |
|
2618 vgRotate(270.0f); |
|
2619 } |
|
2620 else |
|
2621 { |
|
2622 //vgTranslate(aPos.iX, aPos.iY); |
|
2623 vgTranslate(aPos.iX-lastPos.iX, aPos.iY - lastPos.iY); |
|
2624 lastPos = aPos; |
|
2625 } |
|
2626 |
|
2627 switch(aGlyphBitmapType) |
|
2628 { |
|
2629 case EMonochromeGlyphBitmap: |
|
2630 case EAntiAliasedGlyphBitmap: |
|
2631 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
2632 vgDrawImage(foreground[glyph]); |
|
2633 break; |
|
2634 case EFourColourBlendGlyphBitmap: |
|
2635 { |
|
2636 SetVgPaintColor(iTextBrush, iBrushColor); |
|
2637 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
2638 vgDrawImage(foreground[glyph]); |
|
2639 |
|
2640 SetVgPaintColor(iTextBrush, iTextShadowColor); |
|
2641 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
2642 vgDrawImage(shadow[glyph]); |
|
2643 |
|
2644 SetVgPaintColor(iTextBrush, iPenColor); |
|
2645 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
2646 vgDrawImage(outline[glyph]); |
|
2647 break; |
|
2648 } |
|
2649 } |
|
2650 |
|
2651 // Restore the clipping manager to its previous state. |
|
2652 iRegionManager.SetClippingRegion(oldClippingRegion); |
|
2653 } |
|
2654 oldClippingRegion.Close(); |
|
2655 } |
|
2656 #endif |
|
2657 |
|
2658 /** |
|
2659 @see MDirectGdiEngine::CopyRect() |
|
2660 */ |
|
2661 void CVgEngine::CopyRect(const TPoint& aOffset, const TRect& aRect) |
|
2662 { |
|
2663 MakeEngineCurrent(); |
|
2664 |
|
2665 // Transformations, masking and blending are not applied. |
|
2666 // So need to convert to VG coordinate system. |
|
2667 // i.e. Need Bottom-Left coord of aRect in VG's coordinates. |
|
2668 // Also need to allow for drawing engine coordinate system (iOrigin) |
|
2669 const TPoint sourcePoint = ConvertToVgCoords(TPoint(aRect.iTl.iX, aRect.iBr.iY) + iOrigin); |
|
2670 // Scissoring is applied to destination, but does not affect reading of pixels. |
|
2671 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
2672 vgCopyPixels(sourcePoint.iX + aOffset.iX, sourcePoint.iY - aOffset.iY, // destination point |
|
2673 sourcePoint.iX, sourcePoint.iY, // source point |
|
2674 aRect.Width(), aRect.Height()); // size of rect to copy |
|
2675 } |
|
2676 |
|
2677 /** |
|
2678 @see MDirectGdiEngine::ExternalizeL() |
|
2679 @see InternalizeL() |
|
2680 */ |
|
2681 void CVgEngine::ExternalizeL(RWriteStream& aWriteStream) |
|
2682 { |
|
2683 aWriteStream.WriteUint32L(iPaintMode); |
|
2684 aWriteStream << iLinePos; |
|
2685 } |
|
2686 |
|
2687 /** |
|
2688 @see MDirectGdiEngine::InternalizeL() |
|
2689 @see ExternalizeL() |
|
2690 */ |
|
2691 void CVgEngine::InternalizeL(RReadStream& aReadStream) |
|
2692 { |
|
2693 iPaintMode = aReadStream.ReadUint32L(); |
|
2694 aReadStream >> iLinePos; |
|
2695 } |
|
2696 |
|
2697 /** |
|
2698 @see MDirectGdiEngine::GetInterface() |
|
2699 */ |
|
2700 TInt CVgEngine::GetInterface(TUid aInterfaceId, TAny*& aInterface) |
|
2701 { |
|
2702 aInterface = NULL; |
|
2703 TInt err = KErrNone; |
|
2704 switch (aInterfaceId.iUid) |
|
2705 { |
|
2706 case KDirectGdiGetGlyphStorageUid: |
|
2707 { |
|
2708 aInterface = static_cast<MFontGlyphImageStorage*> (iDriver.FontGlyphImageStorage()); |
|
2709 break; |
|
2710 } |
|
2711 case KDirectGdiVgImageCacheUid: |
|
2712 { |
|
2713 aInterface = static_cast<MVgImageCache*> (iDriver.VgImageCache()); |
|
2714 break; |
|
2715 } |
|
2716 case KDrawDeviceOriginInterfaceID: |
|
2717 { |
|
2718 aInterface = static_cast<MDrawDeviceOrigin*>(this); |
|
2719 break; |
|
2720 } |
|
2721 default: |
|
2722 err = KErrNotSupported; |
|
2723 break; |
|
2724 } |
|
2725 return err; |
|
2726 } |
|
2727 |
|
2728 /** |
|
2729 Converts a point from the Symbian OS graphics coordinate system to the OpenVG coordinate system. |
|
2730 |
|
2731 The Symbian OS coordinate system's x-axis increases positively from the origin rightwards. |
|
2732 The y-axis increases positively from the origin downwards. |
|
2733 |
|
2734 The OpenVG coordinate system's x-axis increases positively from the origin rightwards. |
|
2735 The y-axis increases positively from the origin upwards. |
|
2736 |
|
2737 Therefore a point (X,Y) in the Symbian OS coordinate system would be equivalent to a point |
|
2738 (X',Y') in the OpenVG coordinate system by the following transformations: |
|
2739 X' = X |
|
2740 Y' = (Height of rendering target) - Y |
|
2741 |
|
2742 @param aPoint A point specified in the Symbian OS graphics coordinate system. |
|
2743 |
|
2744 @return The point specified in the OpenVG-specific coordinate system. |
|
2745 */ |
|
2746 const TPoint CVgEngine::ConvertToVgCoords(const TPoint& aPoint) |
|
2747 { |
|
2748 MakeEngineCurrent(); |
|
2749 TInt targetHeight = iSize.iHeight; |
|
2750 return TPoint(aPoint.iX, targetHeight - aPoint.iY); |
|
2751 } |
|
2752 |
|
2753 /** |
|
2754 Converts the position of a rectangle from the Symbian OS graphics coordinate system to the |
|
2755 OpenVG coordinate system. |
|
2756 |
|
2757 The Symbian OS coordinate system's x-axis increases positively from the origin rightwards. |
|
2758 The y-axis increases positively from the origin downwards. |
|
2759 A rectangle's position is specified by the top-left coordinate. |
|
2760 |
|
2761 The OpenVG coordinate system's x-axis increases positively from the origin rightwards. |
|
2762 The y-axis increases positively from the origin upwards. |
|
2763 A rectangle's position is specified by the bottom-left coordinate. |
|
2764 |
|
2765 A point (X,Y) in the Symbian OS coordinate system would be equivalent to point |
|
2766 (X',Y') in the OpenVG coordinate system by the following transformations: |
|
2767 X' = X |
|
2768 Y' = (Height of rendering target) - Y |
|
2769 |
|
2770 @param aRect A rectangle whose position is to be converted for use in OpenVG. |
|
2771 |
|
2772 @return The bottom-left point of the rectangle, specified in the OpenVG specific coordinate system. |
|
2773 */ |
|
2774 const TPoint CVgEngine::ConvertToVgCoords(const TRect& aRect) |
|
2775 { |
|
2776 MakeEngineCurrent(); |
|
2777 TInt targetHeight = iSize.iHeight; |
|
2778 return TPoint(aRect.iTl.iX, targetHeight - aRect.iBr.iY); |
|
2779 } |
|
2780 |
|
2781 /** |
|
2782 Resets the user-to-surface OpenVG matrices to the default system matrix. |
|
2783 */ |
|
2784 void CVgEngine::ResetVgMatrix() |
|
2785 { |
|
2786 ResetVgMatrix(VG_MATRIX_PATH_USER_TO_SURFACE); |
|
2787 ResetVgMatrix(VG_MATRIX_IMAGE_USER_TO_SURFACE); |
|
2788 } |
|
2789 |
|
2790 /** |
|
2791 Resets an OpenVG matrix to the default system matrix. Current origin offset applies. |
|
2792 @param aMatrixMode The OpenVG matrix being reset. |
|
2793 */ |
|
2794 void CVgEngine::ResetVgMatrix(const VGMatrixMode aMatrixMode) |
|
2795 { |
|
2796 vgSeti(VG_MATRIX_MODE, aMatrixMode); |
|
2797 // Load matrix which converts Symbian coordinate system to VG coordinate system |
|
2798 vgLoadMatrix(Identity()); |
|
2799 |
|
2800 // Add in translation for drawing engine coordinate system |
|
2801 vgTranslate(iOrigin.iX, iOrigin.iY); |
|
2802 } |
|
2803 |
|
2804 /** |
|
2805 If aPath is a null handle, a VGPath is created with enough space to hold aExpectedCommandCount commands. |
|
2806 If the path is already a handle to a valid path, the path is simply cleared instead of recreated. |
|
2807 Also allocates memory for two arrays; one to hold all commands we will add to the newly |
|
2808 created path, and one to hold all coordinates we will add to the newly created path. |
|
2809 Commands and coordinates are not added to the path until FinishPath() is called for this path. |
|
2810 Commands and coordinates are saved for adding to the path when FinishPath() is |
|
2811 called by calling AppendPathCommand(). Any failures are reported to the driver before returning. |
|
2812 |
|
2813 @param aPath A handle to the path being cleared/created. |
|
2814 @param aExpectedCommandCount The number of commands the path is expected to hold. If this size |
|
2815 is underestimated, the arrays that hold the current commands and coordinates will grow |
|
2816 as additional items are added beyond the original estimated size. Once a path is finished, the current |
|
2817 command and coordinate counters are reset, but the memory remains to cut down on memory allocations. |
|
2818 |
|
2819 @return EFalse if memory allocation or path creation fails, ETrue otherwise. |
|
2820 |
|
2821 @see CVgEngine::AppendPathCommand(VGubyte) |
|
2822 @see CVgEngine::AppendPathCommand(VGubyte, VGfloat) |
|
2823 @see CVgEngine::AppendPathCommand(VGubyte, VGfloat, VGfloat) |
|
2824 @see CVgEngine::PrepareForPathCommand() |
|
2825 @see CVgEngine::FinishPath() |
|
2826 @see CVgEngine::AllocPathCommands() |
|
2827 @see CVgEngine::AllocPathCoords() |
|
2828 */ |
|
2829 TBool CVgEngine::PreparePath(VGPath& aPath, TInt aExpectedCommandCount) |
|
2830 { |
|
2831 TInt err = KErrNone; |
|
2832 err = AllocPathCommands(aExpectedCommandCount); |
|
2833 if(KErrNone == err) |
|
2834 { |
|
2835 err = AllocPathCoords(aExpectedCommandCount*2); // guess at the number of coords needed, this will be reallocated if it is not big enough |
|
2836 } |
|
2837 if(KErrNone == err) |
|
2838 { |
|
2839 if (aPath != VG_INVALID_HANDLE) |
|
2840 vgClearPath(aPath, VG_PATH_CAPABILITY_APPEND_TO); |
|
2841 else |
|
2842 { |
|
2843 aPath = vgCreatePath(VG_PATH_FORMAT_STANDARD, |
|
2844 VG_PATH_DATATYPE_F, |
|
2845 1.0f, //scale |
|
2846 0.0f, //bias |
|
2847 aExpectedCommandCount, |
|
2848 aExpectedCommandCount*2, //expected coord count |
|
2849 VG_PATH_CAPABILITY_APPEND_TO); |
|
2850 |
|
2851 if (aPath == VG_INVALID_HANDLE) |
|
2852 err = KErrNoMemory; |
|
2853 } |
|
2854 } |
|
2855 if (KErrNone != err) |
|
2856 { |
|
2857 iDriver.SetError(err); |
|
2858 return EFalse; |
|
2859 } |
|
2860 return ETrue; |
|
2861 } |
|
2862 |
|
2863 /** |
|
2864 Adds a path command to the currently saved array of path commands. Any error that occurs is stored |
|
2865 with the driver. |
|
2866 |
|
2867 @param aCommand The VGPathCommand to add to the current path, e.g VG_CLOSE_PATH. |
|
2868 |
|
2869 @see CVgEngine::CreatePath() |
|
2870 @see CVgEngine::AppendPathCommand(VGubyte, VGfloat) |
|
2871 @see CVgEngine::AppendPathCommand(VGubyte, VGfloat, VGfloat) |
|
2872 @see CVgEngine::PrepareForPathCommand() |
|
2873 @see CVgEngine::FinishPath() |
|
2874 @see CVgEngine::AllocPathCommands() |
|
2875 @see CVgEngine::AllocPathCoords() |
|
2876 */ |
|
2877 void CVgEngine::AppendPathCommand(VGubyte aCommand) |
|
2878 { |
|
2879 TInt err = PrepareForPathCommand(1, 0); |
|
2880 if (KErrNone == err) |
|
2881 { |
|
2882 iPathCommands[iPathCommandCount++] = aCommand; |
|
2883 } |
|
2884 else |
|
2885 { |
|
2886 iDriver.SetError(err); |
|
2887 } |
|
2888 } |
|
2889 |
|
2890 /** |
|
2891 Adds a path command and a single coordinate to the currently saved array of path commands. |
|
2892 Any error that occurs is stored with the driver. |
|
2893 |
|
2894 @param aCommand The VGPathCommand to add to the current path, e.g VG_HLINE_TO_ABS. |
|
2895 @param aCoord The coordinate to add to the current path, e.g. 10.f. |
|
2896 |
|
2897 @see CVgEngine::CreatePath() |
|
2898 @see CVgEngine::AppendPathCommand(VGubyte) |
|
2899 @see CVgEngine::AppendPathCommand(VGubyte, VGfloat, VGfloat) |
|
2900 @see CVgEngine::PrepareForPathCommand() |
|
2901 @see CVgEngine::FinishPath() |
|
2902 @see CVgEngine::AllocPathCommands() |
|
2903 @see CVgEngine::AllocPathCoords() |
|
2904 */ |
|
2905 void CVgEngine::AppendPathCommand(VGubyte aCommand, VGfloat aCoord) |
|
2906 { |
|
2907 TInt err = PrepareForPathCommand(1, 1); |
|
2908 if (KErrNone == err) |
|
2909 { |
|
2910 iPathCommands[iPathCommandCount++] = aCommand; |
|
2911 iPathCoords[iPathCoordCount++] = aCoord; |
|
2912 } |
|
2913 else |
|
2914 { |
|
2915 iDriver.SetError(err); |
|
2916 } |
|
2917 } |
|
2918 |
|
2919 /** |
|
2920 Adds a path command and two coordinates to the currently saved array of path commands. |
|
2921 Any error that occurs is stored with the driver. |
|
2922 |
|
2923 @param aCommand The VGPathCommand to add to the current path, e.g VG_MOVE_TO_ABS. |
|
2924 @param aCoord1 The coordinate to add to the current path, e.g. 10.f. |
|
2925 @param aCoord2 The coordinate to add to the current path, e.g. 10.f. |
|
2926 |
|
2927 @see CVgEngine::CreatePath() |
|
2928 @see CVgEngine::AppendPathCommand(VGubyte) |
|
2929 @see CVgEngine::AppendPathCommand(VGubyte, VGfloat) |
|
2930 @see CVgEngine::PrepareForPathCommand() |
|
2931 @see CVgEngine::FinishPath() |
|
2932 @see CVgEngine::AllocPathCommands() |
|
2933 @see CVgEngine::AllocPathCoords() |
|
2934 */ |
|
2935 void CVgEngine::AppendPathCommand(VGubyte aCommand, VGfloat aCoord1, VGfloat aCoord2) |
|
2936 { |
|
2937 TInt err = PrepareForPathCommand(1, 2); |
|
2938 if (KErrNone == err) |
|
2939 { |
|
2940 iPathCommands[iPathCommandCount++] = aCommand; |
|
2941 iPathCoords[iPathCoordCount++] = aCoord1; |
|
2942 iPathCoords[iPathCoordCount++] = aCoord2; |
|
2943 } |
|
2944 else |
|
2945 { |
|
2946 iDriver.SetError(err); |
|
2947 } |
|
2948 } |
|
2949 |
|
2950 /** |
|
2951 Allocates memory to store the passed number of commands and coordinates in the saved command and |
|
2952 coordinate arrays. Any error that occurs is stored with the driver. |
|
2953 |
|
2954 @param aCommandCount The number of new commands expected to be added to the current command array. |
|
2955 @param aCoordCount The number of new commands expected to be added to the current coordinate array. |
|
2956 |
|
2957 @return KErrNoMemory if memory allocation fails, KErrNone otherwise. |
|
2958 |
|
2959 @see CVgEngine::CreatePath() |
|
2960 @see CVgEngine::AppendPathCommand(VGubyte) |
|
2961 @see CVgEngine::AppendPathCommand(VGubyte, VGfloat) |
|
2962 @see CVgEngine::AppendPathCommand(VGubyte, VGfloat, VGfloat) |
|
2963 @see CVgEngine::FinishPath() |
|
2964 @see CVgEngine::AllocPathCommands() |
|
2965 @see CVgEngine::AllocPathCoords() |
|
2966 */ |
|
2967 TInt CVgEngine::PrepareForPathCommand(TInt aCommandCount, TInt aCoordCount) |
|
2968 { |
|
2969 // Do we need to add space for the new commands and coords? |
|
2970 TInt err = AllocPathCommands(iPathCommandCount+aCommandCount); |
|
2971 if (err == KErrNone) |
|
2972 { |
|
2973 err = AllocPathCoords(iPathCoordCount+aCoordCount); |
|
2974 } |
|
2975 if (err != KErrNone) |
|
2976 { |
|
2977 iDriver.SetError(err); |
|
2978 } |
|
2979 return err; |
|
2980 } |
|
2981 |
|
2982 /** |
|
2983 Adds the contents of the saved command and coordinate arrays to the passed VGPath ready for drawing. |
|
2984 Clears the counts of saved commands and coordinates. |
|
2985 |
|
2986 @param aPath The path that the current commands and coordinates are to be added to. |
|
2987 |
|
2988 @see CVgEngine::CreatePath() |
|
2989 @see CVgEngine::AppendPathCommand(VGubyte) |
|
2990 @see CVgEngine::AppendPathCommand(VGubyte, VGfloat) |
|
2991 @see CVgEngine::AppendPathCommand(VGubyte, VGfloat, VGfloat) |
|
2992 @see CVgEngine::PrepareForPathCommand() |
|
2993 @see CVgEngine::AllocPathCommands() |
|
2994 @see CVgEngine::AllocPathCoords() |
|
2995 */ |
|
2996 void CVgEngine::FinishPath(VGPath aPath) |
|
2997 { |
|
2998 vgAppendPathData(aPath, iPathCommandCount, iPathCommands, iPathCoords); |
|
2999 iPathCommandCount = 0; |
|
3000 iPathCoordCount = 0; |
|
3001 } |
|
3002 |
|
3003 /** |
|
3004 Allocates enough memory to hold aCommandCount path commands, unless the command array already holds |
|
3005 enough memory. |
|
3006 |
|
3007 @param aCommandCount The number of commands to allocate space for in the command array. |
|
3008 |
|
3009 @return KErrNoMemory if memory allocation fails, KErrNone otherwise. |
|
3010 |
|
3011 @see CVgEngine::CreatePath(TInt) |
|
3012 @see CVgEngine::AppendPathCommand(VGubyte) |
|
3013 @see CVgEngine::AppendPathCommand(VGubyte, VGfloat) |
|
3014 @see CVgEngine::AppendPathCommand(VGubyte, VGfloat, VGfloat) |
|
3015 @see CVgEngine::PrepareForPathCommand(TInt, TInt) |
|
3016 @see CVgEngine::FinishPath(VGPath) |
|
3017 @see CVgEngine::AllocPathCoords(TInt) |
|
3018 */ |
|
3019 TInt CVgEngine::AllocPathCommands(TInt aCommandCount) |
|
3020 { |
|
3021 TInt err = KErrNone; |
|
3022 if (iCurrentMaxCommands < aCommandCount) |
|
3023 { |
|
3024 VGubyte* oldPathCommands = iPathCommands; |
|
3025 iPathCommands = static_cast<VGubyte*>(User::ReAlloc(iPathCommands, sizeof(VGubyte)*aCommandCount)); |
|
3026 if (iPathCommands) |
|
3027 { |
|
3028 iCurrentMaxCommands = aCommandCount; |
|
3029 } |
|
3030 else |
|
3031 { |
|
3032 delete oldPathCommands; |
|
3033 iCurrentMaxCommands = 0; |
|
3034 } |
|
3035 } |
|
3036 if(!iPathCommands) |
|
3037 err = KErrNoMemory; |
|
3038 return err; |
|
3039 } |
|
3040 |
|
3041 /** |
|
3042 Allocates enough memory to hold aCoordCount path coordinates, unless the coordinate array already holds |
|
3043 enough memory. |
|
3044 |
|
3045 @param aCoordCount The number of coordinates to allocate space for in the coordinate array. |
|
3046 |
|
3047 @return KErrNoMemory if memory allocation fails, KErrNone otherwise. |
|
3048 |
|
3049 @see CVgEngine::CreatePath() |
|
3050 @see CVgEngine::AppendPathCommand(VGubyte) |
|
3051 @see CVgEngine::AppendPathCommand(VGubyte, VGfloat) |
|
3052 @see CVgEngine::AppendPathCommand(VGubyte, VGfloat, VGfloat) |
|
3053 @see CVgEngine::PrepareForPathCommand() |
|
3054 @see CVgEngine::FinishPath() |
|
3055 @see CVgEngine::AllocPathCommands() |
|
3056 */ |
|
3057 TInt CVgEngine::AllocPathCoords(TInt aCoordCount) |
|
3058 { |
|
3059 TInt err = KErrNone; |
|
3060 if (iCurrentMaxCoords < aCoordCount) |
|
3061 { |
|
3062 VGfloat* oldPathCoords = iPathCoords; |
|
3063 iPathCoords = static_cast<VGfloat*>(User::ReAlloc(iPathCoords, sizeof(VGfloat)*aCoordCount)); |
|
3064 if (iPathCoords) |
|
3065 { |
|
3066 iCurrentMaxCoords = aCoordCount; |
|
3067 } |
|
3068 else |
|
3069 { |
|
3070 delete oldPathCoords; |
|
3071 iCurrentMaxCoords = 0; |
|
3072 } |
|
3073 } |
|
3074 if(!iPathCoords) |
|
3075 err = KErrNoMemory; |
|
3076 return err; |
|
3077 } |
|
3078 |
|
3079 /** |
|
3080 Helper method for creating a VGImage from a CFbsBitmap. A temporary VGImageFormat-compatible |
|
3081 copy of the image may be created, if the source bitmap pixel format is not directly supported |
|
3082 by OpenVG. If there is not enough memory available, the error state on the driver is set to KErrNoMemory. |
|
3083 It sets the error in the driver if out-of-memory occurs when creating a VGImage. OpenVG will set its |
|
3084 own internal error if creation of VGImage fails. |
|
3085 |
|
3086 @param aSource The source bitmap. |
|
3087 @param aFlipY If 'ETrue' then inverts the image in the y axis. |
|
3088 @param aOrigin An offset from the top-left of the image in which to take the first pixel of the image. |
|
3089 |
|
3090 @return Returns a valid VGImage created using the passed CFbsBitmap. If unable to |
|
3091 create a valid VGImage then VG_INVALID_HANDLE. |
|
3092 */ |
|
3093 VGImage CVgEngine::CreateSourceVGImage(const CFbsBitmap& aSource, TBool aFlipY, const TPoint& aOrigin) |
|
3094 { |
|
3095 TDisplayMode srcDisplayMode = aSource.DisplayMode(); |
|
3096 TDisplayMode vgCompatibleDisplayMode = ClosestVgCompatibleDisplayMode(srcDisplayMode); |
|
3097 VGImageFormat imageFormat = MapToVgDisplayMode(vgCompatibleDisplayMode); |
|
3098 const TSize sourceSize = aSource.SizeInPixels(); |
|
3099 VGImage image = DoVgCreateImage(imageFormat, sourceSize.iWidth, sourceSize.iHeight, VG_IMAGE_QUALITY_NONANTIALIASED); |
|
3100 if (image != VG_INVALID_HANDLE) |
|
3101 { |
|
3102 const TInt pixmapStride = CFbsBitmap::ScanLineLength(sourceSize.iWidth, vgCompatibleDisplayMode); |
|
3103 |
|
3104 // Conversion is performed if changing display mode (pixel format), source is compressed, or want to flip the y orientation. |
|
3105 if ((vgCompatibleDisplayMode != srcDisplayMode) || (aSource.Header().iCompression != ENoBitmapCompression) || aFlipY) |
|
3106 { |
|
3107 // May be worth using a static memory buffer for smaller scan-lines, to avoid overhead of alloc. |
|
3108 TAny* data = User::Alloc(pixmapStride); |
|
3109 if (data) |
|
3110 { |
|
3111 // Allocate memory and transform source into target format. |
|
3112 TPoint sourcePoint (0, aFlipY ? (sourceSize.iHeight - 1) : 0); |
|
3113 TPtr8 targetPoint ((TUint8*)data, pixmapStride, pixmapStride); |
|
3114 TInt adj = aFlipY ? -1 : 1; |
|
3115 |
|
3116 if (aOrigin == TPoint(0,0)) // Not shifted |
|
3117 { |
|
3118 for (TInt targetY = 0; targetY < sourceSize.iHeight; targetY++, sourcePoint.iY += adj) |
|
3119 { |
|
3120 aSource.GetScanLine(targetPoint, sourcePoint, sourceSize.iWidth, vgCompatibleDisplayMode); |
|
3121 vgImageSubData(image, data, pixmapStride, imageFormat, 0, targetY, sourceSize.iWidth, 1); |
|
3122 } |
|
3123 } |
|
3124 else if (aOrigin.iX == 0) // Only shifted in Y. |
|
3125 { |
|
3126 for (TInt targetY = 0x00; targetY < sourceSize.iHeight; targetY++, sourcePoint.iY += adj) |
|
3127 { |
|
3128 aSource.GetScanLine(targetPoint, sourcePoint, sourceSize.iWidth, vgCompatibleDisplayMode); |
|
3129 vgImageSubData(image, data, pixmapStride, imageFormat, 0, targetY + aOrigin.iY, sourceSize.iWidth, 1); |
|
3130 vgImageSubData(image, data, pixmapStride, imageFormat, 0, targetY + aOrigin.iY - sourceSize.iHeight, sourceSize.iWidth, 1); |
|
3131 } |
|
3132 } |
|
3133 else if (aOrigin.iY == 0) // Only shifted in X. |
|
3134 { |
|
3135 for (TInt targetY = 0x00; targetY < sourceSize.iHeight; targetY++, sourcePoint.iY += adj) |
|
3136 { |
|
3137 aSource.GetScanLine(targetPoint, sourcePoint, sourceSize.iWidth, vgCompatibleDisplayMode); |
|
3138 vgImageSubData(image, data, pixmapStride, imageFormat, -aOrigin.iX, targetY, sourceSize.iWidth, 1); |
|
3139 vgImageSubData(image, data, pixmapStride, imageFormat, -aOrigin.iX + sourceSize.iWidth, targetY, sourceSize.iWidth, 1); |
|
3140 } |
|
3141 } |
|
3142 else // Shifted in both X and Y. |
|
3143 { |
|
3144 for (TInt targetY = 0; targetY < sourceSize.iHeight; targetY++, sourcePoint.iY += adj) |
|
3145 { |
|
3146 aSource.GetScanLine(targetPoint, sourcePoint, sourceSize.iWidth, vgCompatibleDisplayMode); |
|
3147 vgImageSubData(image, data, pixmapStride, imageFormat, -aOrigin.iX, targetY + aOrigin.iY, sourceSize.iWidth, 1); |
|
3148 vgImageSubData(image, data, pixmapStride, imageFormat, -aOrigin.iX, targetY + aOrigin.iY - sourceSize.iHeight, sourceSize.iWidth, 1); |
|
3149 vgImageSubData(image, data, pixmapStride, imageFormat, -aOrigin.iX + sourceSize.iWidth, targetY + aOrigin.iY, sourceSize.iWidth, 1); |
|
3150 vgImageSubData(image, data, pixmapStride, imageFormat, -aOrigin.iX + sourceSize.iWidth, targetY + aOrigin.iY - sourceSize.iHeight, sourceSize.iWidth, 1); |
|
3151 } |
|
3152 } |
|
3153 User::Free(data); |
|
3154 } |
|
3155 else |
|
3156 { |
|
3157 iDriver.SetError(KErrNoMemory); |
|
3158 vgDestroyImage(image); |
|
3159 return VG_INVALID_HANDLE; |
|
3160 } |
|
3161 } |
|
3162 else |
|
3163 { |
|
3164 aSource.BeginDataAccess(); |
|
3165 const TInt sourceDataStride = aSource.DataStride(); |
|
3166 if (aOrigin == TPoint(0,0)) // Not shifted |
|
3167 { |
|
3168 vgImageSubData(image, aSource.DataAddress(), sourceDataStride, imageFormat, 0, 0, sourceSize.iWidth, sourceSize.iHeight); |
|
3169 } |
|
3170 else |
|
3171 { |
|
3172 TUint32* dataAddress = aSource.DataAddress(); |
|
3173 |
|
3174 if (aOrigin.iX == 0) // Only shifted in Y. |
|
3175 { |
|
3176 vgImageSubData(image, dataAddress, sourceDataStride, imageFormat, 0, aOrigin.iY, sourceSize.iWidth, sourceSize.iHeight); |
|
3177 vgImageSubData(image, dataAddress, sourceDataStride, imageFormat, 0, aOrigin.iY - sourceSize.iHeight, sourceSize.iWidth, sourceSize.iHeight); |
|
3178 } |
|
3179 else if (aOrigin.iY == 0) // Only shifted in X. |
|
3180 { |
|
3181 vgImageSubData(image, dataAddress, sourceDataStride, imageFormat, -aOrigin.iX, 0, sourceSize.iWidth, sourceSize.iHeight); |
|
3182 vgImageSubData(image, dataAddress, sourceDataStride, imageFormat, -aOrigin.iX + sourceSize.iWidth, 0, sourceSize.iWidth, sourceSize.iHeight); |
|
3183 } |
|
3184 else // Shifted in both X and Y. |
|
3185 { |
|
3186 vgImageSubData(image, dataAddress, sourceDataStride, imageFormat, -aOrigin.iX, aOrigin.iY, sourceSize.iWidth, sourceSize.iHeight); |
|
3187 vgImageSubData(image, dataAddress, sourceDataStride, imageFormat, -aOrigin.iX, aOrigin.iY - sourceSize.iHeight, sourceSize.iWidth, sourceSize.iHeight); |
|
3188 vgImageSubData(image, dataAddress, sourceDataStride, imageFormat, -aOrigin.iX + sourceSize.iWidth, aOrigin.iY, sourceSize.iWidth, sourceSize.iHeight); |
|
3189 vgImageSubData(image, dataAddress, sourceDataStride, imageFormat, -aOrigin.iX + sourceSize.iWidth, aOrigin.iY - sourceSize.iHeight, sourceSize.iWidth, sourceSize.iHeight); |
|
3190 } |
|
3191 } |
|
3192 aSource.EndDataAccess(ETrue); |
|
3193 } |
|
3194 } |
|
3195 return image; |
|
3196 } |
|
3197 |
|
3198 /** |
|
3199 Creates an image which represents a standard brush pattern (e.g. EVerticalHatchBrush), by |
|
3200 drawing the pattern using the current pen and brush colour to an image. |
|
3201 |
|
3202 @param aPatternSize The dimensions in pixels of the image to create. |
|
3203 @param aBrushPattern An array of 1s and 0s representing the pattern to create, the length |
|
3204 of this array is aSize.iWidth*aSize.iHeight. |
|
3205 |
|
3206 @return KErrNone if the brush is created successfully, KErrNoMemory if we fail to create |
|
3207 the brush image, KErrArgument if aBrushPattern is invalid. |
|
3208 */ |
|
3209 TInt CVgEngine::CreateStandardBrush(TSize& aPatternSize, VGbyte* aBrushPattern) |
|
3210 { |
|
3211 if (aBrushPattern == NULL) |
|
3212 return KErrArgument; |
|
3213 |
|
3214 TInt err = KErrNone; |
|
3215 MakeEngineCurrent(); |
|
3216 |
|
3217 // The image format to use as the standard brush. If this changes, it also needs changing in |
|
3218 // GetCurrentBrush(). |
|
3219 const VGImageFormat dataFormat = VG_sARGB_8888; |
|
3220 |
|
3221 // We want to create the brush to the nearest power of 4 size. |
|
3222 TSize brushSize((aPatternSize.iWidth+3)&~3, (aPatternSize.iHeight+3)&~3); |
|
3223 |
|
3224 // Allocate some memory to write the brush pattern to. |
|
3225 TUint32* data = new TUint32[brushSize.iWidth*brushSize.iHeight]; |
|
3226 if (data == NULL) |
|
3227 err = KErrNoMemory; |
|
3228 |
|
3229 if (err == KErrNone) |
|
3230 { |
|
3231 if (iBrushPatternStandardRegion != VG_INVALID_HANDLE) |
|
3232 { |
|
3233 vgDestroyImage(iBrushPatternStandardRegion); |
|
3234 iBrushPatternStandardRegion = VG_INVALID_HANDLE; |
|
3235 } |
|
3236 |
|
3237 // Check the size of the existing standard brush as it can't be re-used if the |
|
3238 // new brush to be created is a different size. |
|
3239 if (iBrushPatternStandard != VG_INVALID_HANDLE) |
|
3240 { |
|
3241 if (iBrushPatternStandardSize != brushSize) |
|
3242 { |
|
3243 vgDestroyImage(iBrushPatternStandard); |
|
3244 iBrushPatternStandard = VG_INVALID_HANDLE; |
|
3245 } |
|
3246 } |
|
3247 |
|
3248 if (iBrushPatternStandard == VG_INVALID_HANDLE) |
|
3249 { |
|
3250 // Create an image to draw the brush pattern onto, this will be our offscreen buffer |
|
3251 iBrushPatternStandardSize = brushSize; |
|
3252 iBrushPatternStandard = DoVgCreateImage(dataFormat, brushSize.iWidth, brushSize.iHeight, VG_IMAGE_QUALITY_NONANTIALIASED); |
|
3253 if (iBrushPatternStandard == VG_INVALID_HANDLE) |
|
3254 err = KErrNoMemory; |
|
3255 } |
|
3256 } |
|
3257 |
|
3258 if (err == KErrNone) |
|
3259 { |
|
3260 // Set the colour used for the pen. If not fully opaque and not in WriteAlpha mode, |
|
3261 // blend the pen colour with the brush colour. |
|
3262 TRgb penColor = iPenColor; |
|
3263 if ((iPenColor.Alpha() != 255) && (iDrawMode != DirectGdi::EDrawModeWriteAlpha)) |
|
3264 { |
|
3265 penColor.SetInternal( |
|
3266 PMA2NonPMAPixel( |
|
3267 PMAPixelBlend( |
|
3268 NonPMA2PMAPixel(iBrushColor.Internal()), |
|
3269 NonPMA2PMAPixel(iPenColor.Internal())))); |
|
3270 } |
|
3271 |
|
3272 // Draw the pattern on to the brush, pixel by pixel. |
|
3273 for (TInt j = 0; j < aPatternSize.iHeight; ++j) |
|
3274 { |
|
3275 for (TInt i = 0; i < aPatternSize.iWidth; ++i) |
|
3276 { |
|
3277 if (aBrushPattern[(j*aPatternSize.iWidth)+i]) |
|
3278 { |
|
3279 data[(j*brushSize.iWidth)+i] = penColor._Color16MA(); |
|
3280 } |
|
3281 else |
|
3282 { |
|
3283 data[(j*brushSize.iWidth)+i] = iBrushColor._Color16MA(); |
|
3284 } |
|
3285 } |
|
3286 } |
|
3287 |
|
3288 // Copy the pattern to the VGImage so we can set it as the current brush |
|
3289 vgImageSubData(iBrushPatternStandard, // the image to copy to |
|
3290 data, // the source data |
|
3291 brushSize.iWidth*4, // the stride of the source data |
|
3292 dataFormat, // the format of the source data |
|
3293 0, // x |
|
3294 0, // y |
|
3295 brushSize.iWidth, // width |
|
3296 brushSize.iHeight); // height |
|
3297 iBrushPatternStandardSize = brushSize; |
|
3298 |
|
3299 // We only want to use the region of the brush we just created that is the size of the pattern |
|
3300 iBrushPatternStandardRegion = vgChildImage(iBrushPatternStandard, |
|
3301 0, |
|
3302 0, |
|
3303 aPatternSize.iWidth, |
|
3304 aPatternSize.iHeight); |
|
3305 iBrushPatternStandardRegionSize = aPatternSize; |
|
3306 } |
|
3307 |
|
3308 // Clean up |
|
3309 delete [] data; |
|
3310 |
|
3311 return err; |
|
3312 } |
|
3313 |
|
3314 /** |
|
3315 Internal function to return the currently active brush and its properties. |
|
3316 @param aBrush On success, holds the current VGImage brush being used. |
|
3317 @param aSize On success, holds the dimensions of VGImage being used as the brush. |
|
3318 @param aFormat On success, holds the VGImageFormat of the brush. |
|
3319 @return ETrue if a brush is currently being used, EFalse otherwise. |
|
3320 */ |
|
3321 TBool CVgEngine::GetCurrentBrushPattern(VGImage& aBrush, TSize& aSize, VGImageFormat& aFormat) const |
|
3322 { |
|
3323 TBool success = ETrue; |
|
3324 if (iBrushStyle == DirectGdi::EPatternedBrush) |
|
3325 { |
|
3326 aBrush = iBrushPatternUser; |
|
3327 aSize = iBrushPatternUserSize; |
|
3328 aFormat = static_cast<VGImageFormat>(vgGetParameteri(aBrush, VG_IMAGE_FORMAT)); |
|
3329 } |
|
3330 else if (iBrushStyle > DirectGdi::EPatternedBrush) |
|
3331 { |
|
3332 aBrush = iBrushPatternStandardRegion; |
|
3333 aSize = iBrushPatternStandardRegionSize; |
|
3334 // Currently we only ever use VG_sARGB_8888 for the standard brush format. |
|
3335 aFormat = VG_sARGB_8888; |
|
3336 } |
|
3337 else |
|
3338 success = EFalse; |
|
3339 return success; |
|
3340 } |
|
3341 |
|
3342 /** |
|
3343 Copies the current brush pattern (if a brush pattern is set) into iBrushPatternNonZeroOrigin. |
|
3344 This function should only be used if the current brush origin is not (0,0). When copying the |
|
3345 current brush pattern, it is shifted to take into account the non-zero origin. This shifted brush |
|
3346 pattern should be used for all brush operations while a non-zero origin is set. |
|
3347 |
|
3348 @return KErrNone if successful, KErrNotFound if the brush pattern could not be copied. |
|
3349 */ |
|
3350 TInt CVgEngine::CopyCurrentBrushPatternForNonZeroOrigin() |
|
3351 { |
|
3352 MakeEngineCurrent(); |
|
3353 TInt ret = KErrNotFound; |
|
3354 VGImage brush = VG_INVALID_HANDLE; |
|
3355 TSize brushSize; |
|
3356 VGImageFormat imageFormat; |
|
3357 |
|
3358 if (GetCurrentBrushPattern(brush, brushSize, imageFormat)) |
|
3359 { |
|
3360 const TInt width = brushSize.iWidth; |
|
3361 const TInt height = brushSize.iHeight; |
|
3362 |
|
3363 if ((width != 0) && (height != 0)) |
|
3364 { |
|
3365 if (iBrushPatternNonZeroOrigin != VG_INVALID_HANDLE) |
|
3366 { |
|
3367 vgDestroyImage(iBrushPatternNonZeroOrigin); |
|
3368 iBrushPatternNonZeroOrigin = VG_INVALID_HANDLE; |
|
3369 } |
|
3370 |
|
3371 // Create the brush we are going to copy the current brush into |
|
3372 iBrushPatternNonZeroOrigin = DoVgCreateImage(imageFormat, |
|
3373 width, |
|
3374 height, |
|
3375 VG_IMAGE_QUALITY_FASTER); |
|
3376 if (iBrushPatternNonZeroOrigin != VG_INVALID_HANDLE) |
|
3377 { |
|
3378 TInt offsetX = width - (iBrushOrigin.iX % width); |
|
3379 TInt offsetY = height - (iBrushOrigin.iY % height); |
|
3380 |
|
3381 // Top left to bottom right |
|
3382 if (offsetX != 0 && offsetY != 0) // check the width and height we are copying are not 0 |
|
3383 vgCopyImage(iBrushPatternNonZeroOrigin, width-offsetX, height-offsetY, brush, 0, 0, offsetX, offsetY, VG_FALSE); |
|
3384 |
|
3385 // Top right to bottom left |
|
3386 if ((width-offsetX) != 0 && offsetY != 0) |
|
3387 vgCopyImage(iBrushPatternNonZeroOrigin, 0, height-offsetY, brush, offsetX, 0, width-offsetX, offsetY, VG_FALSE); |
|
3388 |
|
3389 // Bottom left to top right |
|
3390 if (offsetX != 0 && (height-offsetY) != 0) |
|
3391 vgCopyImage(iBrushPatternNonZeroOrigin, width-offsetX, 0, brush, 0, offsetY, offsetX, height-offsetY, VG_FALSE); |
|
3392 |
|
3393 // Bottom right to top left |
|
3394 if ((width-offsetX) != 0 && (height-offsetY) != 0) |
|
3395 vgCopyImage(iBrushPatternNonZeroOrigin, 0, 0, brush, offsetX, offsetY, width-offsetX, height-offsetY, VG_FALSE); |
|
3396 |
|
3397 // Paint with the new non-zero origin brush |
|
3398 vgPaintPattern(iBrush, iBrushPatternNonZeroOrigin); |
|
3399 |
|
3400 ret = KErrNone; |
|
3401 } |
|
3402 else |
|
3403 { |
|
3404 ret = KErrNoMemory; |
|
3405 } |
|
3406 } |
|
3407 } |
|
3408 |
|
3409 return ret; |
|
3410 } |
|
3411 |
|
3412 /** |
|
3413 Calculates the angle in degrees formed anti-clockwise between vector V1 and V2, where V1 is a |
|
3414 horizontal vector to the right from aOriginX, and V2 is the vector (aPointX-aOriginX, aPointY-aOriginY). |
|
3415 |
|
3416 @param aOriginX The x coordinate of a point which represents the origin of our calculation. |
|
3417 @param aOriginY The y coordinate of a point which represents the origin of our calculation. |
|
3418 @param aPointX The x coordinate of the point which defines the angle with the origin's x-axis. |
|
3419 @param aPointY The y coordinate of the point which defines the angle with the origin's y-axis. |
|
3420 @param aWidth Width of the rectangle which defines where to draw the ellipse. |
|
3421 @param aHeight Height of the rectangle which defines where to draw the ellipse. |
|
3422 |
|
3423 @panic DGDIAdapter 1006, if either width or height are less than 1 (debug-only). |
|
3424 @return The angle in degrees between the vectors V1 and V2 described above. |
|
3425 */ |
|
3426 TReal CVgEngine::GetAngleFromXAxisAnticlockwise(const TReal aOriginX, const TReal aOriginY, const TReal aPointX, const TReal aPointY, const TReal aWidth, const TReal aHeight) |
|
3427 { |
|
3428 GRAPHICS_ASSERT_DEBUG((aWidth > 0) && (aHeight > 0), EDirectGdiPanicInvalidParameter); |
|
3429 |
|
3430 // The angle is calculated from the radius line that joins the point to the origin. |
|
3431 // The point initially provided defines a line relative to the ellipse. |
|
3432 // But the VG spec states that the required angle is that for a perfect circle |
|
3433 // before that circle is scaled by the bounding rect into an ellipse. |
|
3434 // Therefore, downscale the position of the point relative to the origin, by the |
|
3435 // relative dimensions of the width/height of the ellipse to make it relative to |
|
3436 // the underlying circle. Then use the resulting circle radius line to calculate the angle. |
|
3437 |
|
3438 TReal angle = 0.0f; |
|
3439 TReal pointX = aPointX-aOriginX; |
|
3440 TReal pointY = aPointY-aOriginY; |
|
3441 |
|
3442 const TReal scalingFactor = (aWidth / aHeight); |
|
3443 pointY *= scalingFactor; |
|
3444 Math::ATan(angle, pointY, pointX); |
|
3445 |
|
3446 // Adjust the angle for Q2 and Q3 |
|
3447 if (pointY < 0) |
|
3448 angle = (KPi*2)+angle; |
|
3449 |
|
3450 return angle*KRadToDeg; |
|
3451 } |
|
3452 |
|
3453 /** |
|
3454 Method for drawing arcs or pies. An arc is a segment of an ellipse which is defined by a given rectangle. |
|
3455 The arc is drawn anti-clockwise from the arc start point to the arc end point. The arc start point is the |
|
3456 intersection between vectors from the centre of the ellipse to the given start position and the ellipse. |
|
3457 The arc end point is defined in the same way. |
|
3458 |
|
3459 @param aRect The rectangle which defines where to draw the ellipse. |
|
3460 @param aStart Position to be used in defining the arc start point. |
|
3461 @param aEnd Position to be used in defining the arc end point. |
|
3462 @param aArcType The type of arc to draw; an arc, a chord or a pie. |
|
3463 |
|
3464 @post Request to draw an arc has been accepted. There is no guarantee that the request |
|
3465 has been processed when the method returns. |
|
3466 |
|
3467 @see CVgEngine::DrawPie() |
|
3468 @see CVgEngine::DrawArc() |
|
3469 */ |
|
3470 void CVgEngine::DoDrawArc(const TRect& aRect, const TPoint& aStart, const TPoint& aEnd, VGUArcType aArcType) |
|
3471 { |
|
3472 MakeEngineCurrent(); |
|
3473 |
|
3474 // Only draw if we are not painting with a NULL pen and a NULL brush |
|
3475 if (iPaintMode == 0) |
|
3476 return; |
|
3477 |
|
3478 // If the pen width and height are the same then draw as normal. If they are different but we should be filling |
|
3479 // this shape we need to draw the filled area only as normal (not the outline). The outline of the shape is drawn |
|
3480 // in the block of code below to allow the effect of a different width and height pen to be applied. |
|
3481 if ((iPenSize.iWidth == iPenSize.iHeight) || ((iPaintMode & VG_FILL_PATH) && (aArcType == VGU_ARC_PIE))) |
|
3482 { |
|
3483 TReal originX = aRect.iTl.iX + (static_cast<TReal>(aRect.Width())*0.5); |
|
3484 TReal originY = aRect.iTl.iY + (static_cast<TReal>(aRect.Height())*0.5); |
|
3485 TReal startAngle = GetAngleFromXAxisAnticlockwise(originX, originY, aStart.iX, aStart.iY, aRect.Width(), aRect.Height()); |
|
3486 TReal endAngle = GetAngleFromXAxisAnticlockwise(originX, originY, aEnd.iX, aEnd.iY, aRect.Width(), aRect.Height()); |
|
3487 TReal extent = endAngle - startAngle; |
|
3488 |
|
3489 // The extent defines what direction the arc is drawn in, so make sure we always draw |
|
3490 // anti-clockwise (-ve extent) |
|
3491 if (extent > 0.0f) |
|
3492 extent -= 360.0f; |
|
3493 |
|
3494 // If the start and end points are the same, make sure we draw arc all the way round the ellipse |
|
3495 if ((aStart == aEnd) || (extent > -0.0001f)) |
|
3496 extent = -360.0f; |
|
3497 |
|
3498 // Before any vgu command, call SetError() as this stores the current vg error state (if any) in the |
|
3499 // driver. Some implementations of vgu clears error state so we'd lose error codes otherwise. |
|
3500 iDriver.SetError(KErrNone); |
|
3501 |
|
3502 if (PreparePath(iVgPath, 5)) // N.B. 5 is just an initial hint as to how large the path may be, not its final size |
|
3503 { |
|
3504 VGUErrorCode err = vguArc(iVgPath, |
|
3505 originX + 0.5f, |
|
3506 originY + 0.5f, |
|
3507 aRect.Width(), |
|
3508 aRect.Height(), |
|
3509 startAngle, |
|
3510 extent, |
|
3511 aArcType); |
|
3512 |
|
3513 if (err == VGU_NO_ERROR) |
|
3514 { |
|
3515 VGbitfield paintMode = iPaintMode; |
|
3516 if(aArcType == VGU_ARC_OPEN) |
|
3517 { |
|
3518 paintMode &= ~VG_FILL_PATH; |
|
3519 } |
|
3520 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
3521 vgDrawPath(iVgPath, (iPenSize.iWidth == iPenSize.iHeight) ? paintMode : VG_FILL_PATH); |
|
3522 } |
|
3523 else |
|
3524 { |
|
3525 SetVguError(err); |
|
3526 } |
|
3527 } |
|
3528 } |
|
3529 |
|
3530 if ((iPenSize.iWidth != iPenSize.iHeight) |
|
3531 && (iPaintMode & VG_STROKE_PATH) |
|
3532 && (iPenSize.iWidth != 0) && (iPenSize.iHeight != 0)) |
|
3533 { |
|
3534 // Setting a pen with different width and height is not available on OpenVG, so we need to scale |
|
3535 // the coordinates we are drawing and apply a scaling matrix that scales by the width and height |
|
3536 // of the pen to get the effect of a pen width different width and height. |
|
3537 TSize penSize = iPenSize; |
|
3538 SetPenSize(TSize(1, 1)); |
|
3539 vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); |
|
3540 vgScale(penSize.iWidth, penSize.iHeight); |
|
3541 VGfloat scaleX = 1.0f/(float)penSize.iWidth; |
|
3542 VGfloat scaleY = 1.0f/(float)penSize.iHeight; |
|
3543 |
|
3544 TReal originX = aRect.iTl.iX + (static_cast<TReal>(aRect.Width())*0.5); |
|
3545 TReal originY = aRect.iTl.iY + (static_cast<TReal>(aRect.Height())*0.5); |
|
3546 TReal startAngle = GetAngleFromXAxisAnticlockwise(originX, originY, aStart.iX, aStart.iY, aRect.Width(), aRect.Height()); |
|
3547 TReal endAngle = GetAngleFromXAxisAnticlockwise(originX, originY, aEnd.iX, aEnd.iY, aRect.Width(), aRect.Height()); |
|
3548 TReal extent = endAngle - startAngle; |
|
3549 |
|
3550 // The extent defines what direction the arc is drawn in, so make sure we always draw |
|
3551 // anti-clockwise (-ve extent) |
|
3552 if (extent > 0.0f) |
|
3553 extent -= 360.0f; |
|
3554 |
|
3555 // If the start and end points are the same, make sure we draw arc all the way round the ellipse |
|
3556 if ((aStart == aEnd) || (extent > -0.0001f)) |
|
3557 extent = -360.0f; |
|
3558 |
|
3559 // Before any vgu command, call SetError() as this stores the current vg error state (if any) in the |
|
3560 // driver. Some implementations of vgu clears error state so we'd lose error codes otherwise. |
|
3561 iDriver.SetError(KErrNone); |
|
3562 |
|
3563 if (PreparePath(iVgPath, 5)) |
|
3564 { |
|
3565 VGUErrorCode err = vguArc(iVgPath, |
|
3566 (originX + 0.5f) * scaleX, |
|
3567 (originY + 0.5f) * scaleY, |
|
3568 (float)aRect.Width() * scaleX, |
|
3569 (float)aRect.Height() * scaleY, |
|
3570 startAngle, |
|
3571 extent, |
|
3572 aArcType); |
|
3573 |
|
3574 if (err == VGU_NO_ERROR) |
|
3575 { |
|
3576 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
3577 vgDrawPath(iVgPath, VG_STROKE_PATH); |
|
3578 } |
|
3579 else |
|
3580 { |
|
3581 SetVguError(err); |
|
3582 } |
|
3583 } |
|
3584 |
|
3585 ResetVgMatrix(VG_MATRIX_PATH_USER_TO_SURFACE); |
|
3586 SetPenSize(penSize); |
|
3587 } |
|
3588 |
|
3589 } |
|
3590 |
|
3591 /** |
|
3592 Helper function for drawing a source as a CDirectGdiImageSource. |
|
3593 |
|
3594 @see DrawResource(const TRect&, const RDirectGdiDrawableSource&, DirectGdi::TGraphicsRotation). |
|
3595 */ |
|
3596 void CVgEngine::DoDrawResource(const TRect& aDestRect, |
|
3597 CDirectGdiImageSourceImpl* aSource, |
|
3598 DirectGdi::TGraphicsRotation aRotation) |
|
3599 |
|
3600 { |
|
3601 TSize size = aSource->Size(); |
|
3602 TRect srcRect(0, 0, size.iWidth, size.iHeight); |
|
3603 |
|
3604 DoDrawResource(aDestRect, aSource, srcRect, aRotation); |
|
3605 } |
|
3606 |
|
3607 /** |
|
3608 Helper function for drawing a source as a CDirectGdiImageSource. |
|
3609 |
|
3610 @see DrawResource(const TRect&, const RDirectGdiDrawableSource&, const TRect&, DirectGdi::TGraphicsRotation). |
|
3611 */ |
|
3612 void CVgEngine::DoDrawResource(const TRect& aDestRect, |
|
3613 CDirectGdiImageSourceImpl* aSource, |
|
3614 const TRect& aSourceRect, |
|
3615 DirectGdi::TGraphicsRotation aRotation) |
|
3616 { |
|
3617 // If the source rect is smaller than the actual source size then we need to create a child VGImage to draw |
|
3618 VGImage vgImage = aSource->VgImage(); |
|
3619 TSize size = aSource->Size(); |
|
3620 if ((aSourceRect.Width() < size.iWidth) || (aSourceRect.Height() < size.iHeight)) |
|
3621 { |
|
3622 vgImage = vgChildImage(vgImage, aSourceRect.iTl.iX, aSourceRect.iTl.iY, aSourceRect.Width(), aSourceRect.Height()); |
|
3623 } |
|
3624 |
|
3625 if (vgImage != VG_INVALID_HANDLE) |
|
3626 { |
|
3627 vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); |
|
3628 |
|
3629 TRect destRect(aDestRect); |
|
3630 TRect sourceRect(aSourceRect); |
|
3631 |
|
3632 if (aRotation == DirectGdi::EGraphicsRotationNone || |
|
3633 aRotation == DirectGdi::EGraphicsRotation180) |
|
3634 { |
|
3635 // Pixel-data in EGLImages appears to be upside down due to the Y-inversion |
|
3636 // effect of the Identity matrix. Therefore must undo the Y-inversion here |
|
3637 // and adjust destination rect accordingly. |
|
3638 const TInt destRectHeight = aDestRect.Height(); |
|
3639 destRect.iTl.iY = (iSize.iHeight - aDestRect.iTl.iY - iOrigin.iY) - destRectHeight; |
|
3640 destRect.iTl.iX += iOrigin.iX; |
|
3641 destRect.iBr.iX += iOrigin.iX; |
|
3642 destRect.iBr.iY = destRect.iTl.iY + destRectHeight; |
|
3643 sourceRect.iBr.iY = aSource->Size().iHeight - sourceRect.iTl.iY; |
|
3644 sourceRect.iTl.iY = sourceRect.iBr.iY - aSourceRect.Height(); |
|
3645 vgLoadIdentity(); |
|
3646 } |
|
3647 else |
|
3648 { |
|
3649 // But if rotation is 90 or 270 degrees, only need to mirror in the X-axis. |
|
3650 vgScale(-1, 1); |
|
3651 } |
|
3652 |
|
3653 VGfloat xScale = 1.f; |
|
3654 VGfloat yScale = 1.f; |
|
3655 VGint posX = destRect.iTl.iX; |
|
3656 VGint posY = destRect.iTl.iY; |
|
3657 |
|
3658 switch (aRotation) |
|
3659 { |
|
3660 case DirectGdi::EGraphicsRotation90: |
|
3661 xScale = ((VGfloat)destRect.Width()/(VGfloat)sourceRect.Height()); |
|
3662 yScale = ((VGfloat)destRect.Height()/(VGfloat)sourceRect.Width()); |
|
3663 vgTranslate(-posX, posY); |
|
3664 vgScale(xScale, yScale); |
|
3665 vgRotate(90.0f); |
|
3666 break; |
|
3667 case DirectGdi::EGraphicsRotation180: |
|
3668 xScale = ((VGfloat)destRect.Width()/(VGfloat)sourceRect.Width()); |
|
3669 yScale = ((VGfloat)destRect.Height()/(VGfloat)sourceRect.Height()); |
|
3670 vgTranslate(posX+destRect.Width(), posY+destRect.Height()); |
|
3671 vgScale(xScale, yScale); |
|
3672 vgRotate(180.0f); |
|
3673 break; |
|
3674 case DirectGdi::EGraphicsRotation270: |
|
3675 xScale = ((VGfloat)destRect.Width()/(VGfloat)sourceRect.Height()); |
|
3676 yScale = ((VGfloat)destRect.Height()/(VGfloat)sourceRect.Width()); |
|
3677 vgTranslate(-posX-destRect.Width(), (posY+destRect.Height())); |
|
3678 vgScale(xScale, yScale); |
|
3679 vgRotate(270.0f); |
|
3680 break; |
|
3681 default: // DirectGdi::EGraphicsRotationNone |
|
3682 xScale = ((VGfloat)destRect.Width()/(VGfloat)sourceRect.Width()); |
|
3683 yScale = ((VGfloat)destRect.Height()/(VGfloat)sourceRect.Height()); |
|
3684 vgTranslate(posX, posY); |
|
3685 vgScale(xScale, yScale); |
|
3686 break; |
|
3687 } |
|
3688 |
|
3689 for (iRegionManager.Begin(); iRegionManager.ApplyClipRegion(); iRegionManager.Next()) |
|
3690 vgDrawImage(vgImage); |
|
3691 |
|
3692 if (vgImage != aSource->VgImage()) |
|
3693 { |
|
3694 // Created a child VGImage, so destroy after use. |
|
3695 vgDestroyImage(vgImage); |
|
3696 } |
|
3697 |
|
3698 ResetVgMatrix(VG_MATRIX_IMAGE_USER_TO_SURFACE); |
|
3699 } |
|
3700 } |
|
3701 |
|
3702 /** |
|
3703 Maps a VGU error code to a Symbian OS error code, and sets the driver's error state. If the |
|
3704 error is unrecognised, the error is set to KErrGeneral. |
|
3705 |
|
3706 @param aErr The return value (error state) from a VGU command. |
|
3707 @post If empty, the driver's error state is updated to the mapped error code. |
|
3708 */ |
|
3709 void CVgEngine::SetVguError(VGUErrorCode aErr) |
|
3710 { |
|
3711 switch(aErr) |
|
3712 { |
|
3713 case VGU_NO_ERROR: |
|
3714 break; |
|
3715 case VGU_OUT_OF_MEMORY_ERROR: |
|
3716 iDriver.SetError(KErrNoMemory); |
|
3717 break; |
|
3718 case VGU_BAD_HANDLE_ERROR: |
|
3719 iDriver.SetError(KErrBadHandle); |
|
3720 break; |
|
3721 case VGU_ILLEGAL_ARGUMENT_ERROR: |
|
3722 iDriver.SetError(KErrArgument); |
|
3723 break; |
|
3724 case VGU_PATH_CAPABILITY_ERROR: |
|
3725 iDriver.SetError(KErrNotSupported); |
|
3726 break; |
|
3727 default: |
|
3728 iDriver.SetError(KErrGeneral); |
|
3729 break; |
|
3730 } |
|
3731 } |
|
3732 |
|
3733 /** |
|
3734 Helper method for creating a VGImage. This method clears the VG image cache in an attempt to |
|
3735 reclaim some memory if creation of a VGImage fails due to no memory being available, |
|
3736 it then tries to create the image again. If image creation fails again it then clears the glyph |
|
3737 cache and tries to create the image again. If image creation still fails, OpenVG is forced to |
|
3738 complete all currently outstanding drawing requests, so that any OpenVG objects marked for deletion, |
|
3739 such as VGImages that are currently waiting to be drawn, are freed. This is an attempt to make sure |
|
3740 that images are still displayed in low memory conditions. Use this method instead of calling |
|
3741 vgCreateImage() directly. Clearing the VG image cache in this way will have a negative effect on |
|
3742 performance regarding speed, but it is more important that an attempt is made to draw something |
|
3743 when memory is low. |
|
3744 |
|
3745 @param aFormat The pixel format of the image to be created |
|
3746 @param aWidth The width of the image to be created |
|
3747 @param aHeight The height of the image to be created |
|
3748 @param aAllowedQuality One of the VGImageQuality flags |
|
3749 |
|
3750 @return A VGImage handle if the image was created successfully, VG_INVALID_HANDLE otherwise |
|
3751 */ |
|
3752 VGImage CVgEngine::DoVgCreateImage(VGImageFormat aFormat, VGint aWidth, VGint aHeight, VGbitfield aAllowedQuality) |
|
3753 { |
|
3754 const TInt oldVgError = CDirectGdiDriverImpl::GetVgError(); |
|
3755 VGImage imageRet = vgCreateImage(aFormat, aWidth, aHeight, aAllowedQuality); |
|
3756 |
|
3757 if (imageRet == VG_INVALID_HANDLE) |
|
3758 { |
|
3759 // If the new error is anything other than KErrNoMemory, there is nothing that can be done. |
|
3760 TInt newVgError = CDirectGdiDriverImpl::GetVgError(); |
|
3761 if (newVgError != KErrNoMemory) |
|
3762 { |
|
3763 iDriver.SetError(oldVgError != KErrNone ? oldVgError : newVgError); |
|
3764 return imageRet; |
|
3765 } |
|
3766 |
|
3767 // From here on, we are assuming any failure to create the image is due to OOM. |
|
3768 if (iDriver.VgImageCache()) |
|
3769 { |
|
3770 // Delete all the images that are currently in the cache then try and create the image again |
|
3771 iDriver.VgImageCache()->ResetCache(); |
|
3772 imageRet = vgCreateImage(aFormat, aWidth, aHeight, aAllowedQuality); |
|
3773 } |
|
3774 if ((imageRet == VG_INVALID_HANDLE) && iFontGlyphImageStorage) |
|
3775 { |
|
3776 // Clear the glyph cache as well then try and create the image again |
|
3777 iFontGlyphImageStorage->CleanGlyphImageCache(); |
|
3778 imageRet = vgCreateImage(aFormat, aWidth, aHeight, aAllowedQuality); |
|
3779 } |
|
3780 if ((imageRet == VG_INVALID_HANDLE)) |
|
3781 { |
|
3782 // Finally, force completion of any outstanding drawing, may free any VGImages marked for deletion. |
|
3783 // Empty the current OpenVG error state before calling Finish(), as Finish() may call SetError(), |
|
3784 // and could prematurely set the driver error state to KErrNoMemory. |
|
3785 //coverity[check_return] |
|
3786 //coverity[unchecked_value] |
|
3787 vgGetError(); |
|
3788 iDriver.Finish(); |
|
3789 imageRet = vgCreateImage(aFormat, aWidth, aHeight, aAllowedQuality); |
|
3790 } |
|
3791 |
|
3792 // If the above worked, empty any OpenVG error state set by any failed attempts to create the image. |
|
3793 if (imageRet != VG_INVALID_HANDLE) |
|
3794 { |
|
3795 //coverity[check_return] |
|
3796 //coverity[unchecked_value] |
|
3797 vgGetError(); |
|
3798 } |
|
3799 } |
|
3800 |
|
3801 // Reset the error code to the original VG error code. If oldVgError is KErrNone, |
|
3802 // SetError() will use the current OpenVG error state. |
|
3803 iDriver.SetError(oldVgError); |
|
3804 |
|
3805 return imageRet; |
|
3806 } |
|
3807 |
|
3808 /** |
|
3809 Helper method for setting the colour property of a VGPaint object from a TRgb structure. |
|
3810 |
|
3811 @param aPaint The VGPaint object to change the colour property of. |
|
3812 @param aColor The colour to set the paint to. |
|
3813 */ |
|
3814 void CVgEngine::SetVgPaintColor(VGPaint& aPaint, const TRgb& aColor) |
|
3815 { |
|
3816 // Need to swap from internal ARGB to RGBA for OpenVG. |
|
3817 const TUint argb = aColor.Internal(); |
|
3818 const VGuint rgba = ((argb & 0xFFFFFF) << 8) + ((argb & 0xFF000000) >> 24); |
|
3819 vgSetColor(aPaint, rgba); |
|
3820 } |