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 #include "PrintTarget.h"
7 
8 #include "cairo.h"
9 #include "mozilla/gfx/2D.h"
10 #include "mozilla/gfx/Logging.h"
11 
12 namespace mozilla {
13 namespace gfx {
14 
15 PrintTarget::PrintTarget(cairo_surface_t* aCairoSurface, const IntSize& aSize)
16   : mCairoSurface(aCairoSurface)
17   , mSize(aSize)
18   , mIsFinished(false)
19 #ifdef DEBUG
20   , mHasActivePage(false)
21 #endif
22 
23 {
24 #if 0
25   // aCairoSurface is null when our PrintTargetThebes subclass's ctor calls us.
26   // Once PrintTargetThebes is removed, enable this assertion.
27   MOZ_ASSERT(aCairoSurface && !cairo_surface_status(aCairoSurface),
28              "CreateOrNull factory methods should not call us without a "
29              "valid cairo_surface_t*");
30 #endif
31 
32   // CreateOrNull factory methods hand over ownership of aCairoSurface,
33   // so we don't call cairo_surface_reference(aSurface) here.
34 
35   // This code was copied from gfxASurface::Init:
36 #ifdef MOZ_TREE_CAIRO
37   if (mCairoSurface &&
38       cairo_surface_get_content(mCairoSurface) != CAIRO_CONTENT_COLOR) {
39     cairo_surface_set_subpixel_antialiasing(mCairoSurface,
40                                             CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
41   }
42 #endif
43 }
44 
45 PrintTarget::~PrintTarget()
46 {
47   // null surfaces are allowed here
48   cairo_surface_destroy(mCairoSurface);
49   mCairoSurface = nullptr;
50 }
51 
52 already_AddRefed<DrawTarget>
53 PrintTarget::MakeDrawTarget(const IntSize& aSize,
54                             DrawEventRecorder* aRecorder)
55 {
56   MOZ_ASSERT(mCairoSurface,
57              "We shouldn't have been constructed without a cairo surface");
58 
59   // This should not be called outside of BeginPage()/EndPage() calls since
60   // some backends can only provide a valid DrawTarget at that time.
61   MOZ_ASSERT(mHasActivePage, "We can't guarantee a valid DrawTarget");
62 
63   if (cairo_surface_status(mCairoSurface)) {
64     return nullptr;
65   }
66 
67   // Note than aSize may not be the same as mSize (the size of mCairoSurface).
68   // See the comments in our header.  If the sizes are different a clip will
69   // be applied to mCairoSurface.
70   RefPtr<DrawTarget> dt =
71     Factory::CreateDrawTargetForCairoSurface(mCairoSurface, aSize);
72   if (!dt || !dt->IsValid()) {
73     return nullptr;
74   }
75 
76   if (aRecorder) {
77     dt = CreateRecordingDrawTarget(aRecorder, dt);
78     if (!dt || !dt->IsValid()) {
79       return nullptr;
80     }
81   }
82 
83   return dt.forget();
84 }
85 
86 already_AddRefed<DrawTarget>
87 PrintTarget::GetReferenceDrawTarget(DrawEventRecorder* aRecorder)
88 {
89   if (!mRefDT) {
90     IntSize size(1, 1);
91 
92     cairo_surface_t* surface =
93       cairo_surface_create_similar(mCairoSurface,
94                                    cairo_surface_get_content(mCairoSurface),
95                                    size.width, size.height);
96 
97     if (cairo_surface_status(surface)) {
98       return nullptr;
99     }
100 
101     RefPtr<DrawTarget> dt =
102       Factory::CreateDrawTargetForCairoSurface(surface, size);
103 
104     // The DT addrefs the surface, so we need drop our own reference to it:
105     cairo_surface_destroy(surface);
106 
107     if (!dt || !dt->IsValid()) {
108       return nullptr;
109     }
110 
111     if (aRecorder) {
112       dt = CreateRecordingDrawTarget(aRecorder, dt);
113       if (!dt || !dt->IsValid()) {
114         return nullptr;
115       }
116     }
117 
118     mRefDT = dt.forget();
119   }
120   return do_AddRef(mRefDT);
121 }
122 
123 already_AddRefed<DrawTarget>
124 PrintTarget::CreateRecordingDrawTarget(DrawEventRecorder* aRecorder,
125                                        DrawTarget* aDrawTarget)
126 {
127   MOZ_ASSERT(aRecorder);
128   MOZ_ASSERT(aDrawTarget);
129 
130   RefPtr<DrawTarget> dt;
131 
132   if (aRecorder) {
133     // It doesn't really matter what we pass as the DrawTarget here.
134     dt = gfx::Factory::CreateRecordingDrawTarget(aRecorder, aDrawTarget);
135   }
136 
137   if (!dt || !dt->IsValid()) {
138     gfxCriticalNote
139       << "Failed to create a recording DrawTarget for PrintTarget";
140     return nullptr;
141   }
142 
143   return dt.forget();
144 }
145 
146 void
147 PrintTarget::Finish()
148 {
149   if (mIsFinished) {
150     return;
151   }
152   mIsFinished = true;
153 
154   // null surfaces are allowed here
155   cairo_surface_finish(mCairoSurface);
156 }
157 
158 } // namespace gfx
159 } // namespace mozilla
160