Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/thebes/gfxContext.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
 * This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#ifndef GFX_CONTEXT_H
7
#define GFX_CONTEXT_H
8
9
#include "gfxTypes.h"
10
11
#include "gfxASurface.h"
12
#include "gfxPoint.h"
13
#include "gfxRect.h"
14
#include "gfxMatrix.h"
15
#include "gfxPattern.h"
16
#include "nsTArray.h"
17
18
#include "mozilla/gfx/2D.h"
19
20
typedef struct _cairo cairo_t;
21
class GlyphBufferAzure;
22
23
namespace mozilla {
24
namespace gfx {
25
struct RectCornerRadii;
26
} // namespace gfx
27
namespace layout {
28
class TextDrawTarget;
29
} // namespace layout
30
} // namespace mozilla
31
32
class ClipExporter;
33
34
/**
35
 * This is the main class for doing actual drawing. It is initialized using
36
 * a surface and can be drawn on. It manages various state information like
37
 * a current transformation matrix (CTM), a current path, current color,
38
 * etc.
39
 *
40
 * All drawing happens by creating a path and then stroking or filling it.
41
 * The functions like Rectangle and Arc do not do any drawing themselves.
42
 * When a path is drawn (stroked or filled), it is filled/stroked with a
43
 * pattern set by SetPattern or SetColor.
44
 *
45
 * Note that the gfxContext takes coordinates in device pixels,
46
 * as opposed to app units.
47
 */
