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/. */
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"
19 #include "cairo-win32.h"
21 using namespace mozilla
;
22 using namespace mozilla::gfx
;
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
,
38 gfxWindowsNativeDrawing::gfxWindowsNativeDrawing(gfxContext
* ctx
,
39 const gfxRect
& nativeRect
,
40 uint32_t nativeDrawFlags
)
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
));
55 cairo_surface_t
* s
= cairo_get_group_target(cairo
);
57 mDeviceOffset
= mContext
->GetDeviceOffset();
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
;
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
);
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
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));
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"
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);
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
));
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
);
191 NS_ERROR("Bogus render state!");
196 bool gfxWindowsNativeDrawing::ShouldRenderAgain() {
197 switch (mRenderState
) {
198 case RENDER_STATE_NATIVE_DRAWING_DONE
:
201 case RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE
:
202 mRenderState
= RENDER_STATE_ALPHA_RECOVERY_WHITE
;
205 case RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE
:
210 "Invalid RenderState in gfxWindowsNativeDrawing::ShouldRenderAgain");
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
,
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
;
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");
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
;
275 NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::PaintToContext");
279 void gfxWindowsNativeDrawing::TransformToNativeRect(const gfxRect
& r
,
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
);
293 roundedRect
.MoveBy(-mNativeRect
.TopLeft());
298 rout
.left
= LONG(roundedRect
.X());
299 rout
.right
= LONG(roundedRect
.XMost());
300 rout
.top
= LONG(roundedRect
.Y());
301 rout
.bottom
= LONG(roundedRect
.YMost());