gfx/thebes/gfxDrawable.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "gfxDrawable.h"
michael@0 7 #include "gfxASurface.h"
michael@0 8 #include "gfxContext.h"
michael@0 9 #include "gfxImageSurface.h"
michael@0 10 #include "gfxPlatform.h"
michael@0 11 #include "gfxColor.h"
michael@0 12 #ifdef MOZ_X11
michael@0 13 #include "cairo.h"
michael@0 14 #include "gfxXlibSurface.h"
michael@0 15 #endif
michael@0 16
michael@0 17 using namespace mozilla;
michael@0 18 using namespace mozilla::gfx;
michael@0 19
michael@0 20 gfxSurfaceDrawable::gfxSurfaceDrawable(gfxASurface* aSurface,
michael@0 21 const gfxIntSize aSize,
michael@0 22 const gfxMatrix aTransform)
michael@0 23 : gfxDrawable(aSize)
michael@0 24 , mSurface(aSurface)
michael@0 25 , mTransform(aTransform)
michael@0 26 {
michael@0 27 }
michael@0 28
michael@0 29 gfxSurfaceDrawable::gfxSurfaceDrawable(DrawTarget* aDrawTarget,
michael@0 30 const gfxIntSize aSize,
michael@0 31 const gfxMatrix aTransform)
michael@0 32 : gfxDrawable(aSize)
michael@0 33 , mDrawTarget(aDrawTarget)
michael@0 34 , mTransform(aTransform)
michael@0 35 {
michael@0 36 }
michael@0 37
michael@0 38 gfxSurfaceDrawable::gfxSurfaceDrawable(SourceSurface* aSurface,
michael@0 39 const gfxIntSize aSize,
michael@0 40 const gfxMatrix aTransform)
michael@0 41 : gfxDrawable(aSize)
michael@0 42 , mSourceSurface(aSurface)
michael@0 43 , mTransform(aTransform)
michael@0 44 {
michael@0 45 }
michael@0 46
michael@0 47 static gfxMatrix
michael@0 48 DeviceToImageTransform(gfxContext* aContext,
michael@0 49 const gfxMatrix& aUserSpaceToImageSpace)
michael@0 50 {
michael@0 51 gfxFloat deviceX, deviceY;
michael@0 52 nsRefPtr<gfxASurface> currentTarget =
michael@0 53 aContext->CurrentSurface(&deviceX, &deviceY);
michael@0 54 gfxMatrix currentMatrix = aContext->CurrentMatrix();
michael@0 55 gfxMatrix deviceToUser = gfxMatrix(currentMatrix).Invert();
michael@0 56 deviceToUser.Translate(-gfxPoint(-deviceX, -deviceY));
michael@0 57 return gfxMatrix(deviceToUser).Multiply(aUserSpaceToImageSpace);
michael@0 58 }
michael@0 59
michael@0 60 static void
michael@0 61 PreparePatternForUntiledDrawing(gfxPattern* aPattern,
michael@0 62 const gfxMatrix& aDeviceToImage,
michael@0 63 gfxASurface *currentTarget,
michael@0 64 const GraphicsFilter aDefaultFilter)
michael@0 65 {
michael@0 66 if (!currentTarget) {
michael@0 67 // This happens if we're dealing with an Azure target.
michael@0 68 aPattern->SetExtend(gfxPattern::EXTEND_PAD);
michael@0 69 aPattern->SetFilter(aDefaultFilter);
michael@0 70 return;
michael@0 71 }
michael@0 72
michael@0 73 // In theory we can handle this using cairo's EXTEND_PAD,
michael@0 74 // but implementation limitations mean we have to consult
michael@0 75 // the surface type.
michael@0 76 switch (currentTarget->GetType()) {
michael@0 77
michael@0 78 #ifdef MOZ_X11
michael@0 79 case gfxSurfaceType::Xlib:
michael@0 80 {
michael@0 81 // See bugs 324698, 422179, and 468496. This is a workaround for
michael@0 82 // XRender's RepeatPad not being implemented correctly on old X
michael@0 83 // servers.
michael@0 84 //
michael@0 85 // In this situation, cairo avoids XRender and instead reads back
michael@0 86 // to perform EXTEND_PAD with pixman. This is too slow so we
michael@0 87 // avoid EXTEND_PAD and set the filter to CAIRO_FILTER_FAST ---
michael@0 88 // otherwise, pixman's sampling will sample transparency for the
michael@0 89 // outside edges and we'll get blurry edges.
michael@0 90 //
michael@0 91 // But don't do this for simple downscales because it's horrible.
michael@0 92 // Downscaling means that device-space coordinates are
michael@0 93 // scaled *up* to find the image pixel coordinates.
michael@0 94 //
michael@0 95 // Cairo, and hence Gecko, can use RepeatPad on Xorg 1.7. We
michael@0 96 // enable EXTEND_PAD provided that we're running on a recent
michael@0 97 // enough X server.
michael@0 98 if (static_cast<gfxXlibSurface*>(currentTarget)->IsPadSlow()) {
michael@0 99 bool isDownscale =
michael@0 100 aDeviceToImage.xx >= 1.0 && aDeviceToImage.yy >= 1.0 &&
michael@0 101 aDeviceToImage.xy == 0.0 && aDeviceToImage.yx == 0.0;
michael@0 102
michael@0 103 GraphicsFilter filter =
michael@0 104 isDownscale ? aDefaultFilter : (const GraphicsFilter)GraphicsFilter::FILTER_FAST;
michael@0 105 aPattern->SetFilter(filter);
michael@0 106
michael@0 107 // Use the default EXTEND_NONE
michael@0 108 break;
michael@0 109 }
michael@0 110 // else fall through to EXTEND_PAD and the default filter.
michael@0 111 }
michael@0 112 #endif
michael@0 113
michael@0 114 default:
michael@0 115 // turn on EXTEND_PAD.
michael@0 116 // This is what we really want for all surface types, if the
michael@0 117 // implementation was universally good.
michael@0 118 aPattern->SetExtend(gfxPattern::EXTEND_PAD);
michael@0 119 aPattern->SetFilter(aDefaultFilter);
michael@0 120 break;
michael@0 121 }
michael@0 122 }
michael@0 123
michael@0 124 bool
michael@0 125 gfxSurfaceDrawable::Draw(gfxContext* aContext,
michael@0 126 const gfxRect& aFillRect,
michael@0 127 bool aRepeat,
michael@0 128 const GraphicsFilter& aFilter,
michael@0 129 const gfxMatrix& aTransform)
michael@0 130 {
michael@0 131 nsRefPtr<gfxPattern> pattern;
michael@0 132 if (mDrawTarget) {
michael@0 133 if (aContext->IsCairo()) {
michael@0 134 nsRefPtr<gfxASurface> source =
michael@0 135 gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mDrawTarget);
michael@0 136 pattern = new gfxPattern(source);
michael@0 137 } else {
michael@0 138 RefPtr<SourceSurface> source = mDrawTarget->Snapshot();
michael@0 139 pattern = new gfxPattern(source, Matrix());
michael@0 140 }
michael@0 141 } else if (mSourceSurface) {
michael@0 142 pattern = new gfxPattern(mSourceSurface, Matrix());
michael@0 143 } else {
michael@0 144 pattern = new gfxPattern(mSurface);
michael@0 145 }
michael@0 146 if (aRepeat) {
michael@0 147 pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
michael@0 148 pattern->SetFilter(aFilter);
michael@0 149 } else {
michael@0 150 GraphicsFilter filter = aFilter;
michael@0 151 if (aContext->CurrentMatrix().HasOnlyIntegerTranslation() &&
michael@0 152 aTransform.HasOnlyIntegerTranslation())
michael@0 153 {
michael@0 154 // If we only have integer translation, no special filtering needs to
michael@0 155 // happen and we explicitly use FILTER_FAST. This is fast for some
michael@0 156 // backends.
michael@0 157 filter = GraphicsFilter::FILTER_FAST;
michael@0 158 }
michael@0 159 nsRefPtr<gfxASurface> currentTarget = aContext->CurrentSurface();
michael@0 160 gfxMatrix deviceSpaceToImageSpace =
michael@0 161 DeviceToImageTransform(aContext, aTransform);
michael@0 162 PreparePatternForUntiledDrawing(pattern, deviceSpaceToImageSpace,
michael@0 163 currentTarget, filter);
michael@0 164 }
michael@0 165 pattern->SetMatrix(gfxMatrix(aTransform).Multiply(mTransform));
michael@0 166 aContext->NewPath();
michael@0 167 aContext->SetPattern(pattern);
michael@0 168 aContext->Rectangle(aFillRect);
michael@0 169 aContext->Fill();
michael@0 170 // clear the pattern so that the snapshot is released before the
michael@0 171 // drawable is destroyed
michael@0 172 aContext->SetDeviceColor(gfxRGBA(0.0, 0.0, 0.0, 0.0));
michael@0 173 return true;
michael@0 174 }
michael@0 175
michael@0 176 already_AddRefed<gfxImageSurface>
michael@0 177 gfxSurfaceDrawable::GetAsImageSurface()
michael@0 178 {
michael@0 179 if (mDrawTarget || mSourceSurface) {
michael@0 180 // TODO: Find a way to implement this. The caller really wants a 'sub-image' of
michael@0 181 // the original, without having to do a copy. GetDataSurface() might just copy,
michael@0 182 // which isn't useful.
michael@0 183 return nullptr;
michael@0 184
michael@0 185 }
michael@0 186 return mSurface->GetAsImageSurface();
michael@0 187 }
michael@0 188
michael@0 189 gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback,
michael@0 190 const gfxIntSize aSize)
michael@0 191 : gfxDrawable(aSize)
michael@0 192 , mCallback(aCallback)
michael@0 193 {
michael@0 194 }
michael@0 195
michael@0 196 already_AddRefed<gfxSurfaceDrawable>
michael@0 197 gfxCallbackDrawable::MakeSurfaceDrawable(const GraphicsFilter aFilter)
michael@0 198 {
michael@0 199 SurfaceFormat format =
michael@0 200 gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR_ALPHA);
michael@0 201 RefPtr<DrawTarget> dt =
michael@0 202 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(mSize.ToIntSize(),
michael@0 203 format);
michael@0 204 if (!dt)
michael@0 205 return nullptr;
michael@0 206
michael@0 207 nsRefPtr<gfxContext> ctx = new gfxContext(dt);
michael@0 208 Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), false, aFilter);
michael@0 209
michael@0 210 RefPtr<SourceSurface> surface = dt->Snapshot();
michael@0 211 nsRefPtr<gfxSurfaceDrawable> drawable = new gfxSurfaceDrawable(surface, mSize);
michael@0 212 return drawable.forget();
michael@0 213 }
michael@0 214
michael@0 215 bool
michael@0 216 gfxCallbackDrawable::Draw(gfxContext* aContext,
michael@0 217 const gfxRect& aFillRect,
michael@0 218 bool aRepeat,
michael@0 219 const GraphicsFilter& aFilter,
michael@0 220 const gfxMatrix& aTransform)
michael@0 221 {
michael@0 222 if (aRepeat && !mSurfaceDrawable) {
michael@0 223 mSurfaceDrawable = MakeSurfaceDrawable(aFilter);
michael@0 224 }
michael@0 225
michael@0 226 if (mSurfaceDrawable)
michael@0 227 return mSurfaceDrawable->Draw(aContext, aFillRect, aRepeat, aFilter,
michael@0 228 aTransform);
michael@0 229
michael@0 230 if (mCallback)
michael@0 231 return (*mCallback)(aContext, aFillRect, aFilter, aTransform);
michael@0 232
michael@0 233 return false;
michael@0 234 }
michael@0 235
michael@0 236 gfxPatternDrawable::gfxPatternDrawable(gfxPattern* aPattern,
michael@0 237 const gfxIntSize aSize)
michael@0 238 : gfxDrawable(aSize)
michael@0 239 , mPattern(aPattern)
michael@0 240 {
michael@0 241 }
michael@0 242
michael@0 243 gfxPatternDrawable::~gfxPatternDrawable()
michael@0 244 {
michael@0 245 }
michael@0 246
michael@0 247 class DrawingCallbackFromDrawable : public gfxDrawingCallback {
michael@0 248 public:
michael@0 249 DrawingCallbackFromDrawable(gfxDrawable* aDrawable)
michael@0 250 : mDrawable(aDrawable) {
michael@0 251 NS_ASSERTION(aDrawable, "aDrawable is null!");
michael@0 252 }
michael@0 253
michael@0 254 virtual ~DrawingCallbackFromDrawable() {}
michael@0 255
michael@0 256 virtual bool operator()(gfxContext* aContext,
michael@0 257 const gfxRect& aFillRect,
michael@0 258 const GraphicsFilter& aFilter,
michael@0 259 const gfxMatrix& aTransform = gfxMatrix())
michael@0 260 {
michael@0 261 return mDrawable->Draw(aContext, aFillRect, false, aFilter,
michael@0 262 aTransform);
michael@0 263 }
michael@0 264 private:
michael@0 265 nsRefPtr<gfxDrawable> mDrawable;
michael@0 266 };
michael@0 267
michael@0 268 already_AddRefed<gfxCallbackDrawable>
michael@0 269 gfxPatternDrawable::MakeCallbackDrawable()
michael@0 270 {
michael@0 271 nsRefPtr<gfxDrawingCallback> callback =
michael@0 272 new DrawingCallbackFromDrawable(this);
michael@0 273 nsRefPtr<gfxCallbackDrawable> callbackDrawable =
michael@0 274 new gfxCallbackDrawable(callback, mSize);
michael@0 275 return callbackDrawable.forget();
michael@0 276 }
michael@0 277
michael@0 278 bool
michael@0 279 gfxPatternDrawable::Draw(gfxContext* aContext,
michael@0 280 const gfxRect& aFillRect,
michael@0 281 bool aRepeat,
michael@0 282 const GraphicsFilter& aFilter,
michael@0 283 const gfxMatrix& aTransform)
michael@0 284 {
michael@0 285 if (!mPattern)
michael@0 286 return false;
michael@0 287
michael@0 288 if (aRepeat) {
michael@0 289 // We can't use mPattern directly: We want our repeated tiles to have
michael@0 290 // the size mSize, which might not be the case in mPattern.
michael@0 291 // So we need to draw mPattern into a surface of size mSize, create
michael@0 292 // a pattern from the surface and draw that pattern.
michael@0 293 // gfxCallbackDrawable and gfxSurfaceDrawable already know how to do
michael@0 294 // those things, so we use them here. Drawing mPattern into the surface
michael@0 295 // will happen through this Draw() method with aRepeat = false.
michael@0 296 nsRefPtr<gfxCallbackDrawable> callbackDrawable = MakeCallbackDrawable();
michael@0 297 return callbackDrawable->Draw(aContext, aFillRect, true, aFilter,
michael@0 298 aTransform);
michael@0 299 }
michael@0 300
michael@0 301 aContext->NewPath();
michael@0 302 gfxMatrix oldMatrix = mPattern->GetMatrix();
michael@0 303 mPattern->SetMatrix(gfxMatrix(aTransform).Multiply(oldMatrix));
michael@0 304 aContext->SetPattern(mPattern);
michael@0 305 aContext->Rectangle(aFillRect);
michael@0 306 aContext->Fill();
michael@0 307 mPattern->SetMatrix(oldMatrix);
michael@0 308 return true;
michael@0 309 }

mercurial