48
class gfxContext final {
49
    typedef mozilla::gfx::CapStyle CapStyle;
50
    typedef mozilla::gfx::CompositionOp CompositionOp;
51
    typedef mozilla::gfx::JoinStyle JoinStyle;
52
    typedef mozilla::gfx::FillRule FillRule;
53
    typedef mozilla::gfx::Float Float;
54
    typedef mozilla::gfx::Path Path;
55
    typedef mozilla::gfx::Pattern Pattern;
56
    typedef mozilla::gfx::Rect Rect;
57
    typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
58
    typedef mozilla::gfx::Size Size;
59
60
    NS_INLINE_DECL_REFCOUNTING(gfxContext)
61
62
public:
63
    /**
64
     * Initialize this context from a DrawTarget.
65
     * Strips any transform from aTarget.
66
     * aTarget will be flushed in the gfxContext's destructor.
67
     * If aTarget is null or invalid, nullptr is returned.  The caller
68
     * is responsible for handling this scenario as appropriate.
69
     */
70
    static already_AddRefed<gfxContext>
71
        CreateOrNull(mozilla::gfx::DrawTarget* aTarget,
72
                     const mozilla::gfx::Point& aDeviceOffset = mozilla::gfx::Point());
73
74
    /**
75
     * Create a new gfxContext wrapping aTarget and preserving aTarget's
76
     * transform. Note that the transform is moved from aTarget to the resulting
77
     * gfxContext, aTarget will no longer have its transform.
78
     * If aTarget is null or invalid, nullptr is returned.  The caller
79
     * is responsible for handling this scenario as appropriate.
80
     */
81
    static already_AddRefed<gfxContext>
82
        CreatePreservingTransformOrNull(mozilla::gfx::DrawTarget* aTarget);
83
84
    mozilla::gfx::DrawTarget *GetDrawTarget() { return mDT; }
85
86
    /**
87
     * Returns the DrawTarget if it's actually a TextDrawTarget.
88
     */
89
    mozilla::layout::TextDrawTarget* GetTextDrawer();
90
91
    /**
92
     ** State
93
     **/
94
    // XXX document exactly what bits are saved
95
    void Save();
96
    void Restore();
97
98
    /**
99
     ** Paths & Drawing
100
     **/
101
102
    /**
103
     * Fill the current path according to the current settings.
104
     *
105
     * Does not consume the current path.
106
     */
107
    void Fill();
108
    void Fill(const Pattern& aPattern);
109
110
    /**
111
     * Forgets the current path.
112
     */
113
    void NewPath();
114
115
    /**
116
     * Closes the path, i.e. connects the last drawn point to the first one.
117
     *
118
     * Filling a path will implicitly close it.
119
     */
120
    void ClosePath();
121
122
    /**
123
     * Returns the current path.
124
     */
125
    already_AddRefed<Path> GetPath();
126
127
    /**
128
     * Sets the given path as the current path.
129
     */
130
    void SetPath(Path* path);
131
132
    /**
133
     * Moves the pen to a new point without drawing a line.
134
     */
135
    void MoveTo(const gfxPoint& pt);
136
137
    /**
138
     * Draws a line from the current point to pt.
139
     *
140
     * @see MoveTo
141
     */
142
    void LineTo(const gfxPoint& pt);
143
144
    // path helpers
145
    /**
146
     * Draws a line from start to end.
147
     */
148
    void Line(const gfxPoint& start, const gfxPoint& end); // XXX snapToPixels option?
149
150
    /**
151
     * Draws the rectangle given by rect.
152
     * @param snapToPixels ?
153
     */
154
    void Rectangle(const gfxRect& rect, bool snapToPixels = false);
155
    void SnappedRectangle(const gfxRect& rect) { return Rectangle(rect, true); }
156
157
    /**
158
     ** Transformation Matrix manipulation
159
     **/
160
161
    /**
162
     * Post-multiplies 'other' onto the current CTM, i.e. this
163
     * matrix's transformation will take place before the previously set
164
     * transformations.
165
     */
166
    void Multiply(const gfxMatrix& other);
167
168
    /**
169
     * Replaces the current transformation matrix with matrix.
170
     */
171
    void SetMatrix(const mozilla::gfx::Matrix& matrix);
172
    void SetMatrixDouble(const gfxMatrix& matrix);
173
174
    /**
175
     * Returns the current transformation matrix.
176
     */
177
    mozilla::gfx::Matrix CurrentMatrix() const;
178
    gfxMatrix CurrentMatrixDouble() const;
179
180
    /**
181
     * Converts a point from device to user coordinates using the inverse
182
     * transformation matrix.
183
     */
184
    gfxPoint DeviceToUser(const gfxPoint& point) const;
185
186
    /**
187
     * Converts a size from device to user coordinates. This does not apply
188
     * translation components of the matrix.
189
     */
190
    Size DeviceToUser(const Size& size) const;
191
192
    /**
193
     * Converts a rectangle from device to user coordinates; this has the
194
     * same effect as using DeviceToUser on both the rectangle's point and
195
     * size.
196
     */
197
    gfxRect DeviceToUser(const gfxRect& rect) const;
198
199
    /**
200
     * Converts a point from user to device coordinates using the transformation
201
     * matrix.
202
     */
203
    gfxPoint UserToDevice(const gfxPoint& point) const;
204
205
    /**
206
     * Converts a size from user to device coordinates. This does not apply
207
     * translation components of the matrix.
208
     */
209
    Size UserToDevice(const Size& size) const;
210
211
    /**
212
     * Converts a rectangle from user to device coordinates.  The
213
     * resulting rectangle is the minimum device-space rectangle that
214
     * encloses the user-space rectangle given.
215
     */
216
    gfxRect UserToDevice(const gfxRect& rect) const;
217
218
    /**
219
     * Takes the given rect and tries to align it to device pixels.  If
220
     * this succeeds, the method will return true, and the rect will
221
     * be in device coordinates (already transformed by the CTM).  If it
222
     * fails, the method will return false, and the rect will not be
223
     * changed.
224
     *
225
     * If ignoreScale is true, then snapping will take place even if
226
     * the CTM has a scale applied.  Snapping never takes place if
227
     * there is a rotation in the CTM.
228
     */
229
    bool UserToDevicePixelSnapped(gfxRect& rect, bool ignoreScale = false) const;
230
231
    /**
232
     * Takes the given point and tries to align it to device pixels.  If
233
     * this succeeds, the method will return true, and the point will
234
     * be in device coordinates (already transformed by the CTM).  If it
235
     * fails, the method will return false, and the point will not be
236
     * changed.
237
     *
238
     * If ignoreScale is true, then snapping will take place even if
239
     * the CTM has a scale applied.  Snapping never takes place if
240
     * there is a rotation in the CTM.
241
     */
242
    bool UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale = false) const;
