Bug 435739 Poor performance of Firefox 3 with no X RENDER extension
[wine-gecko.git] / gfx / thebes / src / gfxWindowsNativeDrawing.cpp
blobce6587d9b39925380b6361bd0d66e072e098e6a8
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
13 * License.
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.
21 * Contributor(s):
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 ***** */
38 #include <windows.h>
40 #include "nsMathUtils.h"
42 #include "gfxWindowsNativeDrawing.h"
43 #include "gfxWindowsSurface.h"
44 #include "gfxAlphaRecovery.h"
46 enum {
47 RENDER_STATE_INIT,
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,
57 RENDER_STATE_DONE
60 gfxWindowsNativeDrawing::gfxWindowsNativeDrawing(gfxContext* ctx,
61 const gfxRect& nativeRect,
62 PRUint32 nativeDrawFlags)
63 : mContext(ctx), mNativeRect(nativeRect), mNativeDrawFlags(nativeDrawFlags), mRenderState(RENDER_STATE_INIT)
67 HDC
68 gfxWindowsNativeDrawing::BeginNativeDrawing()
70 if (mRenderState == RENDER_STATE_INIT) {
71 nsRefPtr<gfxASurface> surf = mContext->CurrentSurface(&mDeviceOffset.x, &mDeviceOffset.y);
72 if (!surf || surf->CairoStatus())
73 return nsnull;
75 gfxMatrix m = mContext->CurrentMatrix();
76 if (!m.HasNonTranslation())
77 mTransformType = TRANSLATION_ONLY;
78 else if (m.HasNonAxisAlignedTransform())
79 mTransformType = COMPLEX;
80 else
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);
133 } else {
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();
157 // grab the DC
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,
171 NULL);
173 return mDC;
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));
187 else
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);
197 return mDC;
198 } else {
199 NS_ERROR("Bogus render state!");
200 return nsnull;
204 PRBool
205 gfxWindowsNativeDrawing::ShouldRenderAgain()
207 switch (mRenderState) {
208 case RENDER_STATE_NATIVE_DRAWING_DONE:
209 return PR_FALSE;
211 case RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE:
212 mRenderState = RENDER_STATE_ALPHA_RECOVERY_WHITE;
213 return PR_TRUE;
215 case RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE:
216 return PR_FALSE;
218 default:
219 NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::ShouldRenderAgain");
220 break;
223 return PR_FALSE;
226 void
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;
249 } else {
250 NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::EndNativeDrawing");
254 void
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);
266 mContext->Save();
267 mContext->Translate(mNativeRect.pos);
268 mContext->NewPath();
269 mContext->Rectangle(gfxRect(gfxPoint(0.0, 0.0), mNativeRect.size));
271 nsRefPtr<gfxPattern> pat = new gfxPattern(alphaSurface);
273 gfxMatrix m;
274 m.Scale(mScale.width, mScale.height);
275 pat->SetMatrix(m);
277 if (mNativeDrawFlags & DO_NEAREST_NEIGHBOR_FILTERING)
278 pat->SetFilter(0);
280 mContext->SetPattern(pat);
281 mContext->Fill();
282 mContext->Restore();
284 mRenderState = RENDER_STATE_DONE;
285 } else {
286 NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::PaintToContext");
290 void
291 gfxWindowsNativeDrawing::TransformToNativeRect(const gfxRect& r,
292 RECT& rout)
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);
305 } else {
306 roundedRect.MoveBy(- mNativeRect.pos);
309 roundedRect.Round();
311 rout.left = LONG(roundedRect.X());
312 rout.right = LONG(roundedRect.XMost());
313 rout.top = LONG(roundedRect.Y());
314 rout.bottom = LONG(roundedRect.YMost());