1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxDrawable.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,309 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "gfxDrawable.h" 1.10 +#include "gfxASurface.h" 1.11 +#include "gfxContext.h" 1.12 +#include "gfxImageSurface.h" 1.13 +#include "gfxPlatform.h" 1.14 +#include "gfxColor.h" 1.15 +#ifdef MOZ_X11 1.16 +#include "cairo.h" 1.17 +#include "gfxXlibSurface.h" 1.18 +#endif 1.19 + 1.20 +using namespace mozilla; 1.21 +using namespace mozilla::gfx; 1.22 + 1.23 +gfxSurfaceDrawable::gfxSurfaceDrawable(gfxASurface* aSurface, 1.24 + const gfxIntSize aSize, 1.25 + const gfxMatrix aTransform) 1.26 + : gfxDrawable(aSize) 1.27 + , mSurface(aSurface) 1.28 + , mTransform(aTransform) 1.29 +{ 1.30 +} 1.31 + 1.32 +gfxSurfaceDrawable::gfxSurfaceDrawable(DrawTarget* aDrawTarget, 1.33 + const gfxIntSize aSize, 1.34 + const gfxMatrix aTransform) 1.35 + : gfxDrawable(aSize) 1.36 + , mDrawTarget(aDrawTarget) 1.37 + , mTransform(aTransform) 1.38 +{ 1.39 +} 1.40 + 1.41 +gfxSurfaceDrawable::gfxSurfaceDrawable(SourceSurface* aSurface, 1.42 + const gfxIntSize aSize, 1.43 + const gfxMatrix aTransform) 1.44 + : gfxDrawable(aSize) 1.45 + , mSourceSurface(aSurface) 1.46 + , mTransform(aTransform) 1.47 +{ 1.48 +} 1.49 + 1.50 +static gfxMatrix 1.51 +DeviceToImageTransform(gfxContext* aContext, 1.52 + const gfxMatrix& aUserSpaceToImageSpace) 1.53 +{ 1.54 + gfxFloat deviceX, deviceY; 1.55 + nsRefPtr<gfxASurface> currentTarget = 1.56 + aContext->CurrentSurface(&deviceX, &deviceY); 1.57 + gfxMatrix currentMatrix = aContext->CurrentMatrix(); 1.58 + gfxMatrix deviceToUser = gfxMatrix(currentMatrix).Invert(); 1.59 + deviceToUser.Translate(-gfxPoint(-deviceX, -deviceY)); 1.60 + return gfxMatrix(deviceToUser).Multiply(aUserSpaceToImageSpace); 1.61 +} 1.62 + 1.63 +static void 1.64 +PreparePatternForUntiledDrawing(gfxPattern* aPattern, 1.65 + const gfxMatrix& aDeviceToImage, 1.66 + gfxASurface *currentTarget, 1.67 + const GraphicsFilter aDefaultFilter) 1.68 +{ 1.69 + if (!currentTarget) { 1.70 + // This happens if we're dealing with an Azure target. 1.71 + aPattern->SetExtend(gfxPattern::EXTEND_PAD); 1.72 + aPattern->SetFilter(aDefaultFilter); 1.73 + return; 1.74 + } 1.75 + 1.76 + // In theory we can handle this using cairo's EXTEND_PAD, 1.77 + // but implementation limitations mean we have to consult 1.78 + // the surface type. 1.79 + switch (currentTarget->GetType()) { 1.80 + 1.81 +#ifdef MOZ_X11 1.82 + case gfxSurfaceType::Xlib: 1.83 + { 1.84 + // See bugs 324698, 422179, and 468496. This is a workaround for 1.85 + // XRender's RepeatPad not being implemented correctly on old X 1.86 + // servers. 1.87 + // 1.88 + // In this situation, cairo avoids XRender and instead reads back 1.89 + // to perform EXTEND_PAD with pixman. This is too slow so we 1.90 + // avoid EXTEND_PAD and set the filter to CAIRO_FILTER_FAST --- 1.91 + // otherwise, pixman's sampling will sample transparency for the 1.92 + // outside edges and we'll get blurry edges. 1.93 + // 1.94 + // But don't do this for simple downscales because it's horrible. 1.95 + // Downscaling means that device-space coordinates are 1.96 + // scaled *up* to find the image pixel coordinates. 1.97 + // 1.98 + // Cairo, and hence Gecko, can use RepeatPad on Xorg 1.7. We 1.99 + // enable EXTEND_PAD provided that we're running on a recent 1.100 + // enough X server. 1.101 + if (static_cast<gfxXlibSurface*>(currentTarget)->IsPadSlow()) { 1.102 + bool isDownscale = 1.103 + aDeviceToImage.xx >= 1.0 && aDeviceToImage.yy >= 1.0 && 1.104 + aDeviceToImage.xy == 0.0 && aDeviceToImage.yx == 0.0; 1.105 + 1.106 + GraphicsFilter filter = 1.107 + isDownscale ? aDefaultFilter : (const GraphicsFilter)GraphicsFilter::FILTER_FAST; 1.108 + aPattern->SetFilter(filter); 1.109 + 1.110 + // Use the default EXTEND_NONE 1.111 + break; 1.112 + } 1.113 + // else fall through to EXTEND_PAD and the default filter. 1.114 + } 1.115 +#endif 1.116 + 1.117 + default: 1.118 + // turn on EXTEND_PAD. 1.119 + // This is what we really want for all surface types, if the 1.120 + // implementation was universally good. 1.121 + aPattern->SetExtend(gfxPattern::EXTEND_PAD); 1.122 + aPattern->SetFilter(aDefaultFilter); 1.123 + break; 1.124 + } 1.125 +} 1.126 + 1.127 +bool 1.128 +gfxSurfaceDrawable::Draw(gfxContext* aContext, 1.129 + const gfxRect& aFillRect, 1.130 + bool aRepeat, 1.131 + const GraphicsFilter& aFilter, 1.132 + const gfxMatrix& aTransform) 1.133 +{ 1.134 + nsRefPtr<gfxPattern> pattern; 1.135 + if (mDrawTarget) { 1.136 + if (aContext->IsCairo()) { 1.137 + nsRefPtr<gfxASurface> source = 1.138 + gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mDrawTarget); 1.139 + pattern = new gfxPattern(source); 1.140 + } else { 1.141 + RefPtr<SourceSurface> source = mDrawTarget->Snapshot(); 1.142 + pattern = new gfxPattern(source, Matrix()); 1.143 + } 1.144 + } else if (mSourceSurface) { 1.145 + pattern = new gfxPattern(mSourceSurface, Matrix()); 1.146 + } else { 1.147 + pattern = new gfxPattern(mSurface); 1.148 + } 1.149 + if (aRepeat) { 1.150 + pattern->SetExtend(gfxPattern::EXTEND_REPEAT); 1.151 + pattern->SetFilter(aFilter); 1.152 + } else { 1.153 + GraphicsFilter filter = aFilter; 1.154 + if (aContext->CurrentMatrix().HasOnlyIntegerTranslation() && 1.155 + aTransform.HasOnlyIntegerTranslation()) 1.156 + { 1.157 + // If we only have integer translation, no special filtering needs to 1.158 + // happen and we explicitly use FILTER_FAST. This is fast for some 1.159 + // backends. 1.160 + filter = GraphicsFilter::FILTER_FAST; 1.161 + } 1.162 + nsRefPtr<gfxASurface> currentTarget = aContext->CurrentSurface(); 1.163 + gfxMatrix deviceSpaceToImageSpace = 1.164 + DeviceToImageTransform(aContext, aTransform); 1.165 + PreparePatternForUntiledDrawing(pattern, deviceSpaceToImageSpace, 1.166 + currentTarget, filter); 1.167 + } 1.168 + pattern->SetMatrix(gfxMatrix(aTransform).Multiply(mTransform)); 1.169 + aContext->NewPath(); 1.170 + aContext->SetPattern(pattern); 1.171 + aContext->Rectangle(aFillRect); 1.172 + aContext->Fill(); 1.173 + // clear the pattern so that the snapshot is released before the 1.174 + // drawable is destroyed 1.175 + aContext->SetDeviceColor(gfxRGBA(0.0, 0.0, 0.0, 0.0)); 1.176 + return true; 1.177 +} 1.178 + 1.179 +already_AddRefed<gfxImageSurface> 1.180 +gfxSurfaceDrawable::GetAsImageSurface() 1.181 +{ 1.182 + if (mDrawTarget || mSourceSurface) { 1.183 + // TODO: Find a way to implement this. The caller really wants a 'sub-image' of 1.184 + // the original, without having to do a copy. GetDataSurface() might just copy, 1.185 + // which isn't useful. 1.186 + return nullptr; 1.187 + 1.188 + } 1.189 + return mSurface->GetAsImageSurface(); 1.190 +} 1.191 + 1.192 +gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback, 1.193 + const gfxIntSize aSize) 1.194 + : gfxDrawable(aSize) 1.195 + , mCallback(aCallback) 1.196 +{ 1.197 +} 1.198 + 1.199 +already_AddRefed<gfxSurfaceDrawable> 1.200 +gfxCallbackDrawable::MakeSurfaceDrawable(const GraphicsFilter aFilter) 1.201 +{ 1.202 + SurfaceFormat format = 1.203 + gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR_ALPHA); 1.204 + RefPtr<DrawTarget> dt = 1.205 + gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(mSize.ToIntSize(), 1.206 + format); 1.207 + if (!dt) 1.208 + return nullptr; 1.209 + 1.210 + nsRefPtr<gfxContext> ctx = new gfxContext(dt); 1.211 + Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), false, aFilter); 1.212 + 1.213 + RefPtr<SourceSurface> surface = dt->Snapshot(); 1.214 + nsRefPtr<gfxSurfaceDrawable> drawable = new gfxSurfaceDrawable(surface, mSize); 1.215 + return drawable.forget(); 1.216 +} 1.217 + 1.218 +bool 1.219 +gfxCallbackDrawable::Draw(gfxContext* aContext, 1.220 + const gfxRect& aFillRect, 1.221 + bool aRepeat, 1.222 + const GraphicsFilter& aFilter, 1.223 + const gfxMatrix& aTransform) 1.224 +{ 1.225 + if (aRepeat && !mSurfaceDrawable) { 1.226 + mSurfaceDrawable = MakeSurfaceDrawable(aFilter); 1.227 + } 1.228 + 1.229 + if (mSurfaceDrawable) 1.230 + return mSurfaceDrawable->Draw(aContext, aFillRect, aRepeat, aFilter, 1.231 + aTransform); 1.232 + 1.233 + if (mCallback) 1.234 + return (*mCallback)(aContext, aFillRect, aFilter, aTransform); 1.235 + 1.236 + return false; 1.237 +} 1.238 + 1.239 +gfxPatternDrawable::gfxPatternDrawable(gfxPattern* aPattern, 1.240 + const gfxIntSize aSize) 1.241 + : gfxDrawable(aSize) 1.242 + , mPattern(aPattern) 1.243 +{ 1.244 +} 1.245 + 1.246 +gfxPatternDrawable::~gfxPatternDrawable() 1.247 +{ 1.248 +} 1.249 + 1.250 +class DrawingCallbackFromDrawable : public gfxDrawingCallback { 1.251 +public: 1.252 + DrawingCallbackFromDrawable(gfxDrawable* aDrawable) 1.253 + : mDrawable(aDrawable) { 1.254 + NS_ASSERTION(aDrawable, "aDrawable is null!"); 1.255 + } 1.256 + 1.257 + virtual ~DrawingCallbackFromDrawable() {} 1.258 + 1.259 + virtual bool operator()(gfxContext* aContext, 1.260 + const gfxRect& aFillRect, 1.261 + const GraphicsFilter& aFilter, 1.262 + const gfxMatrix& aTransform = gfxMatrix()) 1.263 + { 1.264 + return mDrawable->Draw(aContext, aFillRect, false, aFilter, 1.265 + aTransform); 1.266 + } 1.267 +private: 1.268 + nsRefPtr<gfxDrawable> mDrawable; 1.269 +}; 1.270 + 1.271 +already_AddRefed<gfxCallbackDrawable> 1.272 +gfxPatternDrawable::MakeCallbackDrawable() 1.273 +{ 1.274 + nsRefPtr<gfxDrawingCallback> callback = 1.275 + new DrawingCallbackFromDrawable(this); 1.276 + nsRefPtr<gfxCallbackDrawable> callbackDrawable = 1.277 + new gfxCallbackDrawable(callback, mSize); 1.278 + return callbackDrawable.forget(); 1.279 +} 1.280 + 1.281 +bool 1.282 +gfxPatternDrawable::Draw(gfxContext* aContext, 1.283 + const gfxRect& aFillRect, 1.284 + bool aRepeat, 1.285 + const GraphicsFilter& aFilter, 1.286 + const gfxMatrix& aTransform) 1.287 +{ 1.288 + if (!mPattern) 1.289 + return false; 1.290 + 1.291 + if (aRepeat) { 1.292 + // We can't use mPattern directly: We want our repeated tiles to have 1.293 + // the size mSize, which might not be the case in mPattern. 1.294 + // So we need to draw mPattern into a surface of size mSize, create 1.295 + // a pattern from the surface and draw that pattern. 1.296 + // gfxCallbackDrawable and gfxSurfaceDrawable already know how to do 1.297 + // those things, so we use them here. Drawing mPattern into the surface 1.298 + // will happen through this Draw() method with aRepeat = false. 1.299 + nsRefPtr<gfxCallbackDrawable> callbackDrawable = MakeCallbackDrawable(); 1.300 + return callbackDrawable->Draw(aContext, aFillRect, true, aFilter, 1.301 + aTransform); 1.302 + } 1.303 + 1.304 + aContext->NewPath(); 1.305 + gfxMatrix oldMatrix = mPattern->GetMatrix(); 1.306 + mPattern->SetMatrix(gfxMatrix(aTransform).Multiply(oldMatrix)); 1.307 + aContext->SetPattern(mPattern); 1.308 + aContext->Rectangle(aFillRect); 1.309 + aContext->Fill(); 1.310 + mPattern->SetMatrix(oldMatrix); 1.311 + return true; 1.312 +}