243
244
    /**
245
     ** Painting sources
246
     **/
247
248
    /**
249
     * Set a solid color to use for drawing.  This color is in the device color space
250
     * and is not transformed.
251
     */
252
    void SetDeviceColor(const mozilla::gfx::Color& aColor);
253
254
    /**
255
     * Gets the current color.  It's returned in the device color space.
256
     * returns false if there is something other than a color
257
     *         set as the current source (pattern, surface, etc)
258
     */
259
    bool GetDeviceColor(mozilla::gfx::Color& aColorOut);
260
261
    /**
262
     * Returns true if color is neither opaque nor transparent (i.e. alpha is not 0
263
     * or 1), and false otherwise. If true, aColorOut is set on output.
264
     */
265
    bool HasNonOpaqueNonTransparentColor(mozilla::gfx::Color& aColorOut) {
266
        return GetDeviceColor(aColorOut) &&
267
               0.f < aColorOut.a && aColorOut.a < 1.f;
268
    }
269
270
    /**
271
     * Set a solid color in the sRGB color space to use for drawing.
272
     * If CMS is not enabled, the color is treated as a device-space color
273
     * and this call is identical to SetDeviceColor().
274
     */
275
    void SetColor(const mozilla::gfx::Color& aColor);
276
277
    /**
278
     * Uses a pattern for drawing.
279
     */
280
    void SetPattern(gfxPattern *pattern);
281
282
    /**
283
     * Get the source pattern (solid color, normal pattern, surface, etc)
284
     */
285
    already_AddRefed<gfxPattern> GetPattern();
286
287
    /**
288
     ** Painting
289
     **/
290
    /**
291
     * Paints the current source surface/pattern everywhere in the current
292
     * clip region.
293
     */
294
    void Paint(Float alpha = 1.0);
295
296
    /**
297
     ** Painting with a Mask
298
     **/
299
    /**
300
     * Like Paint, except that it only draws the source where pattern is
301
     * non-transparent.
302
     */
303
    void Mask(mozilla::gfx::SourceSurface *aSurface, mozilla::gfx::Float aAlpha, const mozilla::gfx::Matrix& aTransform);
304
    void Mask(mozilla::gfx::SourceSurface *aSurface, const mozilla::gfx::Matrix& aTransform) { Mask(aSurface, 1.0f, aTransform); }
305
    void Mask(mozilla::gfx::SourceSurface *surface, float alpha = 1.0f, const mozilla::gfx::Point& offset = mozilla::gfx::Point());
306
307
    /**
308
     ** Line Properties
309
     **/
310
311
    void SetDash(const Float *dashes, int ndash, Float offset);
312
    // Return true if dashing is set, false if it's not enabled or the
313
    // context is in an error state.  |offset| can be nullptr to mean
314
    // "don't care".
315
    bool CurrentDash(FallibleTArray<Float>& dashes, Float* offset) const;
316
    // Returns 0.0 if dashing isn't enabled.
317
    Float CurrentDashOffset() const;
318
319
    /**
320
     * Sets the line width that's used for line drawing.
321
     */
322
    void SetLineWidth(Float width);
323
324
    /**
325
     * Returns the currently set line width.
326
     *
327
     * @see SetLineWidth
328
     */
329
    Float CurrentLineWidth() const;
330
331
    /**
332
     * Sets the line caps, i.e. how line endings are drawn.
333
     */
334
    void SetLineCap(CapStyle cap);
335
    CapStyle CurrentLineCap() const;
336
337
    /**
338
     * Sets the line join, i.e. how the connection between two lines is
339
     * drawn.
340
     */
341
    void SetLineJoin(JoinStyle join);
342
    JoinStyle CurrentLineJoin() const;
343
344
    void SetMiterLimit(Float limit);
345
    Float CurrentMiterLimit() const;
