1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Thebes gfx.
17 * The Initial Developer of the Original Code is Mozilla Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 2007
19 * the Initial Developer. All Rights Reserved.
22 * Vladimir Vukicevic <vladimir@pobox.com>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
40 #include "nsMathUtils.h"
42 #include "gfxWindowsNativeDrawing.h"
43 #include "gfxWindowsSurface.h"
44 #include "gfxAlphaRecovery.h"
49 RENDER_STATE_NATIVE_DRAWING
,
50 RENDER_STATE_NATIVE_DRAWING_DONE
,
52 RENDER_STATE_ALPHA_RECOVERY_BLACK
,
53 RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE
,
54 RENDER_STATE_ALPHA_RECOVERY_WHITE
,
55 RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE
,
60 gfxWindowsNativeDrawing::gfxWindowsNativeDrawing(gfxContext
* ctx
,
61 const gfxRect
& nativeRect
,
62 PRUint32 nativeDrawFlags
)
63 : mContext(ctx
), mNativeRect(nativeRect
), mNativeDrawFlags(nativeDrawFlags
), mRenderState(RENDER_STATE_INIT
)
68 gfxWindowsNativeDrawing::BeginNativeDrawing()
70 if (mRenderState
== RENDER_STATE_INIT
) {
71 nsRefPtr
<gfxASurface
> surf
= mContext
->CurrentSurface(&mDeviceOffset
.x
, &mDeviceOffset
.y
);
72 if (!surf
|| surf
->CairoStatus())
75 gfxMatrix m
= mContext
->CurrentMatrix();
76 if (!m
.HasNonTranslation())
77 mTransformType
= TRANSLATION_ONLY
;
78 else if (m
.HasNonAxisAlignedTransform())
79 mTransformType
= COMPLEX
;
81 mTransformType
= AXIS_ALIGNED_SCALE
;
83 // if this is a native win32 surface, we don't have to
84 // redirect rendering to our own HDC; in some cases,
85 // we may be able to use the HDC from the surface directly.
86 if ((surf
->GetType() == gfxASurface::SurfaceTypeWin32
||
87 surf
->GetType() == gfxASurface::SurfaceTypeWin32Printing
) &&
88 (surf
->GetContentType() == gfxASurface::CONTENT_COLOR
||
89 (surf
->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA
&&
90 (mNativeDrawFlags
& CAN_DRAW_TO_COLOR_ALPHA
))))
92 if (mTransformType
== TRANSLATION_ONLY
) {
93 mRenderState
= RENDER_STATE_NATIVE_DRAWING
;
95 mTranslation
= m
.GetTranslation();
97 mWinSurface
= static_cast<gfxWindowsSurface
*>(static_cast<gfxASurface
*>(surf
.get()));
98 } else if (((mTransformType
== AXIS_ALIGNED_SCALE
)
99 && (mNativeDrawFlags
& CAN_AXIS_ALIGNED_SCALE
)) ||
100 (mNativeDrawFlags
& CAN_COMPLEX_TRANSFORM
))
102 mWorldTransform
.eM11
= (FLOAT
) m
.xx
;
103 mWorldTransform
.eM12
= (FLOAT
) m
.yx
;
104 mWorldTransform
.eM21
= (FLOAT
) m
.xy
;
105 mWorldTransform
.eM22
= (FLOAT
) m
.yy
;
106 mWorldTransform
.eDx
= (FLOAT
) m
.x0
;
107 mWorldTransform
.eDy
= (FLOAT
) m
.y0
;
109 mRenderState
= RENDER_STATE_NATIVE_DRAWING
;
110 mWinSurface
= static_cast<gfxWindowsSurface
*>(static_cast<gfxASurface
*>(surf
.get()));
114 // If we couldn't do native drawing, then we have to do two-buffer drawing
115 // and do alpha recovery
116 if (mRenderState
== RENDER_STATE_INIT
) {
117 mRenderState
= RENDER_STATE_ALPHA_RECOVERY_BLACK
;
119 // we only do the scale bit if we can do an axis aligned
120 // scale; otherwise we scale (if necessary) after
121 // rendering with cairo. Note that if we're doing alpha recovery,
122 // we cannot do a full complex transform with win32 (I mean, we could, but
123 // it would require more code that's not here.)
124 if (mTransformType
== TRANSLATION_ONLY
|| !(mNativeDrawFlags
& CAN_AXIS_ALIGNED_SCALE
)) {
125 mScale
= gfxSize(1.0, 1.0);
127 // Add 1 to the surface size; it's guaranteed to not be incorrect,
128 // and it fixes bug 382458
129 // There's probably a better fix, but I haven't figured out
130 // the root cause of the problem.
131 mTempSurfaceSize
.width
= (PRInt32
) NS_ceil(mNativeRect
.size
.width
+ 1);
132 mTempSurfaceSize
.height
= (PRInt32
) NS_ceil(mNativeRect
.size
.height
+ 1);
134 // figure out the scale factors
135 mScale
= m
.ScaleFactors(PR_TRUE
);
137 mWorldTransform
.eM11
= (FLOAT
) mScale
.width
;
138 mWorldTransform
.eM12
= 0.0f
;
139 mWorldTransform
.eM21
= 0.0f
;
140 mWorldTransform
.eM22
= (FLOAT
) mScale
.height
;
141 mWorldTransform
.eDx
= 0.0f
;
142 mWorldTransform
.eDy
= 0.0f
;
144 // See comment above about "+1"
145 mTempSurfaceSize
.width
= (PRInt32
) NS_ceil(mNativeRect
.size
.width
* mScale
.width
+ 1);
146 mTempSurfaceSize
.height
= (PRInt32
) NS_ceil(mNativeRect
.size
.height
* mScale
.height
+ 1);
151 if (mRenderState
== RENDER_STATE_NATIVE_DRAWING
) {
152 // we can just do native drawing directly to the context's surface
154 // Need to force the clip to be set
155 mContext
->UpdateSurfaceClip();
158 mDC
= mWinSurface
->GetDC();
160 // do we need to use SetWorldTransform?
161 if (mTransformType
!= TRANSLATION_ONLY
) {
162 SetGraphicsMode(mDC
, GM_ADVANCED
);
163 GetWorldTransform(mDC
, &mOldWorldTransform
);
164 SetWorldTransform(mDC
, &mWorldTransform
);
167 GetViewportOrgEx(mDC
, &mOrigViewportOrigin
);
168 SetViewportOrgEx(mDC
,
169 mOrigViewportOrigin
.x
+ (int)mDeviceOffset
.x
,
170 mOrigViewportOrigin
.y
+ (int)mDeviceOffset
.y
,
174 } else if (mRenderState
== RENDER_STATE_ALPHA_RECOVERY_BLACK
||
175 mRenderState
== RENDER_STATE_ALPHA_RECOVERY_WHITE
)
177 // we're going to use mWinSurface to create our temporary surface here
179 // get us a RGB24 DIB; DIB is important, because
180 // we can later call GetImageSurface on it.
181 mWinSurface
= new gfxWindowsSurface(mTempSurfaceSize
);
182 mDC
= mWinSurface
->GetDC();
184 RECT r
= { 0, 0, mTempSurfaceSize
.width
, mTempSurfaceSize
.height
};
185 if (mRenderState
== RENDER_STATE_ALPHA_RECOVERY_BLACK
)
186 FillRect(mDC
, &r
, (HBRUSH
)GetStockObject(BLACK_BRUSH
));
188 FillRect(mDC
, &r
, (HBRUSH
)GetStockObject(WHITE_BRUSH
));
190 if ((mTransformType
!= TRANSLATION_ONLY
) &&
191 (mNativeDrawFlags
& CAN_AXIS_ALIGNED_SCALE
))
193 SetGraphicsMode(mDC
, GM_ADVANCED
);
194 SetWorldTransform(mDC
, &mWorldTransform
);
199 NS_ERROR("Bogus render state!");
205 gfxWindowsNativeDrawing::ShouldRenderAgain()
207 switch (mRenderState
) {
208 case RENDER_STATE_NATIVE_DRAWING_DONE
:
211 case RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE
:
212 mRenderState
= RENDER_STATE_ALPHA_RECOVERY_WHITE
;
215 case RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE
:
219 NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::ShouldRenderAgain");
227 gfxWindowsNativeDrawing::EndNativeDrawing()
229 if (mRenderState
== RENDER_STATE_NATIVE_DRAWING
) {
230 // we drew directly to the HDC in the context; undo our changes
231 SetViewportOrgEx(mDC
, mOrigViewportOrigin
.x
, mOrigViewportOrigin
.y
, NULL
);
233 if (mTransformType
!= TRANSLATION_ONLY
)
234 SetWorldTransform(mDC
, &mOldWorldTransform
);
236 mWinSurface
->MarkDirty();
238 mRenderState
= RENDER_STATE_NATIVE_DRAWING_DONE
;
239 } else if (mRenderState
== RENDER_STATE_ALPHA_RECOVERY_BLACK
) {
240 mBlackSurface
= mWinSurface
;
241 mWinSurface
= nsnull
;
243 mRenderState
= RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE
;
244 } else if (mRenderState
== RENDER_STATE_ALPHA_RECOVERY_WHITE
) {
245 mWhiteSurface
= mWinSurface
;
246 mWinSurface
= nsnull
;
248 mRenderState
= RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE
;
250 NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::EndNativeDrawing");
255 gfxWindowsNativeDrawing::PaintToContext()
257 if (mRenderState
== RENDER_STATE_NATIVE_DRAWING_DONE
) {
258 // nothing to do, it already went to the context
259 mRenderState
= RENDER_STATE_DONE
;
260 } else if (mRenderState
== RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE
) {
261 nsRefPtr
<gfxImageSurface
> black
= mBlackSurface
->GetImageSurface();
262 nsRefPtr
<gfxImageSurface
> white
= mWhiteSurface
->GetImageSurface();
263 nsRefPtr
<gfxImageSurface
> alphaSurface
=
264 gfxAlphaRecovery::RecoverAlpha(black
, white
, mTempSurfaceSize
);
267 mContext
->Translate(mNativeRect
.pos
);
269 mContext
->Rectangle(gfxRect(gfxPoint(0.0, 0.0), mNativeRect
.size
));
271 nsRefPtr
<gfxPattern
> pat
= new gfxPattern(alphaSurface
);
274 m
.Scale(mScale
.width
, mScale
.height
);
277 if (mNativeDrawFlags
& DO_NEAREST_NEIGHBOR_FILTERING
)
280 mContext
->SetPattern(pat
);
284 mRenderState
= RENDER_STATE_DONE
;
286 NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::PaintToContext");
291 gfxWindowsNativeDrawing::TransformToNativeRect(const gfxRect
& r
,
294 /* If we're doing native drawing, then we're still in the coordinate space
295 * of the context; otherwise, we're in our own little world,
296 * relative to the passed-in nativeRect.
299 gfxRect
roundedRect(r
);
301 if (mRenderState
== RENDER_STATE_NATIVE_DRAWING
) {
302 if (mTransformType
== TRANSLATION_ONLY
) {
303 roundedRect
.MoveBy(mTranslation
);
306 roundedRect
.MoveBy(- mNativeRect
.pos
);
311 rout
.left
= LONG(roundedRect
.X());
312 rout
.right
= LONG(roundedRect
.XMost());
313 rout
.top
= LONG(roundedRect
.Y());
314 rout
.bottom
= LONG(roundedRect
.YMost());