Line data Source code
1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim: set sw=4 ts=4 expandtab: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "nsDeviceContext.h"
8 : #include <algorithm> // for max
9 : #include "gfxASurface.h" // for gfxASurface, etc
10 : #include "gfxContext.h"
11 : #include "gfxFont.h" // for gfxFontGroup
12 : #include "gfxImageSurface.h" // for gfxImageSurface
13 : #include "gfxPoint.h" // for gfxSize
14 : #include "mozilla/Attributes.h" // for final
15 : #include "mozilla/gfx/PathHelpers.h"
16 : #include "mozilla/gfx/PrintTarget.h"
17 : #include "mozilla/Preferences.h" // for Preferences
18 : #include "mozilla/Services.h" // for GetObserverService
19 : #include "mozilla/mozalloc.h" // for operator new
20 : #include "nsCRT.h" // for nsCRT
21 : #include "nsDebug.h" // for NS_NOTREACHED, NS_ASSERTION, etc
22 : #include "nsFont.h" // for nsFont
23 : #include "nsFontMetrics.h" // for nsFontMetrics
24 : #include "nsIAtom.h" // for nsIAtom, NS_Atomize
25 : #include "nsID.h"
26 : #include "nsIDeviceContextSpec.h" // for nsIDeviceContextSpec
27 : #include "nsLanguageAtomService.h" // for nsLanguageAtomService
28 : #include "nsIObserver.h" // for nsIObserver, etc
29 : #include "nsIObserverService.h" // for nsIObserverService
30 : #include "nsIScreen.h" // for nsIScreen
31 : #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
32 : #include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
33 : #include "nsIWidget.h" // for nsIWidget, NS_NATIVE_WINDOW
34 : #include "nsRect.h" // for nsRect
35 : #include "nsServiceManagerUtils.h" // for do_GetService
36 : #include "nsString.h" // for nsDependentString
37 : #include "nsTArray.h" // for nsTArray, nsTArray_Impl
38 : #include "nsThreadUtils.h" // for NS_IsMainThread
39 : #include "mozilla/gfx/Logging.h"
40 : #include "mozilla/widget/ScreenManager.h" // for ScreenManager
41 :
42 : using namespace mozilla;
43 : using namespace mozilla::gfx;
44 : using mozilla::services::GetObserverService;
45 : using mozilla::widget::ScreenManager;
46 :
47 : class nsFontCache final : public nsIObserver
48 : {
49 : public:
50 5 : nsFontCache() {}
51 :
52 : NS_DECL_ISUPPORTS
53 : NS_DECL_NSIOBSERVER
54 :
55 : void Init(nsDeviceContext* aContext);
56 : void Destroy();
57 :
58 : already_AddRefed<nsFontMetrics> GetMetricsFor(
59 : const nsFont& aFont, const nsFontMetrics::Params& aParams);
60 :
61 : void FontMetricsDeleted(const nsFontMetrics* aFontMetrics);
62 : void Compact();
63 : void Flush();
64 :
65 : void UpdateUserFonts(gfxUserFontSet* aUserFontSet);
66 :
67 : protected:
68 0 : ~nsFontCache() {}
69 :
70 : nsDeviceContext* mContext; // owner
71 : nsCOMPtr<nsIAtom> mLocaleLanguage;
72 : nsTArray<nsFontMetrics*> mFontMetrics;
73 : };
74 :
75 10 : NS_IMPL_ISUPPORTS(nsFontCache, nsIObserver)
76 :
77 : // The Init and Destroy methods are necessary because it's not
78 : // safe to call AddObserver from a constructor or RemoveObserver
79 : // from a destructor. That should be fixed.
80 : void
81 5 : nsFontCache::Init(nsDeviceContext* aContext)
82 : {
83 5 : mContext = aContext;
84 : // register as a memory-pressure observer to free font resources
85 : // in low-memory situations.
86 10 : nsCOMPtr<nsIObserverService> obs = GetObserverService();
87 5 : if (obs)
88 5 : obs->AddObserver(this, "memory-pressure", false);
89 :
90 5 : mLocaleLanguage = nsLanguageAtomService::GetService()->GetLocaleLanguage();
91 5 : if (!mLocaleLanguage) {
92 0 : mLocaleLanguage = NS_Atomize("x-western");
93 : }
94 5 : }
95 :
96 : void
97 0 : nsFontCache::Destroy()
98 : {
99 0 : nsCOMPtr<nsIObserverService> obs = GetObserverService();
100 0 : if (obs)
101 0 : obs->RemoveObserver(this, "memory-pressure");
102 0 : Flush();
103 0 : }
104 :
105 : NS_IMETHODIMP
106 0 : nsFontCache::Observe(nsISupports*, const char* aTopic, const char16_t*)
107 : {
108 0 : if (!nsCRT::strcmp(aTopic, "memory-pressure"))
109 0 : Compact();
110 0 : return NS_OK;
111 : }
112 :
113 : already_AddRefed<nsFontMetrics>
114 415 : nsFontCache::GetMetricsFor(const nsFont& aFont,
115 : const nsFontMetrics::Params& aParams)
116 : {
117 415 : nsIAtom* language = aParams.language ? aParams.language
118 415 : : mLocaleLanguage.get();
119 :
120 : // First check our cache
121 : // start from the end, which is where we put the most-recent-used element
122 :
123 415 : int32_t n = mFontMetrics.Length() - 1;
124 415 : for (int32_t i = n; i >= 0; --i) {
125 409 : nsFontMetrics* fm = mFontMetrics[i];
126 1227 : if (fm->Font().Equals(aFont) &&
127 818 : fm->GetUserFontSet() == aParams.userFontSet &&
128 1227 : fm->Language() == language &&
129 409 : fm->Orientation() == aParams.orientation) {
130 409 : if (i != n) {
131 : // promote it to the end of the cache
132 0 : mFontMetrics.RemoveElementAt(i);
133 0 : mFontMetrics.AppendElement(fm);
134 : }
135 409 : fm->GetThebesFontGroup()->UpdateUserFonts();
136 409 : return do_AddRef(fm);
137 : }
138 : }
139 :
140 : // It's not in the cache. Get font metrics and then cache them.
141 :
142 6 : nsFontMetrics::Params params = aParams;
143 6 : params.language = language;
144 12 : RefPtr<nsFontMetrics> fm = new nsFontMetrics(aFont, params, mContext);
145 : // the mFontMetrics list has the "head" at the end, because append
146 : // is cheaper than insert
147 6 : mFontMetrics.AppendElement(do_AddRef(fm.get()).take());
148 6 : return fm.forget();
149 : }
150 :
151 : void
152 0 : nsFontCache::UpdateUserFonts(gfxUserFontSet* aUserFontSet)
153 : {
154 0 : for (nsFontMetrics* fm : mFontMetrics) {
155 0 : gfxFontGroup* fg = fm->GetThebesFontGroup();
156 0 : if (fg->GetUserFontSet() == aUserFontSet) {
157 0 : fg->UpdateUserFonts();
158 : }
159 : }
160 0 : }
161 :
162 : void
163 0 : nsFontCache::FontMetricsDeleted(const nsFontMetrics* aFontMetrics)
164 : {
165 0 : mFontMetrics.RemoveElement(aFontMetrics);
166 0 : }
167 :
168 : void
169 0 : nsFontCache::Compact()
170 : {
171 : // Need to loop backward because the running element can be removed on
172 : // the way
173 0 : for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) {
174 0 : nsFontMetrics* fm = mFontMetrics[i];
175 0 : nsFontMetrics* oldfm = fm;
176 : // Destroy() isn't here because we want our device context to be
177 : // notified
178 0 : NS_RELEASE(fm); // this will reset fm to nullptr
179 : // if the font is really gone, it would have called back in
180 : // FontMetricsDeleted() and would have removed itself
181 0 : if (mFontMetrics.IndexOf(oldfm) != mFontMetrics.NoIndex) {
182 : // nope, the font is still there, so let's hold onto it too
183 0 : NS_ADDREF(oldfm);
184 : }
185 : }
186 0 : }
187 :
188 : void
189 1 : nsFontCache::Flush()
190 : {
191 2 : for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) {
192 1 : nsFontMetrics* fm = mFontMetrics[i];
193 : // Destroy() will unhook our device context from the fm so that we
194 : // won't waste time in triggering the notification of
195 : // FontMetricsDeleted() in the subsequent release
196 1 : fm->Destroy();
197 1 : NS_RELEASE(fm);
198 : }
199 1 : mFontMetrics.Clear();
200 1 : }
201 :
202 29 : nsDeviceContext::nsDeviceContext()
203 : : mWidth(0), mHeight(0),
204 : mAppUnitsPerDevPixel(-1), mAppUnitsPerDevPixelAtUnitFullZoom(-1),
205 : mAppUnitsPerPhysicalInch(-1),
206 : mFullZoom(1.0f), mPrintingScale(1.0f),
207 : mIsCurrentlyPrintingDoc(false)
208 : #ifdef DEBUG
209 29 : , mIsInitialized(false)
210 : #endif
211 : {
212 29 : MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread");
213 29 : }
214 :
215 0 : nsDeviceContext::~nsDeviceContext()
216 : {
217 0 : if (mFontCache) {
218 0 : mFontCache->Destroy();
219 : }
220 0 : }
221 :
222 : void
223 415 : nsDeviceContext::InitFontCache()
224 : {
225 415 : if (!mFontCache) {
226 5 : mFontCache = new nsFontCache();
227 5 : mFontCache->Init(this);
228 : }
229 415 : }
230 :
231 : void
232 0 : nsDeviceContext::UpdateFontCacheUserFonts(gfxUserFontSet* aUserFontSet)
233 : {
234 0 : if (mFontCache) {
235 0 : mFontCache->UpdateUserFonts(aUserFontSet);
236 : }
237 0 : }
238 :
239 : already_AddRefed<nsFontMetrics>
240 415 : nsDeviceContext::GetMetricsFor(const nsFont& aFont,
241 : const nsFontMetrics::Params& aParams)
242 : {
243 415 : InitFontCache();
244 415 : return mFontCache->GetMetricsFor(aFont, aParams);
245 : }
246 :
247 : nsresult
248 3 : nsDeviceContext::FlushFontCache(void)
249 : {
250 3 : if (mFontCache)
251 1 : mFontCache->Flush();
252 3 : return NS_OK;
253 : }
254 :
255 : nsresult
256 0 : nsDeviceContext::FontMetricsDeleted(const nsFontMetrics* aFontMetrics)
257 : {
258 0 : if (mFontCache) {
259 0 : mFontCache->FontMetricsDeleted(aFontMetrics);
260 : }
261 0 : return NS_OK;
262 : }
263 :
264 : bool
265 20 : nsDeviceContext::IsPrinterContext()
266 : {
267 20 : return mPrintTarget != nullptr;
268 : }
269 :
270 : void
271 82 : nsDeviceContext::SetDPI(double* aScale)
272 : {
273 82 : float dpi = -1.0f;
274 :
275 : // Use the printing DC to determine DPI values, if we have one.
276 82 : if (mDeviceContextSpec) {
277 0 : dpi = mDeviceContextSpec->GetDPI();
278 0 : mPrintingScale = mDeviceContextSpec->GetPrintingScale();
279 0 : mAppUnitsPerDevPixelAtUnitFullZoom =
280 0 : NS_lround((AppUnitsPerCSSPixel() * 96) / dpi);
281 : } else {
282 : // A value of -1 means use the maximum of 96 and the system DPI.
283 : // A value of 0 means use the system DPI. A positive value is used as the DPI.
284 : // This sets the physical size of a device pixel and thus controls the
285 : // interpretation of physical units.
286 82 : int32_t prefDPI = Preferences::GetInt("layout.css.dpi", -1);
287 :
288 82 : if (prefDPI > 0) {
289 0 : dpi = prefDPI;
290 82 : } else if (mWidget) {
291 42 : dpi = mWidget->GetDPI();
292 :
293 42 : if (prefDPI < 0) {
294 42 : dpi = std::max(96.0f, dpi);
295 : }
296 : } else {
297 40 : dpi = 96.0f;
298 : }
299 :
300 : double devPixelsPerCSSPixel;
301 82 : if (aScale && *aScale > 0.0) {
302 : // if caller provided a scale, we just use it
303 0 : devPixelsPerCSSPixel = *aScale;
304 : } else {
305 : // otherwise get from the widget, and return it in aScale for
306 : // the caller to pass to child contexts if needed
307 : CSSToLayoutDeviceScale scale =
308 : mWidget ? mWidget->GetDefaultScale()
309 82 : : CSSToLayoutDeviceScale(1.0);
310 82 : devPixelsPerCSSPixel = scale.scale;
311 82 : if (aScale) {
312 1 : *aScale = devPixelsPerCSSPixel;
313 : }
314 : }
315 :
316 82 : mAppUnitsPerDevPixelAtUnitFullZoom =
317 164 : std::max(1, NS_lround(AppUnitsPerCSSPixel() / devPixelsPerCSSPixel));
318 : }
319 :
320 82 : NS_ASSERTION(dpi != -1.0, "no dpi set");
321 :
322 82 : mAppUnitsPerPhysicalInch = NS_lround(dpi * mAppUnitsPerDevPixelAtUnitFullZoom);
323 82 : UpdateAppUnitsForFullZoom();
324 82 : }
325 :
326 : nsresult
327 29 : nsDeviceContext::Init(nsIWidget *aWidget)
328 : {
329 : #ifdef DEBUG
330 : // We can't assert |!mIsInitialized| here since EndSwapDocShellsForDocument
331 : // re-initializes nsDeviceContext objects. We can only assert in
332 : // InitForPrinting (below).
333 29 : mIsInitialized = true;
334 : #endif
335 :
336 29 : nsresult rv = NS_OK;
337 29 : if (mScreenManager && mWidget == aWidget)
338 0 : return rv;
339 :
340 29 : mWidget = aWidget;
341 29 : SetDPI();
342 :
343 29 : if (mScreenManager)
344 0 : return rv;
345 :
346 29 : mScreenManager = do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
347 :
348 29 : return rv;
349 : }
350 :
351 : // XXX This is only for printing. We should make that obvious in the name.
352 : already_AddRefed<gfxContext>
353 0 : nsDeviceContext::CreateRenderingContext()
354 : {
355 0 : return CreateRenderingContextCommon(/* not a reference context */ false);
356 : }
357 :
358 : already_AddRefed<gfxContext>
359 0 : nsDeviceContext::CreateReferenceRenderingContext()
360 : {
361 0 : return CreateRenderingContextCommon(/* a reference context */ true);
362 : }
363 :
364 : already_AddRefed<gfxContext>
365 0 : nsDeviceContext::CreateRenderingContextCommon(bool aWantReferenceContext)
366 : {
367 0 : MOZ_ASSERT(IsPrinterContext());
368 0 : MOZ_ASSERT(mWidth > 0 && mHeight > 0);
369 :
370 : // This will usually be null, depending on the pref print.print_via_parent.
371 0 : RefPtr<DrawEventRecorder> recorder;
372 0 : mDeviceContextSpec->GetDrawEventRecorder(getter_AddRefs(recorder));
373 :
374 0 : RefPtr<gfx::DrawTarget> dt;
375 0 : if (aWantReferenceContext) {
376 0 : dt = mPrintTarget->GetReferenceDrawTarget(recorder);
377 : } else {
378 0 : dt = mPrintTarget->MakeDrawTarget(gfx::IntSize(mWidth, mHeight), recorder);
379 : }
380 :
381 0 : if (!dt || !dt->IsValid()) {
382 0 : gfxCriticalNote
383 0 : << "Failed to create draw target in device context sized "
384 0 : << mWidth << "x" << mHeight << " and pointer "
385 0 : << hexa(mPrintTarget);
386 0 : return nullptr;
387 : }
388 :
389 : #ifdef XP_MACOSX
390 : // The CGContextRef provided by PMSessionGetCGGraphicsContext is
391 : // write-only, so we need to prevent gfxContext::PushGroupAndCopyBackground
392 : // trying to read from it or else we'll crash.
393 : // XXXjwatt Consider adding a MakeDrawTarget override to PrintTargetCG and
394 : // moving this AddUserData call there.
395 : dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr);
396 : #endif
397 0 : dt->AddUserData(&sDisablePixelSnapping, (void*)0x1, nullptr);
398 :
399 0 : RefPtr<gfxContext> pContext = gfxContext::CreateOrNull(dt);
400 0 : MOZ_ASSERT(pContext); // already checked draw target above
401 :
402 0 : gfxMatrix transform;
403 0 : if (mPrintTarget->RotateNeededForLandscape()) {
404 : // Rotate page 90 degrees to draw landscape page on portrait paper
405 0 : IntSize size = mPrintTarget->GetSize();
406 0 : transform.PreTranslate(gfxPoint(0, size.width));
407 : gfxMatrix rotate(0, -1,
408 : 1, 0,
409 0 : 0, 0);
410 0 : transform = rotate * transform;
411 : }
412 0 : transform.PreScale(mPrintingScale, mPrintingScale);
413 :
414 0 : pContext->SetMatrix(transform);
415 0 : return pContext.forget();
416 : }
417 :
418 : nsresult
419 0 : nsDeviceContext::GetDepth(uint32_t& aDepth)
420 : {
421 0 : nsCOMPtr<nsIScreen> screen;
422 0 : FindScreen(getter_AddRefs(screen));
423 0 : if (!screen) {
424 0 : ScreenManager& screenManager = ScreenManager::GetSingleton();
425 0 : screenManager.GetPrimaryScreen(getter_AddRefs(screen));
426 0 : MOZ_ASSERT(screen);
427 : }
428 0 : screen->GetColorDepth(reinterpret_cast<int32_t *>(&aDepth));
429 :
430 0 : return NS_OK;
431 : }
432 :
433 : nsresult
434 0 : nsDeviceContext::GetDeviceSurfaceDimensions(nscoord &aWidth, nscoord &aHeight)
435 : {
436 0 : if (IsPrinterContext()) {
437 0 : aWidth = mWidth;
438 0 : aHeight = mHeight;
439 : } else {
440 0 : nsRect area;
441 0 : ComputeFullAreaUsingScreen(&area);
442 0 : aWidth = area.width;
443 0 : aHeight = area.height;
444 : }
445 :
446 0 : return NS_OK;
447 : }
448 :
449 : nsresult
450 10 : nsDeviceContext::GetRect(nsRect &aRect)
451 : {
452 10 : if (IsPrinterContext()) {
453 0 : aRect.x = 0;
454 0 : aRect.y = 0;
455 0 : aRect.width = mWidth;
456 0 : aRect.height = mHeight;
457 : } else
458 10 : ComputeFullAreaUsingScreen ( &aRect );
459 :
460 10 : return NS_OK;
461 : }
462 :
463 : nsresult
464 4 : nsDeviceContext::GetClientRect(nsRect &aRect)
465 : {
466 4 : if (IsPrinterContext()) {
467 0 : aRect.x = 0;
468 0 : aRect.y = 0;
469 0 : aRect.width = mWidth;
470 0 : aRect.height = mHeight;
471 : }
472 : else
473 4 : ComputeClientRectUsingScreen(&aRect);
474 :
475 4 : return NS_OK;
476 : }
477 :
478 : nsresult
479 0 : nsDeviceContext::InitForPrinting(nsIDeviceContextSpec *aDevice)
480 : {
481 0 : NS_ENSURE_ARG_POINTER(aDevice);
482 :
483 0 : MOZ_ASSERT(!mIsInitialized,
484 : "Only initialize once, immediately after construction");
485 :
486 : // We don't set mIsInitialized here. The Init() call below does that.
487 :
488 0 : mPrintTarget = aDevice->MakePrintTarget();
489 0 : if (!mPrintTarget) {
490 0 : return NS_ERROR_FAILURE;
491 : }
492 :
493 0 : mDeviceContextSpec = aDevice;
494 :
495 0 : Init(nullptr);
496 :
497 0 : if (!CalcPrintingSize()) {
498 0 : return NS_ERROR_FAILURE;
499 : }
500 :
501 0 : return NS_OK;
502 : }
503 :
504 : nsresult
505 0 : nsDeviceContext::BeginDocument(const nsAString& aTitle,
506 : const nsAString& aPrintToFileName,
507 : int32_t aStartPage,
508 : int32_t aEndPage)
509 : {
510 0 : MOZ_ASSERT(!mIsCurrentlyPrintingDoc,
511 : "Mismatched BeginDocument/EndDocument calls");
512 :
513 0 : nsresult rv = mPrintTarget->BeginPrinting(aTitle, aPrintToFileName,
514 0 : aStartPage, aEndPage);
515 :
516 0 : if (NS_SUCCEEDED(rv)) {
517 0 : if (mDeviceContextSpec) {
518 0 : rv = mDeviceContextSpec->BeginDocument(aTitle, aPrintToFileName,
519 0 : aStartPage, aEndPage);
520 : }
521 0 : mIsCurrentlyPrintingDoc = true;
522 : }
523 :
524 0 : return rv;
525 : }
526 :
527 :
528 : nsresult
529 0 : nsDeviceContext::EndDocument(void)
530 : {
531 0 : MOZ_ASSERT(mIsCurrentlyPrintingDoc,
532 : "Mismatched BeginDocument/EndDocument calls");
533 :
534 0 : nsresult rv = NS_OK;
535 :
536 0 : mIsCurrentlyPrintingDoc = false;
537 :
538 0 : rv = mPrintTarget->EndPrinting();
539 0 : if (NS_SUCCEEDED(rv)) {
540 0 : mPrintTarget->Finish();
541 : }
542 :
543 0 : if (mDeviceContextSpec)
544 0 : mDeviceContextSpec->EndDocument();
545 :
546 0 : mPrintTarget = nullptr;
547 :
548 0 : return rv;
549 : }
550 :
551 :
552 : nsresult
553 0 : nsDeviceContext::AbortDocument(void)
554 : {
555 0 : MOZ_ASSERT(mIsCurrentlyPrintingDoc,
556 : "Mismatched BeginDocument/EndDocument calls");
557 :
558 0 : nsresult rv = mPrintTarget->AbortPrinting();
559 :
560 0 : mIsCurrentlyPrintingDoc = false;
561 :
562 0 : if (mDeviceContextSpec)
563 0 : mDeviceContextSpec->EndDocument();
564 :
565 0 : mPrintTarget = nullptr;
566 :
567 0 : return rv;
568 : }
569 :
570 :
571 : nsresult
572 0 : nsDeviceContext::BeginPage(void)
573 : {
574 0 : nsresult rv = NS_OK;
575 :
576 0 : if (mDeviceContextSpec)
577 0 : rv = mDeviceContextSpec->BeginPage();
578 :
579 0 : if (NS_FAILED(rv)) return rv;
580 :
581 0 : return mPrintTarget->BeginPage();
582 : }
583 :
584 : nsresult
585 0 : nsDeviceContext::EndPage(void)
586 : {
587 0 : nsresult rv = mPrintTarget->EndPage();
588 :
589 0 : if (mDeviceContextSpec)
590 0 : mDeviceContextSpec->EndPage();
591 :
592 0 : return rv;
593 : }
594 :
595 : void
596 4 : nsDeviceContext::ComputeClientRectUsingScreen(nsRect* outRect)
597 : {
598 : // we always need to recompute the clientRect
599 : // because the window may have moved onto a different screen. In the single
600 : // monitor case, we only need to do the computation if we haven't done it
601 : // once already, and remember that we have because we're assured it won't change.
602 8 : nsCOMPtr<nsIScreen> screen;
603 4 : FindScreen (getter_AddRefs(screen));
604 4 : if (screen) {
605 : int32_t x, y, width, height;
606 4 : screen->GetAvailRect(&x, &y, &width, &height);
607 :
608 : // convert to device units
609 4 : outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel());
610 4 : outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel());
611 4 : outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel());
612 4 : outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel());
613 : }
614 4 : }
615 :
616 : void
617 10 : nsDeviceContext::ComputeFullAreaUsingScreen(nsRect* outRect)
618 : {
619 : // if we have more than one screen, we always need to recompute the clientRect
620 : // because the window may have moved onto a different screen. In the single
621 : // monitor case, we only need to do the computation if we haven't done it
622 : // once already, and remember that we have because we're assured it won't change.
623 20 : nsCOMPtr<nsIScreen> screen;
624 10 : FindScreen ( getter_AddRefs(screen) );
625 10 : if ( screen ) {
626 : int32_t x, y, width, height;
627 10 : screen->GetRect ( &x, &y, &width, &height );
628 :
629 : // convert to device units
630 10 : outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel());
631 10 : outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel());
632 10 : outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel());
633 10 : outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel());
634 :
635 10 : mWidth = outRect->width;
636 10 : mHeight = outRect->height;
637 : }
638 10 : }
639 :
640 : //
641 : // FindScreen
642 : //
643 : // Determines which screen intersects the largest area of the given surface.
644 : //
645 : void
646 24 : nsDeviceContext::FindScreen(nsIScreen** outScreen)
647 : {
648 24 : if (!mWidget || !mScreenManager) {
649 0 : return;
650 : }
651 :
652 24 : CheckDPIChange();
653 :
654 48 : nsCOMPtr<nsIScreen> screen = mWidget->GetWidgetScreen();
655 24 : screen.forget(outScreen);
656 :
657 24 : if (!(*outScreen)) {
658 0 : mScreenManager->GetPrimaryScreen(outScreen);
659 : }
660 : }
661 :
662 : bool
663 0 : nsDeviceContext::CalcPrintingSize()
664 : {
665 0 : gfxSize size(mPrintTarget->GetSize());
666 : // For printing, CSS inches and physical inches are identical
667 : // so it doesn't matter which we use here
668 0 : mWidth = NSToCoordRound(size.width * AppUnitsPerPhysicalInch()
669 : / POINTS_PER_INCH_FLOAT);
670 0 : mHeight = NSToCoordRound(size.height * AppUnitsPerPhysicalInch()
671 : / POINTS_PER_INCH_FLOAT);
672 :
673 0 : return (mWidth > 0 && mHeight > 0);
674 : }
675 :
676 53 : bool nsDeviceContext::CheckDPIChange(double* aScale)
677 : {
678 53 : int32_t oldDevPixels = mAppUnitsPerDevPixelAtUnitFullZoom;
679 53 : int32_t oldInches = mAppUnitsPerPhysicalInch;
680 :
681 53 : SetDPI(aScale);
682 :
683 105 : return oldDevPixels != mAppUnitsPerDevPixelAtUnitFullZoom ||
684 105 : oldInches != mAppUnitsPerPhysicalInch;
685 : }
686 :
687 : bool
688 28 : nsDeviceContext::SetFullZoom(float aScale)
689 : {
690 28 : if (aScale <= 0) {
691 0 : NS_NOTREACHED("Invalid full zoom value");
692 0 : return false;
693 : }
694 28 : int32_t oldAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
695 28 : mFullZoom = aScale;
696 28 : UpdateAppUnitsForFullZoom();
697 28 : return oldAppUnitsPerDevPixel != mAppUnitsPerDevPixel;
698 : }
699 :
700 : void
701 110 : nsDeviceContext::UpdateAppUnitsForFullZoom()
702 : {
703 110 : mAppUnitsPerDevPixel =
704 220 : std::max(1, NSToIntRound(float(mAppUnitsPerDevPixelAtUnitFullZoom) / mFullZoom));
705 : // adjust mFullZoom to reflect appunit rounding
706 110 : mFullZoom = float(mAppUnitsPerDevPixelAtUnitFullZoom) / mAppUnitsPerDevPixel;
707 110 : }
708 :
709 : DesktopToLayoutDeviceScale
710 10 : nsDeviceContext::GetDesktopToDeviceScale()
711 : {
712 20 : nsCOMPtr<nsIScreen> screen;
713 10 : FindScreen(getter_AddRefs(screen));
714 :
715 10 : if (screen) {
716 : double scale;
717 10 : screen->GetContentsScaleFactor(&scale);
718 10 : return DesktopToLayoutDeviceScale(scale);
719 : }
720 :
721 0 : return DesktopToLayoutDeviceScale(1.0);
722 : }
|