346
347
    /**
348
     * Sets the operator used for all further drawing. The operator affects
349
     * how drawing something will modify the destination. For example, the
350
     * OVER operator will do alpha blending of source and destination, while
351
     * SOURCE will replace the destination with the source.
352
     */
353
    void SetOp(CompositionOp op);
354
    CompositionOp CurrentOp() const;
355
356
    void SetAntialiasMode(mozilla::gfx::AntialiasMode mode);
357
    mozilla::gfx::AntialiasMode CurrentAntialiasMode() const;
358
359
    /**
360
     ** Clipping
361
     **/
362
363
    /**
364
     * Clips all further drawing to the current path.
365
     * This does not consume the current path.
366
     */
367
    void Clip();
368
369
    /**
370
     * Helper functions that will create a rect path and call Clip().
371
     * Any current path will be destroyed by these functions!
372
     */
373
    void Clip(const Rect& rect);
374
    void Clip(const gfxRect& rect); // will clip to a rect
375
    void Clip(Path* aPath);
376
377
    void PopClip();
378
379
    enum ClipExtentsSpace {
380
        eUserSpace = 0,
381
        eDeviceSpace = 1,
382
    };
383
384
    /**
385
     * According to aSpace, this function will return the current bounds of
386
     * the clip region in user space or device space.
387
     */
388
    gfxRect GetClipExtents(ClipExtentsSpace aSpace = eUserSpace) const;
389
390
    /**
391
     * Returns true if the given rectangle is fully contained in the current clip.
392
     * This is conservative; it may return false even when the given rectangle is
393
     * fully contained by the current clip.
394
     */
395
    bool ClipContainsRect(const gfxRect& aRect);
396
397
     /**
398
      * Exports the current clip using the provided exporter.
399
      */
400
    bool ExportClip(ClipExporter& aExporter);
401
402
    /**
403
     * Groups
404
     */
405
    void PushGroupForBlendBack(gfxContentType content, mozilla::gfx::Float aOpacity = 1.0f,
406
                               mozilla::gfx::SourceSurface* aMask = nullptr,
407
                               const mozilla::gfx::Matrix& aMaskTransform = mozilla::gfx::Matrix());
408
409
    /**
410
     * Like PushGroupForBlendBack, but if the current surface is gfxContentType::COLOR and
411
     * content is gfxContentType::COLOR_ALPHA, makes the pushed surface gfxContentType::COLOR
412
     * instead and copies the contents of the current surface to the pushed
413
     * surface. This is good for pushing opacity groups, since blending the
414
     * group back to the current surface with some alpha applied will give
415
     * the correct results and using an opaque pushed surface gives better
416
     * quality and performance.
417
     */
418
    void PushGroupAndCopyBackground(gfxContentType content = gfxContentType::COLOR,
419
                                    mozilla::gfx::Float aOpacity = 1.0f,
420
                                    mozilla::gfx::SourceSurface* aMask = nullptr,
421
                                    const mozilla::gfx::Matrix& aMaskTransform = mozilla::gfx::Matrix());
422
    void PopGroupAndBlend();
423
424
    mozilla::gfx::Point GetDeviceOffset() const;
425
426
#ifdef MOZ_DUMP_PAINTING
427
    /**
428
     * Debug functions to encode the current surface as a PNG and export it.
429
     */
430
431
    /**
432
     * Writes a binary PNG file.
433
     */
434
    void WriteAsPNG(const char* aFile);
435
436
    /**
437
     * Write as a PNG encoded Data URL to stdout.
438
     */
439
    void DumpAsDataURI();
440
441
    /**
442
     * Copy a PNG encoded Data URL to the clipboard.
443
     */
444
    void CopyAsDataURI();
445
#endif
446
447
    static mozilla::gfx::UserDataKey sDontUseAsSourceKey;
448
449
private:
450
451
    /**
452
     * Initialize this context from a DrawTarget.
453
     * Strips any transform from aTarget.
454
     * aTarget will be flushed in the gfxContext's destructor.  Use the static
455
     * ContextForDrawTargetNoTransform() when you want this behavior, as that
456
     * version deals with null DrawTarget better.
457
     */
