1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is Markus Stange.
18 : * Portions created by the Initial Developer are Copyright (C) 2010
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 2 or later (the "GPL"), or
25 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "gfxDrawable.h"
38 : #include "gfxASurface.h"
39 : #include "gfxContext.h"
40 : #include "gfxPlatform.h"
41 : #include "mozilla/arm.h"
42 : #ifdef MOZ_X11
43 : #include "cairo.h"
44 : #include "gfxXlibSurface.h"
45 : #endif
46 :
47 0 : gfxSurfaceDrawable::gfxSurfaceDrawable(gfxASurface* aSurface,
48 : const gfxIntSize aSize,
49 : const gfxMatrix aTransform)
50 : : gfxDrawable(aSize)
51 : , mSurface(aSurface)
52 0 : , mTransform(aTransform)
53 : {
54 0 : }
55 :
56 : static gfxMatrix
57 0 : DeviceToImageTransform(gfxContext* aContext,
58 : const gfxMatrix& aUserSpaceToImageSpace)
59 : {
60 : gfxFloat deviceX, deviceY;
61 : nsRefPtr<gfxASurface> currentTarget =
62 0 : aContext->CurrentSurface(&deviceX, &deviceY);
63 0 : gfxMatrix currentMatrix = aContext->CurrentMatrix();
64 0 : gfxMatrix deviceToUser = gfxMatrix(currentMatrix).Invert();
65 0 : deviceToUser.Translate(-gfxPoint(-deviceX, -deviceY));
66 0 : return gfxMatrix(deviceToUser).Multiply(aUserSpaceToImageSpace);
67 : }
68 :
69 : static void
70 0 : PreparePatternForUntiledDrawing(gfxPattern* aPattern,
71 : const gfxMatrix& aDeviceToImage,
72 : gfxASurface *currentTarget,
73 : const gfxPattern::GraphicsFilter aDefaultFilter)
74 : {
75 0 : if (!currentTarget) {
76 : // This happens if we're dealing with an Azure target.
77 0 : aPattern->SetExtend(gfxPattern::EXTEND_PAD);
78 0 : aPattern->SetFilter(aDefaultFilter);
79 0 : return;
80 : }
81 :
82 : // In theory we can handle this using cairo's EXTEND_PAD,
83 : // but implementation limitations mean we have to consult
84 : // the surface type.
85 0 : switch (currentTarget->GetType()) {
86 :
87 : // The printing surfaces don't natively support or need
88 : // EXTEND_PAD for padding the edges. Using EXTEND_PAD this way
89 : // is suboptimal as it will result in the printing surface
90 : // creating a new image for each fill operation. The pattern
91 : // will be painted to the image to pad out the pattern, then
92 : // the new image will be used as the source. This increases
93 : // printing time and memory use, and prevents the use of mime
94 : // data from cairo_surface_set_mime_data(). Bug 691061.
95 : case gfxASurface::SurfaceTypePDF:
96 : case gfxASurface::SurfaceTypePS:
97 : case gfxASurface::SurfaceTypeWin32Printing:
98 0 : aPattern->SetExtend(gfxPattern::EXTEND_NONE);
99 0 : aPattern->SetFilter(aDefaultFilter);
100 0 : break;
101 :
102 : #ifdef MOZ_X11
103 : case gfxASurface::SurfaceTypeXlib:
104 : {
105 : // See bugs 324698, 422179, and 468496. This is a workaround for
106 : // XRender's RepeatPad not being implemented correctly on old X
107 : // servers.
108 : //
109 : // In this situation, cairo avoids XRender and instead reads back
110 : // to perform EXTEND_PAD with pixman. This is too slow so we
111 : // avoid EXTEND_PAD and set the filter to CAIRO_FILTER_FAST ---
112 : // otherwise, pixman's sampling will sample transparency for the
113 : // outside edges and we'll get blurry edges.
114 : //
115 : // But don't do this for simple downscales because it's horrible.
116 : // Downscaling means that device-space coordinates are
117 : // scaled *up* to find the image pixel coordinates.
118 : //
119 : // Cairo, and hence Gecko, can use RepeatPad on Xorg 1.7. We
120 : // enable EXTEND_PAD provided that we're running on a recent
121 : // enough X server.
122 :
123 : gfxXlibSurface *xlibSurface =
124 0 : static_cast<gfxXlibSurface *>(currentTarget);
125 0 : Display *dpy = xlibSurface->XDisplay();
126 : // This is the exact condition for cairo to avoid XRender for
127 : // EXTEND_PAD
128 0 : if (VendorRelease(dpy) >= 60700000 ||
129 : VendorRelease(dpy) < 10699000) {
130 :
131 : bool isDownscale =
132 : aDeviceToImage.xx >= 1.0 && aDeviceToImage.yy >= 1.0 &&
133 0 : aDeviceToImage.xy == 0.0 && aDeviceToImage.yx == 0.0;
134 :
135 : gfxPattern::GraphicsFilter filter =
136 0 : isDownscale ? aDefaultFilter : gfxPattern::FILTER_FAST;
137 0 : aPattern->SetFilter(filter);
138 :
139 : // Use the default EXTEND_NONE
140 0 : break;
141 : }
142 : // else fall through to EXTEND_PAD and the default filter.
143 : }
144 : #endif
145 :
146 : default:
147 : // turn on EXTEND_PAD.
148 : // This is what we really want for all surface types, if the
149 : // implementation was universally good.
150 0 : aPattern->SetExtend(gfxPattern::EXTEND_PAD);
151 0 : aPattern->SetFilter(aDefaultFilter);
152 0 : break;
153 : }
154 : }
155 :
156 : bool
157 0 : gfxSurfaceDrawable::Draw(gfxContext* aContext,
158 : const gfxRect& aFillRect,
159 : bool aRepeat,
160 : const gfxPattern::GraphicsFilter& aFilter,
161 : const gfxMatrix& aTransform)
162 : {
163 0 : nsRefPtr<gfxPattern> pattern = new gfxPattern(mSurface);
164 0 : if (aRepeat) {
165 0 : pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
166 0 : pattern->SetFilter(aFilter);
167 : } else {
168 0 : gfxPattern::GraphicsFilter filter = aFilter;
169 0 : if (aContext->CurrentMatrix().HasOnlyIntegerTranslation() &&
170 0 : aTransform.HasOnlyIntegerTranslation())
171 : {
172 : // If we only have integer translation, no special filtering needs to
173 : // happen and we explicitly use FILTER_FAST. This is fast for some
174 : // backends.
175 0 : filter = gfxPattern::FILTER_FAST;
176 : }
177 0 : nsRefPtr<gfxASurface> currentTarget = aContext->CurrentSurface();
178 : gfxMatrix deviceSpaceToImageSpace =
179 0 : DeviceToImageTransform(aContext, aTransform);
180 : PreparePatternForUntiledDrawing(pattern, deviceSpaceToImageSpace,
181 0 : currentTarget, filter);
182 : }
183 0 : pattern->SetMatrix(gfxMatrix(aTransform).Multiply(mTransform));
184 0 : aContext->NewPath();
185 0 : aContext->SetPattern(pattern);
186 0 : aContext->Rectangle(aFillRect);
187 0 : aContext->Fill();
188 0 : return true;
189 : }
190 :
191 0 : gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback,
192 : const gfxIntSize aSize)
193 : : gfxDrawable(aSize)
194 0 : , mCallback(aCallback)
195 : {
196 0 : }
197 :
198 : already_AddRefed<gfxSurfaceDrawable>
199 0 : gfxCallbackDrawable::MakeSurfaceDrawable(const gfxPattern::GraphicsFilter aFilter)
200 : {
201 : nsRefPtr<gfxASurface> surface =
202 0 : gfxPlatform::GetPlatform()->CreateOffscreenSurface(mSize, gfxASurface::CONTENT_COLOR_ALPHA);
203 0 : if (!surface || surface->CairoStatus() != 0)
204 0 : return nsnull;
205 :
206 0 : nsRefPtr<gfxContext> ctx = new gfxContext(surface);
207 0 : Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), false, aFilter);
208 0 : nsRefPtr<gfxSurfaceDrawable> drawable = new gfxSurfaceDrawable(surface, mSize);
209 0 : return drawable.forget();
210 : }
211 :
212 : bool
213 0 : gfxCallbackDrawable::Draw(gfxContext* aContext,
214 : const gfxRect& aFillRect,
215 : bool aRepeat,
216 : const gfxPattern::GraphicsFilter& aFilter,
217 : const gfxMatrix& aTransform)
218 : {
219 0 : if (aRepeat && !mSurfaceDrawable) {
220 0 : mSurfaceDrawable = MakeSurfaceDrawable(aFilter);
221 : }
222 :
223 0 : if (mSurfaceDrawable)
224 0 : return mSurfaceDrawable->Draw(aContext, aFillRect, aRepeat, aFilter,
225 0 : aTransform);
226 :
227 0 : if (mCallback)
228 0 : return (*mCallback)(aContext, aFillRect, aFilter, aTransform);
229 :
230 0 : return false;
231 : }
232 :
233 0 : gfxPatternDrawable::gfxPatternDrawable(gfxPattern* aPattern,
234 : const gfxIntSize aSize)
235 : : gfxDrawable(aSize)
236 0 : , mPattern(aPattern)
237 : {
238 0 : }
239 :
240 : class DrawingCallbackFromDrawable : public gfxDrawingCallback {
241 : public:
242 0 : DrawingCallbackFromDrawable(gfxDrawable* aDrawable)
243 0 : : mDrawable(aDrawable) {
244 0 : NS_ASSERTION(aDrawable, "aDrawable is null!");
245 0 : }
246 :
247 0 : virtual ~DrawingCallbackFromDrawable() {}
248 :
249 0 : virtual bool operator()(gfxContext* aContext,
250 : const gfxRect& aFillRect,
251 : const gfxPattern::GraphicsFilter& aFilter,
252 : const gfxMatrix& aTransform = gfxMatrix())
253 : {
254 0 : return mDrawable->Draw(aContext, aFillRect, false, aFilter,
255 0 : aTransform);
256 : }
257 : private:
258 : nsRefPtr<gfxDrawable> mDrawable;
259 : };
260 :
261 : already_AddRefed<gfxCallbackDrawable>
262 0 : gfxPatternDrawable::MakeCallbackDrawable()
263 : {
264 : nsRefPtr<gfxDrawingCallback> callback =
265 0 : new DrawingCallbackFromDrawable(this);
266 : nsRefPtr<gfxCallbackDrawable> callbackDrawable =
267 0 : new gfxCallbackDrawable(callback, mSize);
268 0 : return callbackDrawable.forget();
269 : }
270 :
271 : bool
272 0 : gfxPatternDrawable::Draw(gfxContext* aContext,
273 : const gfxRect& aFillRect,
274 : bool aRepeat,
275 : const gfxPattern::GraphicsFilter& aFilter,
276 : const gfxMatrix& aTransform)
277 : {
278 0 : if (!mPattern)
279 0 : return false;
280 :
281 0 : if (aRepeat) {
282 : // We can't use mPattern directly: We want our repeated tiles to have
283 : // the size mSize, which might not be the case in mPattern.
284 : // So we need to draw mPattern into a surface of size mSize, create
285 : // a pattern from the surface and draw that pattern.
286 : // gfxCallbackDrawable and gfxSurfaceDrawable already know how to do
287 : // those things, so we use them here. Drawing mPattern into the surface
288 : // will happen through this Draw() method with aRepeat = false.
289 0 : nsRefPtr<gfxCallbackDrawable> callbackDrawable = MakeCallbackDrawable();
290 0 : return callbackDrawable->Draw(aContext, aFillRect, true, aFilter,
291 0 : aTransform);
292 : }
293 :
294 0 : aContext->NewPath();
295 0 : gfxMatrix oldMatrix = mPattern->GetMatrix();
296 0 : mPattern->SetMatrix(gfxMatrix(aTransform).Multiply(oldMatrix));
297 0 : aContext->SetPattern(mPattern);
298 0 : aContext->Rectangle(aFillRect);
299 0 : aContext->Fill();
300 0 : mPattern->SetMatrix(oldMatrix);
301 0 : return true;
302 : }
|