Bumping gaia.json for 1 gaia revision(s) a=gaia-bump
[gecko.git] / gfx / src / nsDeviceContext.cpp
blobeffca7cad9daacfc0b3af6c322eb59507b2040b6
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/. */
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
38 #if !XP_MACOSX
39 #include "gfxPDFSurface.h"
40 #endif
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
50 using namespace mozilla;
51 using mozilla::services::GetObserverService;
53 class nsFontCache MOZ_FINAL : public nsIObserver
55 public:
56 nsFontCache() { MOZ_COUNT_CTOR(nsFontCache); }
58 NS_DECL_ISUPPORTS
59 NS_DECL_NSIOBSERVER
61 void Init(nsDeviceContext* aContext);
62 void Destroy();
64 nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
65 gfxUserFontSet* aUserFontSet,
66 gfxTextPerfMetrics* aTextPerf,
67 nsFontMetrics*& aMetrics);
69 void FontMetricsDeleted(const nsFontMetrics* aFontMetrics);
70 void Compact();
71 void Flush();
73 protected:
74 ~nsFontCache() { MOZ_COUNT_DTOR(nsFontCache); }
76 nsDeviceContext* mContext; // owner
77 nsCOMPtr<nsIAtom> mLocaleLanguage;
78 nsTArray<nsFontMetrics*> mFontMetrics;
81 NS_IMPL_ISUPPORTS(nsFontCache, nsIObserver)
83 // The Init and Destroy methods are necessary because it's not
84 // safe to call AddObserver from a constructor or RemoveObserver
85 // from a destructor. That should be fixed.
86 void
87 nsFontCache::Init(nsDeviceContext* aContext)
89 mContext = aContext;
90 // register as a memory-pressure observer to free font resources
91 // in low-memory situations.
92 nsCOMPtr<nsIObserverService> obs = GetObserverService();
93 if (obs)
94 obs->AddObserver(this, "memory-pressure", false);
96 nsCOMPtr<nsILanguageAtomService> langService;
97 langService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
98 if (langService) {
99 mLocaleLanguage = langService->GetLocaleLanguage();
101 if (!mLocaleLanguage) {
102 mLocaleLanguage = do_GetAtom("x-western");
106 void
107 nsFontCache::Destroy()
109 nsCOMPtr<nsIObserverService> obs = GetObserverService();
110 if (obs)
111 obs->RemoveObserver(this, "memory-pressure");
112 Flush();
115 NS_IMETHODIMP
116 nsFontCache::Observe(nsISupports*, const char* aTopic, const char16_t*)
118 if (!nsCRT::strcmp(aTopic, "memory-pressure"))
119 Compact();
120 return NS_OK;
123 nsresult
124 nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
125 gfxUserFontSet* aUserFontSet,
126 gfxTextPerfMetrics* aTextPerf,
127 nsFontMetrics*& aMetrics)
129 if (!aLanguage)
130 aLanguage = mLocaleLanguage;
132 // First check our cache
133 // start from the end, which is where we put the most-recent-used element
135 nsFontMetrics* fm;
136 int32_t n = mFontMetrics.Length() - 1;
137 for (int32_t i = n; i >= 0; --i) {
138 fm = mFontMetrics[i];
139 if (fm->Font().Equals(aFont) && fm->GetUserFontSet() == aUserFontSet &&
140 fm->Language() == aLanguage) {
141 if (i != n) {
142 // promote it to the end of the cache
143 mFontMetrics.RemoveElementAt(i);
144 mFontMetrics.AppendElement(fm);
146 fm->GetThebesFontGroup()->UpdateFontList();
147 NS_ADDREF(aMetrics = fm);
148 return NS_OK;
152 // It's not in the cache. Get font metrics and then cache them.
154 fm = new nsFontMetrics();
155 NS_ADDREF(fm);
156 nsresult rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf);
157 if (NS_SUCCEEDED(rv)) {
158 // the mFontMetrics list has the "head" at the end, because append
159 // is cheaper than insert
160 mFontMetrics.AppendElement(fm);
161 aMetrics = fm;
162 NS_ADDREF(aMetrics);
163 return NS_OK;
165 fm->Destroy();
166 NS_RELEASE(fm);
168 // One reason why Init() fails is because the system is running out of
169 // resources. e.g., on Win95/98 only a very limited number of GDI
170 // objects are available. Compact the cache and try again.
172 Compact();
173 fm = new nsFontMetrics();
174 NS_ADDREF(fm);
175 rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf);
176 if (NS_SUCCEEDED(rv)) {
177 mFontMetrics.AppendElement(fm);
178 aMetrics = fm;
179 return NS_OK;
181 fm->Destroy();
182 NS_RELEASE(fm);
184 // could not setup a new one, send an old one (XXX search a "best
185 // match"?)
187 n = mFontMetrics.Length() - 1; // could have changed in Compact()
188 if (n >= 0) {
189 aMetrics = mFontMetrics[n];
190 NS_ADDREF(aMetrics);
191 return NS_OK;
194 NS_POSTCONDITION(NS_SUCCEEDED(rv),
195 "font metrics should not be null - bug 136248");
196 return rv;
199 void
200 nsFontCache::FontMetricsDeleted(const nsFontMetrics* aFontMetrics)
202 mFontMetrics.RemoveElement(aFontMetrics);
205 void
206 nsFontCache::Compact()
208 // Need to loop backward because the running element can be removed on
209 // the way
210 for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) {
211 nsFontMetrics* fm = mFontMetrics[i];
212 nsFontMetrics* oldfm = fm;
213 // Destroy() isn't here because we want our device context to be
214 // notified
215 NS_RELEASE(fm); // this will reset fm to nullptr
216 // if the font is really gone, it would have called back in
217 // FontMetricsDeleted() and would have removed itself
218 if (mFontMetrics.IndexOf(oldfm) != mFontMetrics.NoIndex) {
219 // nope, the font is still there, so let's hold onto it too
220 NS_ADDREF(oldfm);
225 void
226 nsFontCache::Flush()
228 for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) {
229 nsFontMetrics* fm = mFontMetrics[i];
230 // Destroy() will unhook our device context from the fm so that we
231 // won't waste time in triggering the notification of
232 // FontMetricsDeleted() in the subsequent release
233 fm->Destroy();
234 NS_RELEASE(fm);
236 mFontMetrics.Clear();
239 nsDeviceContext::nsDeviceContext()
240 : mWidth(0), mHeight(0), mDepth(0),
241 mAppUnitsPerDevPixel(-1), mAppUnitsPerDevNotScaledPixel(-1),
242 mAppUnitsPerPhysicalInch(-1),
243 mPixelScale(1.0f), mPrintingScale(1.0f),
244 mFontCache(nullptr)
246 MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread");
249 // Note: we use a bare pointer for mFontCache so that nsFontCache
250 // can be an incomplete type in nsDeviceContext.h.
251 // Therefore we have to do all the refcounting by hand.
252 nsDeviceContext::~nsDeviceContext()
254 if (mFontCache) {
255 mFontCache->Destroy();
256 NS_RELEASE(mFontCache);
260 nsresult
261 nsDeviceContext::GetMetricsFor(const nsFont& aFont,
262 nsIAtom* aLanguage,
263 gfxUserFontSet* aUserFontSet,
264 gfxTextPerfMetrics* aTextPerf,
265 nsFontMetrics*& aMetrics)
267 if (!mFontCache) {
268 mFontCache = new nsFontCache();
269 NS_ADDREF(mFontCache);
270 mFontCache->Init(this);
273 return mFontCache->GetMetricsFor(aFont, aLanguage, aUserFontSet,
274 aTextPerf, aMetrics);
277 nsresult
278 nsDeviceContext::FlushFontCache(void)
280 if (mFontCache)
281 mFontCache->Flush();
282 return NS_OK;
285 nsresult
286 nsDeviceContext::FontMetricsDeleted(const nsFontMetrics* aFontMetrics)
288 if (mFontCache) {
289 mFontCache->FontMetricsDeleted(aFontMetrics);
291 return NS_OK;
294 bool
295 nsDeviceContext::IsPrinterSurface()
297 return mPrintingSurface != nullptr;
300 void
301 nsDeviceContext::SetDPI()
303 float dpi = -1.0f;
305 // PostScript, PDF and Mac (when printing) all use 72 dpi
306 // Use a printing DC to determine the other dpi values
307 if (mPrintingSurface) {
308 switch (mPrintingSurface->GetType()) {
309 case gfxSurfaceType::PDF:
310 case gfxSurfaceType::PS:
311 case gfxSurfaceType::Quartz:
312 dpi = 72.0f;
313 break;
314 #ifdef XP_WIN
315 case gfxSurfaceType::Win32:
316 case gfxSurfaceType::Win32Printing: {
317 HDC dc = reinterpret_cast<gfxWindowsSurface*>(mPrintingSurface.get())->GetDC();
318 int32_t OSVal = GetDeviceCaps(dc, LOGPIXELSY);
319 dpi = 144.0f;
320 mPrintingScale = float(OSVal) / dpi;
321 break;
323 #endif
324 default:
325 NS_NOTREACHED("Unexpected printing surface type");
326 break;
329 mAppUnitsPerDevNotScaledPixel =
330 NS_lround((AppUnitsPerCSSPixel() * 96) / dpi);
331 } else {
332 // A value of -1 means use the maximum of 96 and the system DPI.
333 // A value of 0 means use the system DPI. A positive value is used as the DPI.
334 // This sets the physical size of a device pixel and thus controls the
335 // interpretation of physical units.
336 int32_t prefDPI = Preferences::GetInt("layout.css.dpi", -1);
338 if (prefDPI > 0) {
339 dpi = prefDPI;
340 } else if (mWidget) {
341 dpi = mWidget->GetDPI();
343 if (prefDPI < 0) {
344 dpi = std::max(96.0f, dpi);
346 } else {
347 dpi = 96.0f;
350 CSSToLayoutDeviceScale scale = mWidget ? mWidget->GetDefaultScale()
351 : CSSToLayoutDeviceScale(1.0);
352 double devPixelsPerCSSPixel = scale.scale;
354 mAppUnitsPerDevNotScaledPixel =
355 std::max(1, NS_lround(AppUnitsPerCSSPixel() / devPixelsPerCSSPixel));
358 NS_ASSERTION(dpi != -1.0, "no dpi set");
360 mAppUnitsPerPhysicalInch = NS_lround(dpi * mAppUnitsPerDevNotScaledPixel);
361 UpdateScaledAppUnits();
364 nsresult
365 nsDeviceContext::Init(nsIWidget *aWidget)
367 if (mScreenManager && mWidget == aWidget)
368 return NS_OK;
370 mWidget = aWidget;
371 SetDPI();
373 if (mScreenManager)
374 return NS_OK;
376 mScreenManager = do_GetService("@mozilla.org/gfx/screenmanager;1");
378 return NS_OK;
381 already_AddRefed<nsRenderingContext>
382 nsDeviceContext::CreateRenderingContext()
384 nsRefPtr<gfxASurface> printingSurface = mPrintingSurface;
385 #ifdef XP_MACOSX
386 // CreateRenderingContext() can be called (on reflow) after EndPage()
387 // but before BeginPage(). On OS X (and only there) mPrintingSurface
388 // will in this case be null, because OS X printing surfaces are
389 // per-page, and therefore only truly valid between calls to BeginPage()
390 // and EndPage(). But we can get away with fudging things here, if need
391 // be, by using a cached copy.
392 if (!printingSurface) {
393 printingSurface = mCachedPrintingSurface;
395 #endif
396 nsRefPtr<nsRenderingContext> pContext = new nsRenderingContext();
398 RefPtr<gfx::DrawTarget> dt =
399 gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(printingSurface,
400 gfx::IntSize(mWidth, mHeight));
402 #ifdef XP_MACOSX
403 dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr);
404 #endif
406 pContext->Init(this, dt);
407 pContext->ThebesContext()->SetFlag(gfxContext::FLAG_DISABLE_SNAPPING);
408 pContext->Scale(mPrintingScale, mPrintingScale);
410 return pContext.forget();
413 nsresult
414 nsDeviceContext::GetDepth(uint32_t& aDepth)
416 if (mDepth == 0) {
417 nsCOMPtr<nsIScreen> primaryScreen;
418 mScreenManager->GetPrimaryScreen(getter_AddRefs(primaryScreen));
419 primaryScreen->GetColorDepth(reinterpret_cast<int32_t *>(&mDepth));
422 aDepth = mDepth;
423 return NS_OK;
426 nsresult
427 nsDeviceContext::GetDeviceSurfaceDimensions(nscoord &aWidth, nscoord &aHeight)
429 if (mPrintingSurface) {
430 // we have a printer device
431 aWidth = mWidth;
432 aHeight = mHeight;
433 } else {
434 nsRect area;
435 ComputeFullAreaUsingScreen(&area);
436 aWidth = area.width;
437 aHeight = area.height;
440 return NS_OK;
443 nsresult
444 nsDeviceContext::GetRect(nsRect &aRect)
446 if (mPrintingSurface) {
447 // we have a printer device
448 aRect.x = 0;
449 aRect.y = 0;
450 aRect.width = mWidth;
451 aRect.height = mHeight;
452 } else
453 ComputeFullAreaUsingScreen ( &aRect );
455 return NS_OK;
458 nsresult
459 nsDeviceContext::GetClientRect(nsRect &aRect)
461 if (mPrintingSurface) {
462 // we have a printer device
463 aRect.x = 0;
464 aRect.y = 0;
465 aRect.width = mWidth;
466 aRect.height = mHeight;
468 else
469 ComputeClientRectUsingScreen(&aRect);
471 return NS_OK;
474 nsresult
475 nsDeviceContext::InitForPrinting(nsIDeviceContextSpec *aDevice)
477 NS_ENSURE_ARG_POINTER(aDevice);
479 mDeviceContextSpec = aDevice;
481 nsresult rv = aDevice->GetSurfaceForPrinter(getter_AddRefs(mPrintingSurface));
482 if (NS_FAILED(rv))
483 return NS_ERROR_FAILURE;
485 Init(nullptr);
487 CalcPrintingSize();
489 return NS_OK;
492 nsresult
493 nsDeviceContext::BeginDocument(const nsAString& aTitle,
494 char16_t* aPrintToFileName,
495 int32_t aStartPage,
496 int32_t aEndPage)
498 static const char16_t kEmpty[] = { '\0' };
499 nsresult rv;
501 rv = mPrintingSurface->BeginPrinting(aTitle,
502 nsDependentString(aPrintToFileName ? aPrintToFileName : kEmpty));
504 if (NS_SUCCEEDED(rv) && mDeviceContextSpec)
505 rv = mDeviceContextSpec->BeginDocument(aTitle, aPrintToFileName, aStartPage, aEndPage);
507 return rv;
511 nsresult
512 nsDeviceContext::EndDocument(void)
514 nsresult rv = NS_OK;
516 if (mPrintingSurface) {
517 rv = mPrintingSurface->EndPrinting();
518 if (NS_SUCCEEDED(rv))
519 mPrintingSurface->Finish();
522 if (mDeviceContextSpec)
523 mDeviceContextSpec->EndDocument();
525 return rv;
529 nsresult
530 nsDeviceContext::AbortDocument(void)
532 nsresult rv = mPrintingSurface->AbortPrinting();
534 if (mDeviceContextSpec)
535 mDeviceContextSpec->EndDocument();
537 return rv;
541 nsresult
542 nsDeviceContext::BeginPage(void)
544 nsresult rv = NS_OK;
546 if (mDeviceContextSpec)
547 rv = mDeviceContextSpec->BeginPage();
549 if (NS_FAILED(rv)) return rv;
551 #ifdef XP_MACOSX
552 // We need to get a new surface for each page on the Mac, as the
553 // CGContextRefs are only good for one page.
554 mDeviceContextSpec->GetSurfaceForPrinter(getter_AddRefs(mPrintingSurface));
555 #endif
557 rv = mPrintingSurface->BeginPage();
559 return rv;
562 nsresult
563 nsDeviceContext::EndPage(void)
565 nsresult rv = mPrintingSurface->EndPage();
567 #ifdef XP_MACOSX
568 // We need to release the CGContextRef in the surface here, plus it's
569 // not something you would want anyway, as these CGContextRefs are only
570 // good for one page. But we need to keep a cached reference to it, since
571 // CreateRenderingContext() may try to access it when mPrintingSurface
572 // would normally be null. See bug 665218. If we just stop nulling out
573 // mPrintingSurface here (and thereby make that our cached copy), we'll
574 // break all our null checks on mPrintingSurface. See bug 684622.
575 mCachedPrintingSurface = mPrintingSurface;
576 mPrintingSurface = nullptr;
577 #endif
579 if (mDeviceContextSpec)
580 mDeviceContextSpec->EndPage();
582 return rv;
585 void
586 nsDeviceContext::ComputeClientRectUsingScreen(nsRect* outRect)
588 // we always need to recompute the clientRect
589 // because the window may have moved onto a different screen. In the single
590 // monitor case, we only need to do the computation if we haven't done it
591 // once already, and remember that we have because we're assured it won't change.
592 nsCOMPtr<nsIScreen> screen;
593 FindScreen (getter_AddRefs(screen));
594 if (screen) {
595 int32_t x, y, width, height;
596 screen->GetAvailRect(&x, &y, &width, &height);
598 // convert to device units
599 outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel());
600 outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel());
601 outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel());
602 outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel());
606 void
607 nsDeviceContext::ComputeFullAreaUsingScreen(nsRect* outRect)
609 // if we have more than one screen, we always need to recompute the clientRect
610 // because the window may have moved onto a different screen. In the single
611 // monitor case, we only need to do the computation if we haven't done it
612 // once already, and remember that we have because we're assured it won't change.
613 nsCOMPtr<nsIScreen> screen;
614 FindScreen ( getter_AddRefs(screen) );
615 if ( screen ) {
616 int32_t x, y, width, height;
617 screen->GetRect ( &x, &y, &width, &height );
619 // convert to device units
620 outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel());
621 outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel());
622 outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel());
623 outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel());
625 mWidth = outRect->width;
626 mHeight = outRect->height;
631 // FindScreen
633 // Determines which screen intersects the largest area of the given surface.
635 void
636 nsDeviceContext::FindScreen(nsIScreen** outScreen)
638 if (mWidget && mWidget->GetOwningTabChild()) {
639 mScreenManager->ScreenForNativeWidget((void *)mWidget->GetOwningTabChild(),
640 outScreen);
642 else if (mWidget && mWidget->GetNativeData(NS_NATIVE_WINDOW)) {
643 mScreenManager->ScreenForNativeWidget(mWidget->GetNativeData(NS_NATIVE_WINDOW),
644 outScreen);
646 else {
647 mScreenManager->GetPrimaryScreen(outScreen);
651 void
652 nsDeviceContext::CalcPrintingSize()
654 if (!mPrintingSurface)
655 return;
657 bool inPoints = true;
659 gfxSize size(0, 0);
660 switch (mPrintingSurface->GetType()) {
661 case gfxSurfaceType::Image:
662 inPoints = false;
663 size = reinterpret_cast<gfxImageSurface*>(mPrintingSurface.get())->GetSize();
664 break;
666 #if defined(MOZ_PDF_PRINTING)
667 case gfxSurfaceType::PDF:
668 inPoints = true;
669 size = reinterpret_cast<gfxPDFSurface*>(mPrintingSurface.get())->GetSize();
670 break;
671 #endif
673 #ifdef MOZ_WIDGET_GTK
674 case gfxSurfaceType::PS:
675 inPoints = true;
676 size = reinterpret_cast<gfxPSSurface*>(mPrintingSurface.get())->GetSize();
677 break;
678 #endif
680 #ifdef XP_MACOSX
681 case gfxSurfaceType::Quartz:
682 inPoints = true; // this is really only true when we're printing
683 size = reinterpret_cast<gfxQuartzSurface*>(mPrintingSurface.get())->GetSize();
684 break;
685 #endif
687 #ifdef XP_WIN
688 case gfxSurfaceType::Win32:
689 case gfxSurfaceType::Win32Printing:
691 inPoints = false;
692 HDC dc = reinterpret_cast<gfxWindowsSurface*>(mPrintingSurface.get())->GetDC();
693 if (!dc)
694 dc = GetDC((HWND)mWidget->GetNativeData(NS_NATIVE_WIDGET));
695 size.width = NSFloatPixelsToAppUnits(::GetDeviceCaps(dc, HORZRES)/mPrintingScale, AppUnitsPerDevPixel());
696 size.height = NSFloatPixelsToAppUnits(::GetDeviceCaps(dc, VERTRES)/mPrintingScale, AppUnitsPerDevPixel());
697 mDepth = (uint32_t)::GetDeviceCaps(dc, BITSPIXEL);
698 if (dc != reinterpret_cast<gfxWindowsSurface*>(mPrintingSurface.get())->GetDC())
699 ReleaseDC((HWND)mWidget->GetNativeData(NS_NATIVE_WIDGET), dc);
700 break;
702 #endif
704 default:
705 NS_ERROR("trying to print to unknown surface type");
708 if (inPoints) {
709 // For printing, CSS inches and physical inches are identical
710 // so it doesn't matter which we use here
711 mWidth = NSToCoordRound(float(size.width) * AppUnitsPerPhysicalInch() / 72);
712 mHeight = NSToCoordRound(float(size.height) * AppUnitsPerPhysicalInch() / 72);
713 } else {
714 mWidth = NSToIntRound(size.width);
715 mHeight = NSToIntRound(size.height);
719 bool nsDeviceContext::CheckDPIChange() {
720 int32_t oldDevPixels = mAppUnitsPerDevNotScaledPixel;
721 int32_t oldInches = mAppUnitsPerPhysicalInch;
723 SetDPI();
725 return oldDevPixels != mAppUnitsPerDevNotScaledPixel ||
726 oldInches != mAppUnitsPerPhysicalInch;
729 bool
730 nsDeviceContext::SetPixelScale(float aScale)
732 if (aScale <= 0) {
733 NS_NOTREACHED("Invalid pixel scale value");
734 return false;
736 int32_t oldAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
737 mPixelScale = aScale;
738 UpdateScaledAppUnits();
739 return oldAppUnitsPerDevPixel != mAppUnitsPerDevPixel;
742 void
743 nsDeviceContext::UpdateScaledAppUnits()
745 mAppUnitsPerDevPixel =
746 std::max(1, NSToIntRound(float(mAppUnitsPerDevNotScaledPixel) / mPixelScale));
747 // adjust mPixelScale to reflect appunit rounding
748 mPixelScale = float(mAppUnitsPerDevNotScaledPixel) / mAppUnitsPerDevPixel;