458
    explicit gfxContext(mozilla::gfx::DrawTarget *aTarget,
459
                        const mozilla::gfx::Point& aDeviceOffset = mozilla::gfx::Point());
460
    ~gfxContext();
461
462
  friend class PatternFromState;
463
  friend class GlyphBufferAzure;
464
465
  typedef mozilla::gfx::Matrix Matrix;
466
  typedef mozilla::gfx::DrawTarget DrawTarget;
467
  typedef mozilla::gfx::Color Color;
468
  typedef mozilla::gfx::StrokeOptions StrokeOptions;
469
  typedef mozilla::gfx::PathBuilder PathBuilder;
470
  typedef mozilla::gfx::SourceSurface SourceSurface;
471
472
  struct AzureState {
473
    AzureState()
474
      : op(mozilla::gfx::CompositionOp::OP_OVER)
475
      , color(0, 0, 0, 1.0f)
476
      , aaMode(mozilla::gfx::AntialiasMode::SUBPIXEL)
477
      , patternTransformChanged(false)
478
#ifdef DEBUG
479
      , mContentChanged(false)
480
#endif
481
    {}
482
483
    mozilla::gfx::CompositionOp op;
484
    Color color;
485
    RefPtr<gfxPattern> pattern;
486
    Matrix transform;
487
    struct PushedClip {
488
      RefPtr<Path> path;
489
      Rect rect;
490
      Matrix transform;
491
    };
492
    nsTArray<PushedClip> pushedClips;
493
    nsTArray<Float> dashPattern;
494
    StrokeOptions strokeOptions;
495
    RefPtr<DrawTarget> drawTarget;
496
    mozilla::gfx::AntialiasMode aaMode;
497
    bool patternTransformChanged;
498
    Matrix patternTransform;
499
    Color fontSmoothingBackgroundColor;
500
    // This is used solely for using minimal intermediate surface size.
501
    mozilla::gfx::Point deviceOffset;
502
#ifdef DEBUG
503
    // Whether the content of this AzureState changed after construction.
504
    bool mContentChanged;
505
#endif
506
  };
507
508
  // This ensures mPath contains a valid path (in user space!)
509
  void EnsurePath();
510
  // This ensures mPathBuilder contains a valid PathBuilder (in user space!)
511
  void EnsurePathBuilder();
512
  CompositionOp GetOp();
513
  void ChangeTransform(const mozilla::gfx::Matrix &aNewMatrix, bool aUpdatePatternTransform = true);
514
  Rect GetAzureDeviceSpaceClipBounds() const;
515
  Matrix GetDeviceTransform() const;
516
  Matrix GetDTTransform() const;
517
518
  bool mPathIsRect;
519
  bool mTransformChanged;
520
  Matrix mPathTransform;
521
  Rect mRect;
522
  RefPtr<PathBuilder> mPathBuilder;
523
  RefPtr<Path> mPath;
524
  Matrix mTransform;
525
  nsTArray<AzureState> mStateStack;
526
527
  AzureState &CurrentState() { return mStateStack[mStateStack.Length() - 1]; }
528
  const AzureState &CurrentState() const { return mStateStack[mStateStack.Length() - 1]; }
529
530
  RefPtr<DrawTarget> mDT;
531
};
532
533
/**
534
 * Sentry helper class for functions with multiple return points that need to
535
 * call Save() on a gfxContext and have Restore() called automatically on the
536
 * gfxContext before they return.
537
 */
538
class gfxContextAutoSaveRestore
539
{
540
public:
541
  gfxContextAutoSaveRestore() : mContext(nullptr) {}
542
543
0
  explicit gfxContextAutoSaveRestore(gfxContext *aContext) : mContext(aContext) {
544
0
    mContext->Save();
545
0
  }
546
547
  ~gfxContextAutoSaveRestore() {
548
    Restore();
549
  }
550
551
  void SetContext(gfxContext *aContext) {
552
    NS_ASSERTION(!mContext, "Not going to call Restore() on some context!!!");
553
    mContext = aContext;
554
    mContext->Save();
555
  }
556
557
  void EnsureSaved(gfxContext *aContext) {
558
    MOZ_ASSERT(!mContext || mContext == aContext, "wrong context");
559
    if (!mContext) {
560
        mContext = aContext;
561
        mContext->Save();
562
    }
563
  }
564
565
  void Restore() {
566
    if (mContext) {
567
      mContext->Restore();
568
      mContext = nullptr;
569
    }
570
  }
571
572
private:
573
  gfxContext *mContext;
574
};
575
576
/**
577
 * Sentry helper class for functions with multiple return points that need to
578
 * back up the current matrix of a context and have it automatically restored
579
 * before they return.
580
 */
