Bug 1874684 - Part 28: Return DateDuration from DifferenceISODateTime. r=mgaudet
[gecko.git] / gfx / thebes / gfxWindowsNativeDrawing.cpp
blob548ec56869b7a6f41be6cbe7ab2f70bb0dadc004
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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/. */
6 #include <windows.h>
8 #include "nsMathUtils.h"
10 #include "gfxWindowsNativeDrawing.h"
11 #include "gfxWindowsSurface.h"
12 #include "gfxAlphaRecovery.h"
13 #include "gfxPattern.h"
14 #include "mozilla/gfx/2D.h"
15 #include "mozilla/gfx/Helpers.h"
16 #include "gfx2DGlue.h"
18 #include "cairo.h"
19 #include "cairo-win32.h"
21 using namespace mozilla;
22 using namespace mozilla::gfx;
24 enum {
25 RENDER_STATE_INIT,
27 RENDER_STATE_NATIVE_DRAWING,
28 RENDER_STATE_NATIVE_DRAWING_DONE,
30 RENDER_STATE_ALPHA_RECOVERY_BLACK,
31 RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE,
32 RENDER_STATE_ALPHA_RECOVERY_WHITE,
33 RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE,
35 RENDER_STATE_DONE
38 gfxWindowsNativeDrawing::gfxWindowsNativeDrawing(gfxContext* ctx,
39 const gfxRect& nativeRect,
40 uint32_t nativeDrawFlags)
41 : mContext(ctx),
42 mNativeRect(nativeRect),
43 mNativeDrawFlags(nativeDrawFlags),
44 mRenderState(RENDER_STATE_INIT) {}
46 HDC gfxWindowsNativeDrawing::BeginNativeDrawing() {
47 if (mRenderState == RENDER_STATE_INIT) {
48 RefPtr<gfxASurface> surf;
49 DrawTarget* drawTarget = mContext->GetDrawTarget();
50 cairo_t* cairo = nullptr;
51 if (drawTarget->GetBackendType() == BackendType::CAIRO) {
52 cairo = static_cast<cairo_t*>(
53 drawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
54 if (cairo) {
55 cairo_surface_t* s = cairo_get_group_target(cairo);
56 if (s) {
57 mDeviceOffset = mContext->GetDeviceOffset();
58 double sdx, sdy;
59 cairo_surface_get_device_offset(s, &sdx, &sdy);
60 mDeviceOffset.x -= sdx;
61 mDeviceOffset.y -= sdy;
62 surf = gfxASurface::Wrap(s);
67 if (surf && surf->CairoStatus() != 0) return nullptr;
69 gfxMatrix m = mContext->CurrentMatrixDouble();
70 if (!m.HasNonTranslation())
71 mTransformType = TRANSLATION_ONLY;
72 else if (m.HasNonAxisAlignedTransform())
73 mTransformType = COMPLEX;
74 else
75 mTransformType = AXIS_ALIGNED_SCALE;
77 // if this is a native win32 surface, we don't have to
78 // redirect rendering to our own HDC; in some cases,
79 // we may be able to use the HDC from the surface directly.
80 if (surf && ((surf->GetType() == gfxSurfaceType::Win32 ||
81 surf->GetType() == gfxSurfaceType::Win32Printing) &&
82 (surf->GetContentType() == gfxContentType::COLOR ||
83 (surf->GetContentType() == gfxContentType::COLOR_ALPHA &&
84 (mNativeDrawFlags & CAN_DRAW_TO_COLOR_ALPHA))))) {
85 // grab the DC. This can fail if there is a complex clipping path,
86 // in which case we'll have to fall back.
87 mWinSurface = static_cast<gfxWindowsSurface*>(
88 static_cast<gfxASurface*>(surf.get()));
89 mDC = cairo_win32_get_dc_with_clip(cairo);
91 if (mDC) {
92 if (mTransformType == TRANSLATION_ONLY) {
93 mRenderState = RENDER_STATE_NATIVE_DRAWING;
95 mTranslation = m.GetTranslation();
96 } else if (((mTransformType == AXIS_ALIGNED_SCALE) &&
97 (mNativeDrawFlags & CAN_AXIS_ALIGNED_SCALE)) ||
98 (mNativeDrawFlags & CAN_COMPLEX_TRANSFORM)) {
99 mWorldTransform.eM11 = (FLOAT)m._11;
100 mWorldTransform.eM12 = (FLOAT)m._12;
101 mWorldTransform.eM21 = (FLOAT)m._21;
102 mWorldTransform.eM22 = (FLOAT)m._22;
103 mWorldTransform.eDx = (FLOAT)m._31;
104 mWorldTransform.eDy = (FLOAT)m._32;
106 mRenderState = RENDER_STATE_NATIVE_DRAWING;
111 // If we couldn't do native drawing, then we have to do two-buffer drawing
112 // and do alpha recovery
113 if (mRenderState == RENDER_STATE_INIT) {
114 mRenderState = RENDER_STATE_ALPHA_RECOVERY_BLACK;
116 // We round out our native rect here, that way the snapping will
117 // happen correctly.
118 mNativeRect.RoundOut();
120 // we only do the scale bit if we can do an axis aligned
121 // scale; otherwise we scale (if necessary) after
122 // rendering with cairo. Note that if we're doing alpha recovery,
123 // we cannot do a full complex transform with win32 (I mean, we could, but
124 // it would require more code that's not here.)
125 if (mTransformType == TRANSLATION_ONLY ||
126 !(mNativeDrawFlags & CAN_AXIS_ALIGNED_SCALE)) {
127 mScale = MatrixScalesDouble();
129 // Add 1 to the surface size; it's guaranteed to not be incorrect,
130 // and it fixes bug 382458
131 // There's probably a better fix, but I haven't figured out
132 // the root cause of the problem.
133 mTempSurfaceSize = IntSize((int32_t)ceil(mNativeRect.Width() + 1),
134 (int32_t)ceil(mNativeRect.Height() + 1));
135 } else {
136 // figure out the scale factors
137 mScale = m.ScaleFactors();
139 mWorldTransform.eM11 = (FLOAT)mScale.xScale;
140 mWorldTransform.eM12 = 0.0f;
141 mWorldTransform.eM21 = 0.0f;
142 mWorldTransform.eM22 = (FLOAT)mScale.yScale;
143 mWorldTransform.eDx = 0.0f;
144 mWorldTransform.eDy = 0.0f;
146 // See comment above about "+1"
147 mTempSurfaceSize =
148 IntSize((int32_t)ceil(mNativeRect.Width() * mScale.xScale + 1),
149 (int32_t)ceil(mNativeRect.Height() * mScale.yScale + 1));
154 if (mRenderState == RENDER_STATE_NATIVE_DRAWING) {
155 // we can just do native drawing directly to the context's surface
157 // do we need to use SetWorldTransform?
158 if (mTransformType != TRANSLATION_ONLY) {
159 SetGraphicsMode(mDC, GM_ADVANCED);
160 GetWorldTransform(mDC, &mOldWorldTransform);
161 SetWorldTransform(mDC, &mWorldTransform);
163 GetViewportOrgEx(mDC, &mOrigViewportOrigin);
164 SetViewportOrgEx(mDC, mOrigViewportOrigin.x - (int)mDeviceOffset.x,
165 mOrigViewportOrigin.y - (int)mDeviceOffset.y, nullptr);
167 return mDC;
168 } else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_BLACK ||
169 mRenderState == RENDER_STATE_ALPHA_RECOVERY_WHITE) {
170 // we're going to use mWinSurface to create our temporary surface here
172 // get us a RGB24 DIB; DIB is important, because
173 // we can later call GetImageSurface on it.
174 mWinSurface = new gfxWindowsSurface(mTempSurfaceSize);
175 mDC = mWinSurface->GetDC();
177 RECT r = {0, 0, mTempSurfaceSize.width, mTempSurfaceSize.height};
178 if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_BLACK)
179 FillRect(mDC, &r, (HBRUSH)GetStockObject(BLACK_BRUSH));
180 else
181 FillRect(mDC, &r, (HBRUSH)GetStockObject(WHITE_BRUSH));
183 if ((mTransformType != TRANSLATION_ONLY) &&
184 (mNativeDrawFlags & CAN_AXIS_ALIGNED_SCALE)) {
185 SetGraphicsMode(mDC, GM_ADVANCED);
186 SetWorldTransform(mDC, &mWorldTransform);
189 return mDC;
190 } else {
191 NS_ERROR("Bogus render state!");
192 return nullptr;
196 bool gfxWindowsNativeDrawing::ShouldRenderAgain() {
197 switch (mRenderState) {
198 case RENDER_STATE_NATIVE_DRAWING_DONE:
199 return false;
201 case RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE:
202 mRenderState = RENDER_STATE_ALPHA_RECOVERY_WHITE;
203 return true;
205 case RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE:
206 return false;
208 default:
209 NS_ERROR(
210 "Invalid RenderState in gfxWindowsNativeDrawing::ShouldRenderAgain");
211 break;
214 return false;
217 void gfxWindowsNativeDrawing::EndNativeDrawing() {
218 if (mRenderState == RENDER_STATE_NATIVE_DRAWING) {
219 // we drew directly to the HDC in the context; undo our changes
220 SetViewportOrgEx(mDC, mOrigViewportOrigin.x, mOrigViewportOrigin.y,
221 nullptr);
223 if (mTransformType != TRANSLATION_ONLY)
224 SetWorldTransform(mDC, &mOldWorldTransform);
226 mWinSurface->MarkDirty();
228 mRenderState = RENDER_STATE_NATIVE_DRAWING_DONE;
229 } else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_BLACK) {
230 mBlackSurface = mWinSurface;
231 mWinSurface = nullptr;
233 mRenderState = RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE;
234 } else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_WHITE) {
235 mWhiteSurface = mWinSurface;
236 mWinSurface = nullptr;
238 mRenderState = RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE;
239 } else {
240 NS_ERROR(
241 "Invalid RenderState in gfxWindowsNativeDrawing::EndNativeDrawing");
245 void gfxWindowsNativeDrawing::PaintToContext() {
246 if (mRenderState == RENDER_STATE_NATIVE_DRAWING_DONE) {
247 // nothing to do, it already went to the context
248 mRenderState = RENDER_STATE_DONE;
249 } else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE) {
250 RefPtr<gfxImageSurface> black = mBlackSurface->GetAsImageSurface();
251 RefPtr<gfxImageSurface> white = mWhiteSurface->GetAsImageSurface();
252 if (!gfxAlphaRecovery::RecoverAlpha(black, white)) {
253 NS_ERROR("Alpha recovery failure");
254 return;
256 RefPtr<DataSourceSurface> source = Factory::CreateWrappingDataSourceSurface(
257 black->Data(), black->Stride(), black->GetSize(),
258 SurfaceFormat::B8G8R8A8);
260 DrawTarget* dt = mContext->GetDrawTarget();
261 AutoRestoreTransform autoRestoreTransform(dt);
263 Matrix newTransform = dt->GetTransform();
264 newTransform.PreTranslate(ToPoint(mNativeRect.TopLeft()));
265 dt->SetTransform(newTransform);
267 Rect rect(Point(0.0, 0.0), ToSize(mNativeRect.Size()));
268 Matrix m = Matrix::Scaling(1.0 / mScale.xScale, 1.0 / mScale.yScale);
269 SurfacePattern pat(source, ExtendMode::CLAMP, m);
270 dt->FillRect(rect, pat);
273 mRenderState = RENDER_STATE_DONE;
274 } else {
275 NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::PaintToContext");
279 void gfxWindowsNativeDrawing::TransformToNativeRect(const gfxRect& r,
280 RECT& rout) {
281 /* If we're doing native drawing, then we're still in the coordinate space
282 * of the context; otherwise, we're in our own little world,
283 * relative to the passed-in nativeRect.
286 gfxRect roundedRect(r);
288 if (mRenderState == RENDER_STATE_NATIVE_DRAWING) {
289 if (mTransformType == TRANSLATION_ONLY) {
290 roundedRect.MoveBy(mTranslation);
292 } else {
293 roundedRect.MoveBy(-mNativeRect.TopLeft());
296 roundedRect.Round();
298 rout.left = LONG(roundedRect.X());
299 rout.right = LONG(roundedRect.XMost());
300 rout.top = LONG(roundedRect.Y());
301 rout.bottom = LONG(roundedRect.YMost());