|
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
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/. */ |
|
5 |
|
6 #include "nsDeviceContext.h" |
|
7 #include <algorithm> // for max |
|
8 #include "gfxASurface.h" // for gfxASurface, etc |
|
9 #include "gfxFont.h" // for gfxFontGroup |
|
10 #include "gfxImageSurface.h" // for gfxImageSurface |
|
11 #include "gfxPoint.h" // for gfxSize |
|
12 #include "mozilla/Attributes.h" // for MOZ_FINAL |
|
13 #include "mozilla/Preferences.h" // for Preferences |
|
14 #include "mozilla/Services.h" // for GetObserverService |
|
15 #include "mozilla/mozalloc.h" // for operator new |
|
16 #include "nsCRT.h" // for nsCRT |
|
17 #include "nsDebug.h" // for NS_NOTREACHED, NS_ASSERTION, etc |
|
18 #include "nsFont.h" // for nsFont |
|
19 #include "nsFontMetrics.h" // for nsFontMetrics |
|
20 #include "nsIAtom.h" // for nsIAtom, do_GetAtom |
|
21 #include "nsID.h" |
|
22 #include "nsIDeviceContextSpec.h" // for nsIDeviceContextSpec |
|
23 #include "nsILanguageAtomService.h" // for nsILanguageAtomService, etc |
|
24 #include "nsIObserver.h" // for nsIObserver, etc |
|
25 #include "nsIObserverService.h" // for nsIObserverService |
|
26 #include "nsIScreen.h" // for nsIScreen |
|
27 #include "nsIScreenManager.h" // for nsIScreenManager |
|
28 #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc |
|
29 #include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE |
|
30 #include "nsIWidget.h" // for nsIWidget, NS_NATIVE_WINDOW |
|
31 #include "nsRect.h" // for nsRect |
|
32 #include "nsRenderingContext.h" // for nsRenderingContext |
|
33 #include "nsServiceManagerUtils.h" // for do_GetService |
|
34 #include "nsString.h" // for nsDependentString |
|
35 #include "nsTArray.h" // for nsTArray, nsTArray_Impl |
|
36 #include "nsThreadUtils.h" // for NS_IsMainThread |
|
37 |
|
38 #if !XP_MACOSX |
|
39 #include "gfxPDFSurface.h" |
|
40 #endif |
|
41 |
|
42 #ifdef MOZ_WIDGET_GTK |
|
43 #include "gfxPSSurface.h" |
|
44 #elif XP_WIN |
|
45 #include "gfxWindowsSurface.h" |
|
46 #elif XP_MACOSX |
|
47 #include "gfxQuartzSurface.h" |
|
48 #endif |
|
49 |
|
50 using namespace mozilla; |
|
51 using mozilla::services::GetObserverService; |
|
52 |
|
53 class nsFontCache MOZ_FINAL : public nsIObserver |
|
54 { |
|
55 public: |
|
56 nsFontCache() { MOZ_COUNT_CTOR(nsFontCache); } |
|
57 ~nsFontCache() { MOZ_COUNT_DTOR(nsFontCache); } |
|
58 |
|
59 NS_DECL_ISUPPORTS |
|
60 NS_DECL_NSIOBSERVER |
|
61 |
|
62 void Init(nsDeviceContext* aContext); |
|
63 void Destroy(); |
|
64 |
|
65 nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, |
|
66 gfxUserFontSet* aUserFontSet, |
|
67 gfxTextPerfMetrics* aTextPerf, |
|
68 nsFontMetrics*& aMetrics); |
|
69 |
|
70 void FontMetricsDeleted(const nsFontMetrics* aFontMetrics); |
|
71 void Compact(); |
|
72 void Flush(); |
|
73 |
|
74 protected: |
|
75 nsDeviceContext* mContext; // owner |
|
76 nsCOMPtr<nsIAtom> mLocaleLanguage; |
|
77 nsTArray<nsFontMetrics*> mFontMetrics; |
|
78 }; |
|
79 |
|
80 NS_IMPL_ISUPPORTS(nsFontCache, nsIObserver) |
|
81 |
|
82 // The Init and Destroy methods are necessary because it's not |
|
83 // safe to call AddObserver from a constructor or RemoveObserver |
|
84 // from a destructor. That should be fixed. |
|
85 void |
|
86 nsFontCache::Init(nsDeviceContext* aContext) |
|
87 { |
|
88 mContext = aContext; |
|
89 // register as a memory-pressure observer to free font resources |
|
90 // in low-memory situations. |
|
91 nsCOMPtr<nsIObserverService> obs = GetObserverService(); |
|
92 if (obs) |
|
93 obs->AddObserver(this, "memory-pressure", false); |
|
94 |
|
95 nsCOMPtr<nsILanguageAtomService> langService; |
|
96 langService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID); |
|
97 if (langService) { |
|
98 mLocaleLanguage = langService->GetLocaleLanguage(); |
|
99 } |
|
100 if (!mLocaleLanguage) { |
|
101 mLocaleLanguage = do_GetAtom("x-western"); |
|
102 } |
|
103 } |
|
104 |
|
105 void |
|
106 nsFontCache::Destroy() |
|
107 { |
|
108 nsCOMPtr<nsIObserverService> obs = GetObserverService(); |
|
109 if (obs) |
|
110 obs->RemoveObserver(this, "memory-pressure"); |
|
111 Flush(); |
|
112 } |
|
113 |
|
114 NS_IMETHODIMP |
|
115 nsFontCache::Observe(nsISupports*, const char* aTopic, const char16_t*) |
|
116 { |
|
117 if (!nsCRT::strcmp(aTopic, "memory-pressure")) |
|
118 Compact(); |
|
119 return NS_OK; |
|
120 } |
|
121 |
|
122 nsresult |
|
123 nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, |
|
124 gfxUserFontSet* aUserFontSet, |
|
125 gfxTextPerfMetrics* aTextPerf, |
|
126 nsFontMetrics*& aMetrics) |
|
127 { |
|
128 if (!aLanguage) |
|
129 aLanguage = mLocaleLanguage; |
|
130 |
|
131 // First check our cache |
|
132 // start from the end, which is where we put the most-recent-used element |
|
133 |
|
134 nsFontMetrics* fm; |
|
135 int32_t n = mFontMetrics.Length() - 1; |
|
136 for (int32_t i = n; i >= 0; --i) { |
|
137 fm = mFontMetrics[i]; |
|
138 if (fm->Font().Equals(aFont) && fm->GetUserFontSet() == aUserFontSet && |
|
139 fm->Language() == aLanguage) { |
|
140 if (i != n) { |
|
141 // promote it to the end of the cache |
|
142 mFontMetrics.RemoveElementAt(i); |
|
143 mFontMetrics.AppendElement(fm); |
|
144 } |
|
145 fm->GetThebesFontGroup()->UpdateFontList(); |
|
146 NS_ADDREF(aMetrics = fm); |
|
147 return NS_OK; |
|
148 } |
|
149 } |
|
150 |
|
151 // It's not in the cache. Get font metrics and then cache them. |
|
152 |
|
153 fm = new nsFontMetrics(); |
|
154 NS_ADDREF(fm); |
|
155 nsresult rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf); |
|
156 if (NS_SUCCEEDED(rv)) { |
|
157 // the mFontMetrics list has the "head" at the end, because append |
|
158 // is cheaper than insert |
|
159 mFontMetrics.AppendElement(fm); |
|
160 aMetrics = fm; |
|
161 NS_ADDREF(aMetrics); |
|
162 return NS_OK; |
|
163 } |
|
164 fm->Destroy(); |
|
165 NS_RELEASE(fm); |
|
166 |
|
167 // One reason why Init() fails is because the system is running out of |
|
168 // resources. e.g., on Win95/98 only a very limited number of GDI |
|
169 // objects are available. Compact the cache and try again. |
|
170 |
|
171 Compact(); |
|
172 fm = new nsFontMetrics(); |
|
173 NS_ADDREF(fm); |
|
174 rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf); |
|
175 if (NS_SUCCEEDED(rv)) { |
|
176 mFontMetrics.AppendElement(fm); |
|
177 aMetrics = fm; |
|
178 return NS_OK; |
|
179 } |
|
180 fm->Destroy(); |
|
181 NS_RELEASE(fm); |
|
182 |
|
183 // could not setup a new one, send an old one (XXX search a "best |
|
184 // match"?) |
|
185 |
|
186 n = mFontMetrics.Length() - 1; // could have changed in Compact() |
|
187 if (n >= 0) { |
|
188 aMetrics = mFontMetrics[n]; |
|
189 NS_ADDREF(aMetrics); |
|
190 return NS_OK; |
|
191 } |
|
192 |
|
193 NS_POSTCONDITION(NS_SUCCEEDED(rv), |
|
194 "font metrics should not be null - bug 136248"); |
|
195 return rv; |
|
196 } |
|
197 |
|
198 void |
|
199 nsFontCache::FontMetricsDeleted(const nsFontMetrics* aFontMetrics) |
|
200 { |
|
201 mFontMetrics.RemoveElement(aFontMetrics); |
|
202 } |
|
203 |
|
204 void |
|
205 nsFontCache::Compact() |
|
206 { |
|
207 // Need to loop backward because the running element can be removed on |
|
208 // the way |
|
209 for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) { |
|
210 nsFontMetrics* fm = mFontMetrics[i]; |
|
211 nsFontMetrics* oldfm = fm; |
|
212 // Destroy() isn't here because we want our device context to be |
|
213 // notified |
|
214 NS_RELEASE(fm); // this will reset fm to nullptr |
|
215 // if the font is really gone, it would have called back in |
|
216 // FontMetricsDeleted() and would have removed itself |
|
217 if (mFontMetrics.IndexOf(oldfm) != mFontMetrics.NoIndex) { |
|
218 // nope, the font is still there, so let's hold onto it too |
|
219 NS_ADDREF(oldfm); |
|
220 } |
|
221 } |
|
222 } |
|
223 |
|
224 void |
|
225 nsFontCache::Flush() |
|
226 { |
|
227 for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) { |
|
228 nsFontMetrics* fm = mFontMetrics[i]; |
|
229 // Destroy() will unhook our device context from the fm so that we |
|
230 // won't waste time in triggering the notification of |
|
231 // FontMetricsDeleted() in the subsequent release |
|
232 fm->Destroy(); |
|
233 NS_RELEASE(fm); |
|
234 } |
|
235 mFontMetrics.Clear(); |
|
236 } |
|
237 |
|
238 nsDeviceContext::nsDeviceContext() |
|
239 : mWidth(0), mHeight(0), mDepth(0), |
|
240 mAppUnitsPerDevPixel(-1), mAppUnitsPerDevNotScaledPixel(-1), |
|
241 mAppUnitsPerPhysicalInch(-1), |
|
242 mPixelScale(1.0f), mPrintingScale(1.0f), |
|
243 mFontCache(nullptr) |
|
244 { |
|
245 MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread"); |
|
246 } |
|
247 |
|
248 // Note: we use a bare pointer for mFontCache so that nsFontCache |
|
249 // can be an incomplete type in nsDeviceContext.h. |
|
250 // Therefore we have to do all the refcounting by hand. |
|
251 nsDeviceContext::~nsDeviceContext() |
|
252 { |
|
253 if (mFontCache) { |
|
254 mFontCache->Destroy(); |
|
255 NS_RELEASE(mFontCache); |
|
256 } |
|
257 } |
|
258 |
|
259 nsresult |
|
260 nsDeviceContext::GetMetricsFor(const nsFont& aFont, |
|
261 nsIAtom* aLanguage, |
|
262 gfxUserFontSet* aUserFontSet, |
|
263 gfxTextPerfMetrics* aTextPerf, |
|
264 nsFontMetrics*& aMetrics) |
|
265 { |
|
266 if (!mFontCache) { |
|
267 mFontCache = new nsFontCache(); |
|
268 NS_ADDREF(mFontCache); |
|
269 mFontCache->Init(this); |
|
270 } |
|
271 |
|
272 return mFontCache->GetMetricsFor(aFont, aLanguage, aUserFontSet, |
|
273 aTextPerf, aMetrics); |
|
274 } |
|
275 |
|
276 nsresult |
|
277 nsDeviceContext::FlushFontCache(void) |
|
278 { |
|
279 if (mFontCache) |
|
280 mFontCache->Flush(); |
|
281 return NS_OK; |
|
282 } |
|
283 |
|
284 nsresult |
|
285 nsDeviceContext::FontMetricsDeleted(const nsFontMetrics* aFontMetrics) |
|
286 { |
|
287 if (mFontCache) { |
|
288 mFontCache->FontMetricsDeleted(aFontMetrics); |
|
289 } |
|
290 return NS_OK; |
|
291 } |
|
292 |
|
293 bool |
|
294 nsDeviceContext::IsPrinterSurface() |
|
295 { |
|
296 return mPrintingSurface != nullptr; |
|
297 } |
|
298 |
|
299 void |
|
300 nsDeviceContext::SetDPI() |
|
301 { |
|
302 float dpi = -1.0f; |
|
303 |
|
304 // PostScript, PDF and Mac (when printing) all use 72 dpi |
|
305 // Use a printing DC to determine the other dpi values |
|
306 if (mPrintingSurface) { |
|
307 switch (mPrintingSurface->GetType()) { |
|
308 case gfxSurfaceType::PDF: |
|
309 case gfxSurfaceType::PS: |
|
310 case gfxSurfaceType::Quartz: |
|
311 dpi = 72.0f; |
|
312 break; |
|
313 #ifdef XP_WIN |
|
314 case gfxSurfaceType::Win32: |
|
315 case gfxSurfaceType::Win32Printing: { |
|
316 HDC dc = reinterpret_cast<gfxWindowsSurface*>(mPrintingSurface.get())->GetDC(); |
|
317 int32_t OSVal = GetDeviceCaps(dc, LOGPIXELSY); |
|
318 dpi = 144.0f; |
|
319 mPrintingScale = float(OSVal) / dpi; |
|
320 break; |
|
321 } |
|
322 #endif |
|
323 default: |
|
324 NS_NOTREACHED("Unexpected printing surface type"); |
|
325 break; |
|
326 } |
|
327 |
|
328 mAppUnitsPerDevNotScaledPixel = |
|
329 NS_lround((AppUnitsPerCSSPixel() * 96) / dpi); |
|
330 } else { |
|
331 // A value of -1 means use the maximum of 96 and the system DPI. |
|
332 // A value of 0 means use the system DPI. A positive value is used as the DPI. |
|
333 // This sets the physical size of a device pixel and thus controls the |
|
334 // interpretation of physical units. |
|
335 int32_t prefDPI = Preferences::GetInt("layout.css.dpi", -1); |
|
336 |
|
337 if (prefDPI > 0) { |
|
338 dpi = prefDPI; |
|
339 } else if (mWidget) { |
|
340 dpi = mWidget->GetDPI(); |
|
341 |
|
342 if (prefDPI < 0) { |
|
343 dpi = std::max(96.0f, dpi); |
|
344 } |
|
345 } else { |
|
346 dpi = 96.0f; |
|
347 } |
|
348 |
|
349 CSSToLayoutDeviceScale scale = mWidget ? mWidget->GetDefaultScale() |
|
350 : CSSToLayoutDeviceScale(1.0); |
|
351 double devPixelsPerCSSPixel = scale.scale; |
|
352 |
|
353 mAppUnitsPerDevNotScaledPixel = |
|
354 std::max(1, NS_lround(AppUnitsPerCSSPixel() / devPixelsPerCSSPixel)); |
|
355 } |
|
356 |
|
357 NS_ASSERTION(dpi != -1.0, "no dpi set"); |
|
358 |
|
359 mAppUnitsPerPhysicalInch = NS_lround(dpi * mAppUnitsPerDevNotScaledPixel); |
|
360 UpdateScaledAppUnits(); |
|
361 } |
|
362 |
|
363 nsresult |
|
364 nsDeviceContext::Init(nsIWidget *aWidget) |
|
365 { |
|
366 if (mScreenManager && mWidget == aWidget) |
|
367 return NS_OK; |
|
368 |
|
369 mWidget = aWidget; |
|
370 SetDPI(); |
|
371 |
|
372 if (mScreenManager) |
|
373 return NS_OK; |
|
374 |
|
375 mScreenManager = do_GetService("@mozilla.org/gfx/screenmanager;1"); |
|
376 |
|
377 return NS_OK; |
|
378 } |
|
379 |
|
380 already_AddRefed<nsRenderingContext> |
|
381 nsDeviceContext::CreateRenderingContext() |
|
382 { |
|
383 nsRefPtr<gfxASurface> printingSurface = mPrintingSurface; |
|
384 #ifdef XP_MACOSX |
|
385 // CreateRenderingContext() can be called (on reflow) after EndPage() |
|
386 // but before BeginPage(). On OS X (and only there) mPrintingSurface |
|
387 // will in this case be null, because OS X printing surfaces are |
|
388 // per-page, and therefore only truly valid between calls to BeginPage() |
|
389 // and EndPage(). But we can get away with fudging things here, if need |
|
390 // be, by using a cached copy. |
|
391 if (!printingSurface) { |
|
392 printingSurface = mCachedPrintingSurface; |
|
393 } |
|
394 #endif |
|
395 nsRefPtr<nsRenderingContext> pContext = new nsRenderingContext(); |
|
396 |
|
397 RefPtr<gfx::DrawTarget> dt = |
|
398 gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(printingSurface, |
|
399 gfx::IntSize(mWidth, mHeight)); |
|
400 |
|
401 pContext->Init(this, dt); |
|
402 pContext->ThebesContext()->SetFlag(gfxContext::FLAG_DISABLE_SNAPPING); |
|
403 pContext->Scale(mPrintingScale, mPrintingScale); |
|
404 |
|
405 return pContext.forget(); |
|
406 } |
|
407 |
|
408 nsresult |
|
409 nsDeviceContext::GetDepth(uint32_t& aDepth) |
|
410 { |
|
411 if (mDepth == 0) { |
|
412 nsCOMPtr<nsIScreen> primaryScreen; |
|
413 mScreenManager->GetPrimaryScreen(getter_AddRefs(primaryScreen)); |
|
414 primaryScreen->GetColorDepth(reinterpret_cast<int32_t *>(&mDepth)); |
|
415 } |
|
416 |
|
417 aDepth = mDepth; |
|
418 return NS_OK; |
|
419 } |
|
420 |
|
421 nsresult |
|
422 nsDeviceContext::GetDeviceSurfaceDimensions(nscoord &aWidth, nscoord &aHeight) |
|
423 { |
|
424 if (mPrintingSurface) { |
|
425 // we have a printer device |
|
426 aWidth = mWidth; |
|
427 aHeight = mHeight; |
|
428 } else { |
|
429 nsRect area; |
|
430 ComputeFullAreaUsingScreen(&area); |
|
431 aWidth = area.width; |
|
432 aHeight = area.height; |
|
433 } |
|
434 |
|
435 return NS_OK; |
|
436 } |
|
437 |
|
438 nsresult |
|
439 nsDeviceContext::GetRect(nsRect &aRect) |
|
440 { |
|
441 if (mPrintingSurface) { |
|
442 // we have a printer device |
|
443 aRect.x = 0; |
|
444 aRect.y = 0; |
|
445 aRect.width = mWidth; |
|
446 aRect.height = mHeight; |
|
447 } else |
|
448 ComputeFullAreaUsingScreen ( &aRect ); |
|
449 |
|
450 return NS_OK; |
|
451 } |
|
452 |
|
453 nsresult |
|
454 nsDeviceContext::GetClientRect(nsRect &aRect) |
|
455 { |
|
456 if (mPrintingSurface) { |
|
457 // we have a printer device |
|
458 aRect.x = 0; |
|
459 aRect.y = 0; |
|
460 aRect.width = mWidth; |
|
461 aRect.height = mHeight; |
|
462 } |
|
463 else |
|
464 ComputeClientRectUsingScreen(&aRect); |
|
465 |
|
466 return NS_OK; |
|
467 } |
|
468 |
|
469 nsresult |
|
470 nsDeviceContext::InitForPrinting(nsIDeviceContextSpec *aDevice) |
|
471 { |
|
472 NS_ENSURE_ARG_POINTER(aDevice); |
|
473 |
|
474 mDeviceContextSpec = aDevice; |
|
475 |
|
476 nsresult rv = aDevice->GetSurfaceForPrinter(getter_AddRefs(mPrintingSurface)); |
|
477 if (NS_FAILED(rv)) |
|
478 return NS_ERROR_FAILURE; |
|
479 |
|
480 Init(nullptr); |
|
481 |
|
482 CalcPrintingSize(); |
|
483 |
|
484 return NS_OK; |
|
485 } |
|
486 |
|
487 nsresult |
|
488 nsDeviceContext::BeginDocument(const nsAString& aTitle, |
|
489 char16_t* aPrintToFileName, |
|
490 int32_t aStartPage, |
|
491 int32_t aEndPage) |
|
492 { |
|
493 static const char16_t kEmpty[] = { '\0' }; |
|
494 nsresult rv; |
|
495 |
|
496 rv = mPrintingSurface->BeginPrinting(aTitle, |
|
497 nsDependentString(aPrintToFileName ? aPrintToFileName : kEmpty)); |
|
498 |
|
499 if (NS_SUCCEEDED(rv) && mDeviceContextSpec) |
|
500 rv = mDeviceContextSpec->BeginDocument(aTitle, aPrintToFileName, aStartPage, aEndPage); |
|
501 |
|
502 return rv; |
|
503 } |
|
504 |
|
505 |
|
506 nsresult |
|
507 nsDeviceContext::EndDocument(void) |
|
508 { |
|
509 nsresult rv = NS_OK; |
|
510 |
|
511 if (mPrintingSurface) { |
|
512 rv = mPrintingSurface->EndPrinting(); |
|
513 if (NS_SUCCEEDED(rv)) |
|
514 mPrintingSurface->Finish(); |
|
515 } |
|
516 |
|
517 if (mDeviceContextSpec) |
|
518 mDeviceContextSpec->EndDocument(); |
|
519 |
|
520 return rv; |
|
521 } |
|
522 |
|
523 |
|
524 nsresult |
|
525 nsDeviceContext::AbortDocument(void) |
|
526 { |
|
527 nsresult rv = mPrintingSurface->AbortPrinting(); |
|
528 |
|
529 if (mDeviceContextSpec) |
|
530 mDeviceContextSpec->EndDocument(); |
|
531 |
|
532 return rv; |
|
533 } |
|
534 |
|
535 |
|
536 nsresult |
|
537 nsDeviceContext::BeginPage(void) |
|
538 { |
|
539 nsresult rv = NS_OK; |
|
540 |
|
541 if (mDeviceContextSpec) |
|
542 rv = mDeviceContextSpec->BeginPage(); |
|
543 |
|
544 if (NS_FAILED(rv)) return rv; |
|
545 |
|
546 #ifdef XP_MACOSX |
|
547 // We need to get a new surface for each page on the Mac, as the |
|
548 // CGContextRefs are only good for one page. |
|
549 mDeviceContextSpec->GetSurfaceForPrinter(getter_AddRefs(mPrintingSurface)); |
|
550 #endif |
|
551 |
|
552 rv = mPrintingSurface->BeginPage(); |
|
553 |
|
554 return rv; |
|
555 } |
|
556 |
|
557 nsresult |
|
558 nsDeviceContext::EndPage(void) |
|
559 { |
|
560 nsresult rv = mPrintingSurface->EndPage(); |
|
561 |
|
562 #ifdef XP_MACOSX |
|
563 // We need to release the CGContextRef in the surface here, plus it's |
|
564 // not something you would want anyway, as these CGContextRefs are only |
|
565 // good for one page. But we need to keep a cached reference to it, since |
|
566 // CreateRenderingContext() may try to access it when mPrintingSurface |
|
567 // would normally be null. See bug 665218. If we just stop nulling out |
|
568 // mPrintingSurface here (and thereby make that our cached copy), we'll |
|
569 // break all our null checks on mPrintingSurface. See bug 684622. |
|
570 mCachedPrintingSurface = mPrintingSurface; |
|
571 mPrintingSurface = nullptr; |
|
572 #endif |
|
573 |
|
574 if (mDeviceContextSpec) |
|
575 mDeviceContextSpec->EndPage(); |
|
576 |
|
577 return rv; |
|
578 } |
|
579 |
|
580 void |
|
581 nsDeviceContext::ComputeClientRectUsingScreen(nsRect* outRect) |
|
582 { |
|
583 // we always need to recompute the clientRect |
|
584 // because the window may have moved onto a different screen. In the single |
|
585 // monitor case, we only need to do the computation if we haven't done it |
|
586 // once already, and remember that we have because we're assured it won't change. |
|
587 nsCOMPtr<nsIScreen> screen; |
|
588 FindScreen (getter_AddRefs(screen)); |
|
589 if (screen) { |
|
590 int32_t x, y, width, height; |
|
591 screen->GetAvailRect(&x, &y, &width, &height); |
|
592 |
|
593 // convert to device units |
|
594 outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel()); |
|
595 outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel()); |
|
596 outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel()); |
|
597 outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel()); |
|
598 } |
|
599 } |
|
600 |
|
601 void |
|
602 nsDeviceContext::ComputeFullAreaUsingScreen(nsRect* outRect) |
|
603 { |
|
604 // if we have more than one screen, we always need to recompute the clientRect |
|
605 // because the window may have moved onto a different screen. In the single |
|
606 // monitor case, we only need to do the computation if we haven't done it |
|
607 // once already, and remember that we have because we're assured it won't change. |
|
608 nsCOMPtr<nsIScreen> screen; |
|
609 FindScreen ( getter_AddRefs(screen) ); |
|
610 if ( screen ) { |
|
611 int32_t x, y, width, height; |
|
612 screen->GetRect ( &x, &y, &width, &height ); |
|
613 |
|
614 // convert to device units |
|
615 outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel()); |
|
616 outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel()); |
|
617 outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel()); |
|
618 outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel()); |
|
619 |
|
620 mWidth = outRect->width; |
|
621 mHeight = outRect->height; |
|
622 } |
|
623 } |
|
624 |
|
625 // |
|
626 // FindScreen |
|
627 // |
|
628 // Determines which screen intersects the largest area of the given surface. |
|
629 // |
|
630 void |
|
631 nsDeviceContext::FindScreen(nsIScreen** outScreen) |
|
632 { |
|
633 if (mWidget && mWidget->GetNativeData(NS_NATIVE_WINDOW)) |
|
634 mScreenManager->ScreenForNativeWidget(mWidget->GetNativeData(NS_NATIVE_WINDOW), |
|
635 outScreen); |
|
636 else |
|
637 mScreenManager->GetPrimaryScreen(outScreen); |
|
638 } |
|
639 |
|
640 void |
|
641 nsDeviceContext::CalcPrintingSize() |
|
642 { |
|
643 if (!mPrintingSurface) |
|
644 return; |
|
645 |
|
646 bool inPoints = true; |
|
647 |
|
648 gfxSize size(0, 0); |
|
649 switch (mPrintingSurface->GetType()) { |
|
650 case gfxSurfaceType::Image: |
|
651 inPoints = false; |
|
652 size = reinterpret_cast<gfxImageSurface*>(mPrintingSurface.get())->GetSize(); |
|
653 break; |
|
654 |
|
655 #if defined(MOZ_PDF_PRINTING) |
|
656 case gfxSurfaceType::PDF: |
|
657 inPoints = true; |
|
658 size = reinterpret_cast<gfxPDFSurface*>(mPrintingSurface.get())->GetSize(); |
|
659 break; |
|
660 #endif |
|
661 |
|
662 #ifdef MOZ_WIDGET_GTK |
|
663 case gfxSurfaceType::PS: |
|
664 inPoints = true; |
|
665 size = reinterpret_cast<gfxPSSurface*>(mPrintingSurface.get())->GetSize(); |
|
666 break; |
|
667 #endif |
|
668 |
|
669 #ifdef XP_MACOSX |
|
670 case gfxSurfaceType::Quartz: |
|
671 inPoints = true; // this is really only true when we're printing |
|
672 size = reinterpret_cast<gfxQuartzSurface*>(mPrintingSurface.get())->GetSize(); |
|
673 break; |
|
674 #endif |
|
675 |
|
676 #ifdef XP_WIN |
|
677 case gfxSurfaceType::Win32: |
|
678 case gfxSurfaceType::Win32Printing: |
|
679 { |
|
680 inPoints = false; |
|
681 HDC dc = reinterpret_cast<gfxWindowsSurface*>(mPrintingSurface.get())->GetDC(); |
|
682 if (!dc) |
|
683 dc = GetDC((HWND)mWidget->GetNativeData(NS_NATIVE_WIDGET)); |
|
684 size.width = NSFloatPixelsToAppUnits(::GetDeviceCaps(dc, HORZRES)/mPrintingScale, AppUnitsPerDevPixel()); |
|
685 size.height = NSFloatPixelsToAppUnits(::GetDeviceCaps(dc, VERTRES)/mPrintingScale, AppUnitsPerDevPixel()); |
|
686 mDepth = (uint32_t)::GetDeviceCaps(dc, BITSPIXEL); |
|
687 if (dc != reinterpret_cast<gfxWindowsSurface*>(mPrintingSurface.get())->GetDC()) |
|
688 ReleaseDC((HWND)mWidget->GetNativeData(NS_NATIVE_WIDGET), dc); |
|
689 break; |
|
690 } |
|
691 #endif |
|
692 |
|
693 default: |
|
694 NS_ERROR("trying to print to unknown surface type"); |
|
695 } |
|
696 |
|
697 if (inPoints) { |
|
698 // For printing, CSS inches and physical inches are identical |
|
699 // so it doesn't matter which we use here |
|
700 mWidth = NSToCoordRound(float(size.width) * AppUnitsPerPhysicalInch() / 72); |
|
701 mHeight = NSToCoordRound(float(size.height) * AppUnitsPerPhysicalInch() / 72); |
|
702 } else { |
|
703 mWidth = NSToIntRound(size.width); |
|
704 mHeight = NSToIntRound(size.height); |
|
705 } |
|
706 } |
|
707 |
|
708 bool nsDeviceContext::CheckDPIChange() { |
|
709 int32_t oldDevPixels = mAppUnitsPerDevNotScaledPixel; |
|
710 int32_t oldInches = mAppUnitsPerPhysicalInch; |
|
711 |
|
712 SetDPI(); |
|
713 |
|
714 return oldDevPixels != mAppUnitsPerDevNotScaledPixel || |
|
715 oldInches != mAppUnitsPerPhysicalInch; |
|
716 } |
|
717 |
|
718 bool |
|
719 nsDeviceContext::SetPixelScale(float aScale) |
|
720 { |
|
721 if (aScale <= 0) { |
|
722 NS_NOTREACHED("Invalid pixel scale value"); |
|
723 return false; |
|
724 } |
|
725 int32_t oldAppUnitsPerDevPixel = mAppUnitsPerDevPixel; |
|
726 mPixelScale = aScale; |
|
727 UpdateScaledAppUnits(); |
|
728 return oldAppUnitsPerDevPixel != mAppUnitsPerDevPixel; |
|
729 } |
|
730 |
|
731 void |
|
732 nsDeviceContext::UpdateScaledAppUnits() |
|
733 { |
|
734 mAppUnitsPerDevPixel = |
|
735 std::max(1, NSToIntRound(float(mAppUnitsPerDevNotScaledPixel) / mPixelScale)); |
|
736 // adjust mPixelScale to reflect appunit rounding |
|
737 mPixelScale = float(mAppUnitsPerDevNotScaledPixel) / mAppUnitsPerDevPixel; |
|
738 } |