581
class gfxContextMatrixAutoSaveRestore
582
{
583
public:
584
    gfxContextMatrixAutoSaveRestore() :
585
      mContext(nullptr)
586
    {
587
    }
588
589
    explicit gfxContextMatrixAutoSaveRestore(gfxContext *aContext) :
590
      mContext(aContext), mMatrix(aContext->CurrentMatrix())
591
    {
592
    }
593
594
    ~gfxContextMatrixAutoSaveRestore()
595
    {
596
      if (mContext) {
597
        mContext->SetMatrix(mMatrix);
598
      }
599
    }
600
601
    void SetContext(gfxContext *aContext)
602
    {
603
      NS_ASSERTION(!mContext,
604
                   "Not going to restore the matrix on some context!");
605
      mContext = aContext;
606
      mMatrix = aContext->CurrentMatrix();
607
    }
608
609
    void Restore()
610
    {
611
      if (mContext) {
612
        mContext->SetMatrix(mMatrix);
613
        mContext = nullptr;
614
      }
615
    }
616
617
    const mozilla::gfx::Matrix& Matrix()
618
    {
619
      MOZ_ASSERT(mContext, "mMatrix doesn't contain a useful matrix");
620
      return mMatrix;
621
    }
622
623
    bool HasMatrix() const { return !!mContext; }
624
625
private:
626
    gfxContext *mContext;
627
    mozilla::gfx::Matrix mMatrix;
628
};
629
630
631
class DrawTargetAutoDisableSubpixelAntialiasing {
632
public:
633
    typedef mozilla::gfx::DrawTarget DrawTarget;
634
635
    DrawTargetAutoDisableSubpixelAntialiasing(DrawTarget* aDT, bool aDisable) :
636
      mSubpixelAntialiasingEnabled(false)
637
    {
638
        if (aDisable) {
639
            mDT = aDT;
640
            mSubpixelAntialiasingEnabled = mDT->GetPermitSubpixelAA();
641
            mDT->SetPermitSubpixelAA(false);
642
        }
643
    }
644
    ~DrawTargetAutoDisableSubpixelAntialiasing()
645
    {
646
        if (mDT) {
647
            mDT->SetPermitSubpixelAA(mSubpixelAntialiasingEnabled);
648
        }
649
    }
650
651
private:
652
    RefPtr<DrawTarget> mDT;
653
    bool mSubpixelAntialiasingEnabled;
654
};
655
656
/* This class lives on the stack and allows gfxContext users to easily, and
657
 * performantly get a gfx::Pattern to use for drawing in their current context.
658
 */
659
class PatternFromState
660
{
661
public:
662
  explicit PatternFromState(gfxContext *aContext) : mContext(aContext), mPattern(nullptr) {}
663
  ~PatternFromState() { if (mPattern) { mPattern->~Pattern(); } }
664
665
  operator mozilla::gfx::Pattern&();
666
667
private:
668
  union {
669
    mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern;
670
    mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern;
671
  };
672
673
  gfxContext *mContext;
674
  mozilla::gfx::Pattern *mPattern;
675
};
676
677
/* This interface should be implemented to handle exporting the clip from a context.
678
 */
679
class ClipExporter : public mozilla::gfx::PathSink {
680
public:
681
  virtual void BeginClip(const mozilla::gfx::Matrix& aMatrix) = 0;
682
  virtual void EndClip() = 0;
683
};
684
685
#endif /* GFX_CONTEXT_H */