1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxWindowsNativeDrawing.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,324 @@ 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 <windows.h> 1.10 + 1.11 +#include "nsMathUtils.h" 1.12 + 1.13 +#include "gfxWindowsNativeDrawing.h" 1.14 +#include "gfxWindowsSurface.h" 1.15 +#include "gfxAlphaRecovery.h" 1.16 +#include "gfxPattern.h" 1.17 +#include "mozilla/gfx/2D.h" 1.18 + 1.19 +enum { 1.20 + RENDER_STATE_INIT, 1.21 + 1.22 + RENDER_STATE_NATIVE_DRAWING, 1.23 + RENDER_STATE_NATIVE_DRAWING_DONE, 1.24 + 1.25 + RENDER_STATE_ALPHA_RECOVERY_BLACK, 1.26 + RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE, 1.27 + RENDER_STATE_ALPHA_RECOVERY_WHITE, 1.28 + RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE, 1.29 + 1.30 + RENDER_STATE_DONE 1.31 +}; 1.32 + 1.33 +gfxWindowsNativeDrawing::gfxWindowsNativeDrawing(gfxContext* ctx, 1.34 + const gfxRect& nativeRect, 1.35 + uint32_t nativeDrawFlags) 1.36 + : mContext(ctx), mNativeRect(nativeRect), mNativeDrawFlags(nativeDrawFlags), mRenderState(RENDER_STATE_INIT) 1.37 +{ 1.38 +} 1.39 + 1.40 +HDC 1.41 +gfxWindowsNativeDrawing::BeginNativeDrawing() 1.42 +{ 1.43 + if (mRenderState == RENDER_STATE_INIT) { 1.44 + nsRefPtr<gfxASurface> surf; 1.45 + 1.46 + if (mContext->GetCairo()) { 1.47 + surf = mContext->CurrentSurface(&mDeviceOffset.x, &mDeviceOffset.y); 1.48 + } 1.49 + 1.50 + if (surf && surf->CairoStatus()) 1.51 + return nullptr; 1.52 + 1.53 + gfxMatrix m = mContext->CurrentMatrix(); 1.54 + if (!m.HasNonTranslation()) 1.55 + mTransformType = TRANSLATION_ONLY; 1.56 + else if (m.HasNonAxisAlignedTransform()) 1.57 + mTransformType = COMPLEX; 1.58 + else 1.59 + mTransformType = AXIS_ALIGNED_SCALE; 1.60 + 1.61 + // if this is a native win32 surface, we don't have to 1.62 + // redirect rendering to our own HDC; in some cases, 1.63 + // we may be able to use the HDC from the surface directly. 1.64 + if (surf && 1.65 + ((surf->GetType() == gfxSurfaceType::Win32 || 1.66 + surf->GetType() == gfxSurfaceType::Win32Printing) && 1.67 + (surf->GetContentType() == gfxContentType::COLOR || 1.68 + (surf->GetContentType() == gfxContentType::COLOR_ALPHA && 1.69 + (mNativeDrawFlags & CAN_DRAW_TO_COLOR_ALPHA))))) 1.70 + { 1.71 + // grab the DC. This can fail if there is a complex clipping path, 1.72 + // in which case we'll have to fall back. 1.73 + mWinSurface = static_cast<gfxWindowsSurface*>(static_cast<gfxASurface*>(surf.get())); 1.74 + mDC = mWinSurface->GetDCWithClip(mContext); 1.75 + 1.76 + if (mDC) { 1.77 + if (mTransformType == TRANSLATION_ONLY) { 1.78 + mRenderState = RENDER_STATE_NATIVE_DRAWING; 1.79 + 1.80 + mTranslation = m.GetTranslation(); 1.81 + } else if (((mTransformType == AXIS_ALIGNED_SCALE) 1.82 + && (mNativeDrawFlags & CAN_AXIS_ALIGNED_SCALE)) || 1.83 + (mNativeDrawFlags & CAN_COMPLEX_TRANSFORM)) 1.84 + { 1.85 + mWorldTransform.eM11 = (FLOAT) m.xx; 1.86 + mWorldTransform.eM12 = (FLOAT) m.yx; 1.87 + mWorldTransform.eM21 = (FLOAT) m.xy; 1.88 + mWorldTransform.eM22 = (FLOAT) m.yy; 1.89 + mWorldTransform.eDx = (FLOAT) m.x0; 1.90 + mWorldTransform.eDy = (FLOAT) m.y0; 1.91 + 1.92 + mRenderState = RENDER_STATE_NATIVE_DRAWING; 1.93 + } 1.94 + } 1.95 + } 1.96 + 1.97 + // If we couldn't do native drawing, then we have to do two-buffer drawing 1.98 + // and do alpha recovery 1.99 + if (mRenderState == RENDER_STATE_INIT) { 1.100 + mRenderState = RENDER_STATE_ALPHA_RECOVERY_BLACK; 1.101 + 1.102 + // We round out our native rect here, that way the snapping will 1.103 + // happen correctly. 1.104 + mNativeRect.RoundOut(); 1.105 + 1.106 + // we only do the scale bit if we can do an axis aligned 1.107 + // scale; otherwise we scale (if necessary) after 1.108 + // rendering with cairo. Note that if we're doing alpha recovery, 1.109 + // we cannot do a full complex transform with win32 (I mean, we could, but 1.110 + // it would require more code that's not here.) 1.111 + if (mTransformType == TRANSLATION_ONLY || !(mNativeDrawFlags & CAN_AXIS_ALIGNED_SCALE)) { 1.112 + mScale = gfxSize(1.0, 1.0); 1.113 + 1.114 + // Add 1 to the surface size; it's guaranteed to not be incorrect, 1.115 + // and it fixes bug 382458 1.116 + // There's probably a better fix, but I haven't figured out 1.117 + // the root cause of the problem. 1.118 + mTempSurfaceSize = 1.119 + gfxIntSize((int32_t) ceil(mNativeRect.Width() + 1), 1.120 + (int32_t) ceil(mNativeRect.Height() + 1)); 1.121 + } else { 1.122 + // figure out the scale factors 1.123 + mScale = m.ScaleFactors(true); 1.124 + 1.125 + mWorldTransform.eM11 = (FLOAT) mScale.width; 1.126 + mWorldTransform.eM12 = 0.0f; 1.127 + mWorldTransform.eM21 = 0.0f; 1.128 + mWorldTransform.eM22 = (FLOAT) mScale.height; 1.129 + mWorldTransform.eDx = 0.0f; 1.130 + mWorldTransform.eDy = 0.0f; 1.131 + 1.132 + // See comment above about "+1" 1.133 + mTempSurfaceSize = 1.134 + gfxIntSize((int32_t) ceil(mNativeRect.Width() * mScale.width + 1), 1.135 + (int32_t) ceil(mNativeRect.Height() * mScale.height + 1)); 1.136 + } 1.137 + } 1.138 + } 1.139 + 1.140 + if (mRenderState == RENDER_STATE_NATIVE_DRAWING) { 1.141 + // we can just do native drawing directly to the context's surface 1.142 + 1.143 + // do we need to use SetWorldTransform? 1.144 + if (mTransformType != TRANSLATION_ONLY) { 1.145 + SetGraphicsMode(mDC, GM_ADVANCED); 1.146 + GetWorldTransform(mDC, &mOldWorldTransform); 1.147 + SetWorldTransform(mDC, &mWorldTransform); 1.148 + } 1.149 + GetViewportOrgEx(mDC, &mOrigViewportOrigin); 1.150 + SetViewportOrgEx(mDC, 1.151 + mOrigViewportOrigin.x + (int)mDeviceOffset.x, 1.152 + mOrigViewportOrigin.y + (int)mDeviceOffset.y, 1.153 + nullptr); 1.154 + 1.155 + return mDC; 1.156 + } else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_BLACK || 1.157 + mRenderState == RENDER_STATE_ALPHA_RECOVERY_WHITE) 1.158 + { 1.159 + // we're going to use mWinSurface to create our temporary surface here 1.160 + 1.161 + // get us a RGB24 DIB; DIB is important, because 1.162 + // we can later call GetImageSurface on it. 1.163 + mWinSurface = new gfxWindowsSurface(mTempSurfaceSize); 1.164 + mDC = mWinSurface->GetDC(); 1.165 + 1.166 + RECT r = { 0, 0, mTempSurfaceSize.width, mTempSurfaceSize.height }; 1.167 + if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_BLACK) 1.168 + FillRect(mDC, &r, (HBRUSH)GetStockObject(BLACK_BRUSH)); 1.169 + else 1.170 + FillRect(mDC, &r, (HBRUSH)GetStockObject(WHITE_BRUSH)); 1.171 + 1.172 + if ((mTransformType != TRANSLATION_ONLY) && 1.173 + (mNativeDrawFlags & CAN_AXIS_ALIGNED_SCALE)) 1.174 + { 1.175 + SetGraphicsMode(mDC, GM_ADVANCED); 1.176 + SetWorldTransform(mDC, &mWorldTransform); 1.177 + } 1.178 + 1.179 + return mDC; 1.180 + } else { 1.181 + NS_ERROR("Bogus render state!"); 1.182 + return nullptr; 1.183 + } 1.184 +} 1.185 + 1.186 +bool 1.187 +gfxWindowsNativeDrawing::IsDoublePass() 1.188 +{ 1.189 + if (!mContext->IsCairo() && 1.190 + (mContext->GetDrawTarget()->GetType() != mozilla::gfx::BackendType::CAIRO || 1.191 + mContext->GetDrawTarget()->IsDualDrawTarget())) { 1.192 + return true; 1.193 + } 1.194 + 1.195 + nsRefPtr<gfxASurface> surf = mContext->CurrentSurface(&mDeviceOffset.x, &mDeviceOffset.y); 1.196 + if (!surf || surf->CairoStatus()) 1.197 + return false; 1.198 + if (surf->GetType() != gfxSurfaceType::Win32 && 1.199 + surf->GetType() != gfxSurfaceType::Win32Printing) { 1.200 + return true; 1.201 + } 1.202 + if ((surf->GetContentType() != gfxContentType::COLOR || 1.203 + (surf->GetContentType() == gfxContentType::COLOR_ALPHA && 1.204 + !(mNativeDrawFlags & CAN_DRAW_TO_COLOR_ALPHA)))) 1.205 + return true; 1.206 + return false; 1.207 +} 1.208 + 1.209 +bool 1.210 +gfxWindowsNativeDrawing::ShouldRenderAgain() 1.211 +{ 1.212 + switch (mRenderState) { 1.213 + case RENDER_STATE_NATIVE_DRAWING_DONE: 1.214 + return false; 1.215 + 1.216 + case RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE: 1.217 + mRenderState = RENDER_STATE_ALPHA_RECOVERY_WHITE; 1.218 + return true; 1.219 + 1.220 + case RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE: 1.221 + return false; 1.222 + 1.223 + default: 1.224 + NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::ShouldRenderAgain"); 1.225 + break; 1.226 + } 1.227 + 1.228 + return false; 1.229 +} 1.230 + 1.231 +void 1.232 +gfxWindowsNativeDrawing::EndNativeDrawing() 1.233 +{ 1.234 + if (mRenderState == RENDER_STATE_NATIVE_DRAWING) { 1.235 + // we drew directly to the HDC in the context; undo our changes 1.236 + SetViewportOrgEx(mDC, mOrigViewportOrigin.x, mOrigViewportOrigin.y, nullptr); 1.237 + 1.238 + if (mTransformType != TRANSLATION_ONLY) 1.239 + SetWorldTransform(mDC, &mOldWorldTransform); 1.240 + 1.241 + mWinSurface->MarkDirty(); 1.242 + 1.243 + mRenderState = RENDER_STATE_NATIVE_DRAWING_DONE; 1.244 + } else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_BLACK) { 1.245 + mBlackSurface = mWinSurface; 1.246 + mWinSurface = nullptr; 1.247 + 1.248 + mRenderState = RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE; 1.249 + } else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_WHITE) { 1.250 + mWhiteSurface = mWinSurface; 1.251 + mWinSurface = nullptr; 1.252 + 1.253 + mRenderState = RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE; 1.254 + } else { 1.255 + NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::EndNativeDrawing"); 1.256 + } 1.257 +} 1.258 + 1.259 +void 1.260 +gfxWindowsNativeDrawing::PaintToContext() 1.261 +{ 1.262 + if (mRenderState == RENDER_STATE_NATIVE_DRAWING_DONE) { 1.263 + // nothing to do, it already went to the context 1.264 + mRenderState = RENDER_STATE_DONE; 1.265 + } else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE) { 1.266 + nsRefPtr<gfxImageSurface> black = mBlackSurface->GetAsImageSurface(); 1.267 + nsRefPtr<gfxImageSurface> white = mWhiteSurface->GetAsImageSurface(); 1.268 + if (!gfxAlphaRecovery::RecoverAlpha(black, white)) { 1.269 + NS_ERROR("Alpha recovery failure"); 1.270 + return; 1.271 + } 1.272 + nsRefPtr<gfxImageSurface> alphaSurface = 1.273 + new gfxImageSurface(black->Data(), black->GetSize(), 1.274 + black->Stride(), 1.275 + gfxImageFormat::ARGB32); 1.276 + 1.277 + mContext->Save(); 1.278 + mContext->Translate(mNativeRect.TopLeft()); 1.279 + mContext->NewPath(); 1.280 + mContext->Rectangle(gfxRect(gfxPoint(0.0, 0.0), mNativeRect.Size())); 1.281 + 1.282 + nsRefPtr<gfxPattern> pat = new gfxPattern(alphaSurface); 1.283 + 1.284 + gfxMatrix m; 1.285 + m.Scale(mScale.width, mScale.height); 1.286 + pat->SetMatrix(m); 1.287 + 1.288 + if (mNativeDrawFlags & DO_NEAREST_NEIGHBOR_FILTERING) 1.289 + pat->SetFilter(GraphicsFilter::FILTER_FAST); 1.290 + 1.291 + pat->SetExtend(gfxPattern::EXTEND_PAD); 1.292 + mContext->SetPattern(pat); 1.293 + mContext->Fill(); 1.294 + mContext->Restore(); 1.295 + 1.296 + mRenderState = RENDER_STATE_DONE; 1.297 + } else { 1.298 + NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::PaintToContext"); 1.299 + } 1.300 +} 1.301 + 1.302 +void 1.303 +gfxWindowsNativeDrawing::TransformToNativeRect(const gfxRect& r, 1.304 + RECT& rout) 1.305 +{ 1.306 + /* If we're doing native drawing, then we're still in the coordinate space 1.307 + * of the context; otherwise, we're in our own little world, 1.308 + * relative to the passed-in nativeRect. 1.309 + */ 1.310 + 1.311 + gfxRect roundedRect(r); 1.312 + 1.313 + if (mRenderState == RENDER_STATE_NATIVE_DRAWING) { 1.314 + if (mTransformType == TRANSLATION_ONLY) { 1.315 + roundedRect.MoveBy(mTranslation); 1.316 + } 1.317 + } else { 1.318 + roundedRect.MoveBy(-mNativeRect.TopLeft()); 1.319 + } 1.320 + 1.321 + roundedRect.Round(); 1.322 + 1.323 + rout.left = LONG(roundedRect.X()); 1.324 + rout.right = LONG(roundedRect.XMost()); 1.325 + rout.top = LONG(roundedRect.Y()); 1.326 + rout.bottom = LONG(roundedRect.YMost()); 1.327 +}