Back out a5a5d2c176f7 (bug 882865) because of Android test failures on a CLOSED TREE
[gecko.git] / gfx / src / nsDeviceContext.cpp
blobdc917ab76d840ebda148b20d3bc40f8c34882b6d
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 "nsCRT.h"
8 #include "nsFontMetrics.h"
9 #include "nsRenderingContext.h"
10 #include "nsIWidget.h"
12 #include "mozilla/Attributes.h"
13 #include "mozilla/Services.h"
14 #include "mozilla/Preferences.h"
15 #include "nsIServiceManager.h"
16 #include "nsILanguageAtomService.h"
17 #include "nsIObserver.h"
18 #include "nsIObserverService.h"
20 #include "gfxImageSurface.h"
21 #include <algorithm>
23 #if !XP_MACOSX
24 #include "gfxPDFSurface.h"
25 #endif
27 #ifdef MOZ_WIDGET_GTK
28 #include "gfxPSSurface.h"
29 #elif XP_WIN
30 #include "gfxWindowsSurface.h"
31 #elif defined(XP_OS2)
32 #include "gfxOS2Surface.h"
33 #elif XP_MACOSX
34 #include "gfxQuartzSurface.h"
35 #endif
37 using namespace mozilla;
38 using mozilla::services::GetObserverService;
40 class nsFontCache MOZ_FINAL : public nsIObserver
42 public:
43 nsFontCache() { MOZ_COUNT_CTOR(nsFontCache); }
44 ~nsFontCache() { MOZ_COUNT_DTOR(nsFontCache); }
46 NS_DECL_ISUPPORTS
47 NS_DECL_NSIOBSERVER
49 void Init(nsDeviceContext* aContext);
50 void Destroy();
52 nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
53 gfxUserFontSet* aUserFontSet,
54 nsFontMetrics*& aMetrics);
56 void FontMetricsDeleted(const nsFontMetrics* aFontMetrics);
57 void Compact();
58 void Flush();
60 protected:
61 nsDeviceContext* mContext; // owner
62 nsCOMPtr<nsIAtom> mLocaleLanguage;
63 nsTArray<nsFontMetrics*> mFontMetrics;
66 NS_IMPL_ISUPPORTS1(nsFontCache, nsIObserver)
68 // The Init and Destroy methods are necessary because it's not
69 // safe to call AddObserver from a constructor or RemoveObserver
70 // from a destructor. That should be fixed.
71 void
72 nsFontCache::Init(nsDeviceContext* aContext)
74 mContext = aContext;
75 // register as a memory-pressure observer to free font resources
76 // in low-memory situations.
77 nsCOMPtr<nsIObserverService> obs = GetObserverService();
78 if (obs)
79 obs->AddObserver(this, "memory-pressure", false);
81 nsCOMPtr<nsILanguageAtomService> langService;
82 langService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
83 if (langService) {
84 mLocaleLanguage = langService->GetLocaleLanguage();
86 if (!mLocaleLanguage) {
87 mLocaleLanguage = do_GetAtom("x-western");
91 void
92 nsFontCache::Destroy()
94 nsCOMPtr<nsIObserverService> obs = GetObserverService();
95 if (obs)
96 obs->RemoveObserver(this, "memory-pressure");
97 Flush();
100 NS_IMETHODIMP
101 nsFontCache::Observe(nsISupports*, const char* aTopic, const PRUnichar*)
103 if (!nsCRT::strcmp(aTopic, "memory-pressure"))
104 Compact();
105 return NS_OK;
108 nsresult
109 nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
110 gfxUserFontSet* aUserFontSet,
111 nsFontMetrics*& aMetrics)
113 if (!aLanguage)
114 aLanguage = mLocaleLanguage;
116 // First check our cache
117 // start from the end, which is where we put the most-recent-used element
119 nsFontMetrics* fm;
120 int32_t n = mFontMetrics.Length() - 1;
121 for (int32_t i = n; i >= 0; --i) {
122 fm = mFontMetrics[i];
123 if (fm->Font().Equals(aFont) && fm->GetUserFontSet() == aUserFontSet &&
124 fm->Language() == aLanguage) {
125 if (i != n) {
126 // promote it to the end of the cache
127 mFontMetrics.RemoveElementAt(i);
128 mFontMetrics.AppendElement(fm);
130 fm->GetThebesFontGroup()->UpdateFontList();
131 NS_ADDREF(aMetrics = fm);
132 return NS_OK;
136 // It's not in the cache. Get font metrics and then cache them.
138 fm = new nsFontMetrics();
139 NS_ADDREF(fm);
140 nsresult rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet);
141 if (NS_SUCCEEDED(rv)) {
142 // the mFontMetrics list has the "head" at the end, because append
143 // is cheaper than insert
144 mFontMetrics.AppendElement(fm);
145 aMetrics = fm;
146 NS_ADDREF(aMetrics);
147 return NS_OK;
149 fm->Destroy();
150 NS_RELEASE(fm);
152 // One reason why Init() fails is because the system is running out of
153 // resources. e.g., on Win95/98 only a very limited number of GDI
154 // objects are available. Compact the cache and try again.
156 Compact();
157 fm = new nsFontMetrics();
158 NS_ADDREF(fm);
159 rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet);
160 if (NS_SUCCEEDED(rv)) {
161 mFontMetrics.AppendElement(fm);
162 aMetrics = fm;
163 return NS_OK;
165 fm->Destroy();
166 NS_RELEASE(fm);
168 // could not setup a new one, send an old one (XXX search a "best
169 // match"?)
171 n = mFontMetrics.Length() - 1; // could have changed in Compact()
172 if (n >= 0) {
173 aMetrics = mFontMetrics[n];
174 NS_ADDREF(aMetrics);
175 return NS_OK;
178 NS_POSTCONDITION(NS_SUCCEEDED(rv),
179 "font metrics should not be null - bug 136248");
180 return rv;
183 void
184 nsFontCache::FontMetricsDeleted(const nsFontMetrics* aFontMetrics)
186 mFontMetrics.RemoveElement(aFontMetrics);
189 void
190 nsFontCache::Compact()
192 // Need to loop backward because the running element can be removed on
193 // the way
194 for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) {
195 nsFontMetrics* fm = mFontMetrics[i];
196 nsFontMetrics* oldfm = fm;
197 // Destroy() isn't here because we want our device context to be
198 // notified
199 NS_RELEASE(fm); // this will reset fm to nullptr
200 // if the font is really gone, it would have called back in
201 // FontMetricsDeleted() and would have removed itself
202 if (mFontMetrics.IndexOf(oldfm) != mFontMetrics.NoIndex) {
203 // nope, the font is still there, so let's hold onto it too
204 NS_ADDREF(oldfm);
209 void
210 nsFontCache::Flush()
212 for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) {
213 nsFontMetrics* fm = mFontMetrics[i];
214 // Destroy() will unhook our device context from the fm so that we
215 // won't waste time in triggering the notification of
216 // FontMetricsDeleted() in the subsequent release
217 fm->Destroy();
218 NS_RELEASE(fm);
220 mFontMetrics.Clear();
223 nsDeviceContext::nsDeviceContext()
224 : mWidth(0), mHeight(0), mDepth(0),
225 mAppUnitsPerDevPixel(-1), mAppUnitsPerDevNotScaledPixel(-1),
226 mAppUnitsPerPhysicalInch(-1),
227 mPixelScale(1.0f), mPrintingScale(1.0f),
228 mFontCache(nullptr)
230 MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread");
233 // Note: we use a bare pointer for mFontCache so that nsFontCache
234 // can be an incomplete type in nsDeviceContext.h.
235 // Therefore we have to do all the refcounting by hand.
236 nsDeviceContext::~nsDeviceContext()
238 if (mFontCache) {
239 mFontCache->Destroy();
240 NS_RELEASE(mFontCache);
244 nsresult
245 nsDeviceContext::GetMetricsFor(const nsFont& aFont,
246 nsIAtom* aLanguage,
247 gfxUserFontSet* aUserFontSet,
248 nsFontMetrics*& aMetrics)
250 if (!mFontCache) {
251 mFontCache = new nsFontCache();
252 NS_ADDREF(mFontCache);
253 mFontCache->Init(this);
256 return mFontCache->GetMetricsFor(aFont, aLanguage, aUserFontSet, aMetrics);
259 nsresult
260 nsDeviceContext::FlushFontCache(void)
262 if (mFontCache)
263 mFontCache->Flush();
264 return NS_OK;
267 nsresult
268 nsDeviceContext::FontMetricsDeleted(const nsFontMetrics* aFontMetrics)
270 if (mFontCache) {
271 mFontCache->FontMetricsDeleted(aFontMetrics);
273 return NS_OK;
276 bool
277 nsDeviceContext::IsPrinterSurface()
279 return(mPrintingSurface != NULL);
282 void
283 nsDeviceContext::SetDPI()
285 float dpi = -1.0f;
287 // PostScript, PDF and Mac (when printing) all use 72 dpi
288 // Use a printing DC to determine the other dpi values
289 if (mPrintingSurface) {
290 switch (mPrintingSurface->GetType()) {
291 case gfxASurface::SurfaceTypePDF:
292 case gfxASurface::SurfaceTypePS:
293 case gfxASurface::SurfaceTypeQuartz:
294 dpi = 72.0f;
295 break;
296 #ifdef XP_WIN
297 case gfxASurface::SurfaceTypeWin32:
298 case gfxASurface::SurfaceTypeWin32Printing: {
299 HDC dc = reinterpret_cast<gfxWindowsSurface*>(mPrintingSurface.get())->GetDC();
300 int32_t OSVal = GetDeviceCaps(dc, LOGPIXELSY);
301 dpi = 144.0f;
302 mPrintingScale = float(OSVal) / dpi;
303 break;
305 #endif
306 #ifdef XP_OS2
307 case gfxASurface::SurfaceTypeOS2: {
308 LONG lDPI;
309 HDC dc = GpiQueryDevice(reinterpret_cast<gfxOS2Surface*>(mPrintingSurface.get())->GetPS());
310 if (DevQueryCaps(dc, CAPS_VERTICAL_FONT_RES, 1, &lDPI))
311 dpi = lDPI;
312 break;
314 #endif
315 default:
316 NS_NOTREACHED("Unexpected printing surface type");
317 break;
320 mAppUnitsPerDevNotScaledPixel =
321 NS_lround((AppUnitsPerCSSPixel() * 96) / dpi);
322 } else {
323 // A value of -1 means use the maximum of 96 and the system DPI.
324 // A value of 0 means use the system DPI. A positive value is used as the DPI.
325 // This sets the physical size of a device pixel and thus controls the
326 // interpretation of physical units.
327 int32_t prefDPI = Preferences::GetInt("layout.css.dpi", -1);
329 if (prefDPI > 0) {
330 dpi = prefDPI;
331 } else if (mWidget) {
332 dpi = mWidget->GetDPI();
334 if (prefDPI < 0) {
335 dpi = std::max(96.0f, dpi);
337 } else {
338 dpi = 96.0f;
341 double devPixelsPerCSSPixel = mWidget ? mWidget->GetDefaultScale() : 1.0;
343 mAppUnitsPerDevNotScaledPixel =
344 std::max(1, NS_lround(AppUnitsPerCSSPixel() / devPixelsPerCSSPixel));
347 NS_ASSERTION(dpi != -1.0, "no dpi set");
349 mAppUnitsPerPhysicalInch = NS_lround(dpi * mAppUnitsPerDevNotScaledPixel);
350 UpdateScaledAppUnits();
353 nsresult
354 nsDeviceContext::Init(nsIWidget *aWidget)
356 if (mScreenManager && mWidget == aWidget)
357 return NS_OK;
359 mWidget = aWidget;
360 SetDPI();
362 if (mScreenManager)
363 return NS_OK;
365 mScreenManager = do_GetService("@mozilla.org/gfx/screenmanager;1");
367 return NS_OK;
370 nsresult
371 nsDeviceContext::CreateRenderingContext(nsRenderingContext *&aContext)
373 nsRefPtr<gfxASurface> printingSurface = mPrintingSurface;
374 #ifdef XP_MACOSX
375 // CreateRenderingContext() can be called (on reflow) after EndPage()
376 // but before BeginPage(). On OS X (and only there) mPrintingSurface
377 // will in this case be null, because OS X printing surfaces are
378 // per-page, and therefore only truly valid between calls to BeginPage()
379 // and EndPage(). But we can get away with fudging things here, if need
380 // be, by using a cached copy.
381 if (!printingSurface) {
382 printingSurface = mCachedPrintingSurface;
384 #endif
385 nsRefPtr<nsRenderingContext> pContext = new nsRenderingContext();
387 pContext->Init(this, printingSurface);
388 pContext->Scale(mPrintingScale, mPrintingScale);
389 aContext = pContext;
390 NS_ADDREF(aContext);
392 return NS_OK;
395 nsresult
396 nsDeviceContext::GetDepth(uint32_t& aDepth)
398 if (mDepth == 0) {
399 nsCOMPtr<nsIScreen> primaryScreen;
400 mScreenManager->GetPrimaryScreen(getter_AddRefs(primaryScreen));
401 primaryScreen->GetColorDepth(reinterpret_cast<int32_t *>(&mDepth));
404 aDepth = mDepth;
405 return NS_OK;
408 nsresult
409 nsDeviceContext::GetDeviceSurfaceDimensions(nscoord &aWidth, nscoord &aHeight)
411 if (mPrintingSurface) {
412 // we have a printer device
413 aWidth = mWidth;
414 aHeight = mHeight;
415 } else {
416 nsRect area;
417 ComputeFullAreaUsingScreen(&area);
418 aWidth = area.width;
419 aHeight = area.height;
422 return NS_OK;
425 nsresult
426 nsDeviceContext::GetRect(nsRect &aRect)
428 if (mPrintingSurface) {
429 // we have a printer device
430 aRect.x = 0;
431 aRect.y = 0;
432 aRect.width = mWidth;
433 aRect.height = mHeight;
434 } else
435 ComputeFullAreaUsingScreen ( &aRect );
437 return NS_OK;
440 nsresult
441 nsDeviceContext::GetClientRect(nsRect &aRect)
443 if (mPrintingSurface) {
444 // we have a printer device
445 aRect.x = 0;
446 aRect.y = 0;
447 aRect.width = mWidth;
448 aRect.height = mHeight;
450 else
451 ComputeClientRectUsingScreen(&aRect);
453 return NS_OK;
456 nsresult
457 nsDeviceContext::InitForPrinting(nsIDeviceContextSpec *aDevice)
459 NS_ENSURE_ARG_POINTER(aDevice);
461 mDeviceContextSpec = aDevice;
463 nsresult rv = aDevice->GetSurfaceForPrinter(getter_AddRefs(mPrintingSurface));
464 if (NS_FAILED(rv))
465 return NS_ERROR_FAILURE;
467 Init(nullptr);
469 CalcPrintingSize();
471 return NS_OK;
474 nsresult
475 nsDeviceContext::BeginDocument(PRUnichar* aTitle,
476 PRUnichar* aPrintToFileName,
477 int32_t aStartPage,
478 int32_t aEndPage)
480 static const PRUnichar kEmpty[] = { '\0' };
481 nsresult rv;
483 rv = mPrintingSurface->BeginPrinting(nsDependentString(aTitle ? aTitle : kEmpty),
484 nsDependentString(aPrintToFileName ? aPrintToFileName : kEmpty));
486 if (NS_SUCCEEDED(rv) && mDeviceContextSpec)
487 rv = mDeviceContextSpec->BeginDocument(aTitle, aPrintToFileName, aStartPage, aEndPage);
489 return rv;
493 nsresult
494 nsDeviceContext::EndDocument(void)
496 nsresult rv = NS_OK;
498 if (mPrintingSurface) {
499 rv = mPrintingSurface->EndPrinting();
500 if (NS_SUCCEEDED(rv))
501 mPrintingSurface->Finish();
504 if (mDeviceContextSpec)
505 mDeviceContextSpec->EndDocument();
507 return rv;
511 nsresult
512 nsDeviceContext::AbortDocument(void)
514 nsresult rv = mPrintingSurface->AbortPrinting();
516 if (mDeviceContextSpec)
517 mDeviceContextSpec->EndDocument();
519 return rv;
523 nsresult
524 nsDeviceContext::BeginPage(void)
526 nsresult rv = NS_OK;
528 if (mDeviceContextSpec)
529 rv = mDeviceContextSpec->BeginPage();
531 if (NS_FAILED(rv)) return rv;
533 #ifdef XP_MACOSX
534 // We need to get a new surface for each page on the Mac, as the
535 // CGContextRefs are only good for one page.
536 mDeviceContextSpec->GetSurfaceForPrinter(getter_AddRefs(mPrintingSurface));
537 #endif
539 rv = mPrintingSurface->BeginPage();
541 return rv;
544 nsresult
545 nsDeviceContext::EndPage(void)
547 nsresult rv = mPrintingSurface->EndPage();
549 #ifdef XP_MACOSX
550 // We need to release the CGContextRef in the surface here, plus it's
551 // not something you would want anyway, as these CGContextRefs are only
552 // good for one page. But we need to keep a cached reference to it, since
553 // CreateRenderingContext() may try to access it when mPrintingSurface
554 // would normally be null. See bug 665218. If we just stop nulling out
555 // mPrintingSurface here (and thereby make that our cached copy), we'll
556 // break all our null checks on mPrintingSurface. See bug 684622.
557 mCachedPrintingSurface = mPrintingSurface;
558 mPrintingSurface = nullptr;
559 #endif
561 if (mDeviceContextSpec)
562 mDeviceContextSpec->EndPage();
564 return rv;
567 void
568 nsDeviceContext::ComputeClientRectUsingScreen(nsRect* outRect)
570 // we always need to recompute the clientRect
571 // because the window may have moved onto a different screen. In the single
572 // monitor case, we only need to do the computation if we haven't done it
573 // once already, and remember that we have because we're assured it won't change.
574 nsCOMPtr<nsIScreen> screen;
575 FindScreen (getter_AddRefs(screen));
576 if (screen) {
577 int32_t x, y, width, height;
578 screen->GetAvailRect(&x, &y, &width, &height);
580 // convert to device units
581 outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel());
582 outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel());
583 outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel());
584 outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel());
588 void
589 nsDeviceContext::ComputeFullAreaUsingScreen(nsRect* outRect)
591 // if we have more than one screen, we always need to recompute the clientRect
592 // because the window may have moved onto a different screen. In the single
593 // monitor case, we only need to do the computation if we haven't done it
594 // once already, and remember that we have because we're assured it won't change.
595 nsCOMPtr<nsIScreen> screen;
596 FindScreen ( getter_AddRefs(screen) );
597 if ( screen ) {
598 int32_t x, y, width, height;
599 screen->GetRect ( &x, &y, &width, &height );
601 // convert to device units
602 outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel());
603 outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel());
604 outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel());
605 outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel());
607 mWidth = outRect->width;
608 mHeight = outRect->height;
613 // FindScreen
615 // Determines which screen intersects the largest area of the given surface.
617 void
618 nsDeviceContext::FindScreen(nsIScreen** outScreen)
620 if (mWidget && mWidget->GetNativeData(NS_NATIVE_WINDOW))
621 mScreenManager->ScreenForNativeWidget(mWidget->GetNativeData(NS_NATIVE_WINDOW),
622 outScreen);
623 else
624 mScreenManager->GetPrimaryScreen(outScreen);
627 void
628 nsDeviceContext::CalcPrintingSize()
630 if (!mPrintingSurface)
631 return;
633 bool inPoints = true;
635 gfxSize size(0, 0);
636 switch (mPrintingSurface->GetType()) {
637 case gfxASurface::SurfaceTypeImage:
638 inPoints = false;
639 size = reinterpret_cast<gfxImageSurface*>(mPrintingSurface.get())->GetSize();
640 break;
642 #if defined(MOZ_PDF_PRINTING)
643 case gfxASurface::SurfaceTypePDF:
644 inPoints = true;
645 size = reinterpret_cast<gfxPDFSurface*>(mPrintingSurface.get())->GetSize();
646 break;
647 #endif
649 #ifdef MOZ_WIDGET_GTK
650 case gfxASurface::SurfaceTypePS:
651 inPoints = true;
652 size = reinterpret_cast<gfxPSSurface*>(mPrintingSurface.get())->GetSize();
653 break;
654 #endif
656 #ifdef XP_MACOSX
657 case gfxASurface::SurfaceTypeQuartz:
658 inPoints = true; // this is really only true when we're printing
659 size = reinterpret_cast<gfxQuartzSurface*>(mPrintingSurface.get())->GetSize();
660 break;
661 #endif
663 #ifdef XP_WIN
664 case gfxASurface::SurfaceTypeWin32:
665 case gfxASurface::SurfaceTypeWin32Printing:
667 inPoints = false;
668 HDC dc = reinterpret_cast<gfxWindowsSurface*>(mPrintingSurface.get())->GetDC();
669 if (!dc)
670 dc = GetDC((HWND)mWidget->GetNativeData(NS_NATIVE_WIDGET));
671 size.width = NSFloatPixelsToAppUnits(::GetDeviceCaps(dc, HORZRES)/mPrintingScale, AppUnitsPerDevPixel());
672 size.height = NSFloatPixelsToAppUnits(::GetDeviceCaps(dc, VERTRES)/mPrintingScale, AppUnitsPerDevPixel());
673 mDepth = (uint32_t)::GetDeviceCaps(dc, BITSPIXEL);
674 if (dc != reinterpret_cast<gfxWindowsSurface*>(mPrintingSurface.get())->GetDC())
675 ReleaseDC((HWND)mWidget->GetNativeData(NS_NATIVE_WIDGET), dc);
676 break;
678 #endif
680 #ifdef XP_OS2
681 case gfxASurface::SurfaceTypeOS2:
683 inPoints = false;
684 // we already set the size in the surface constructor we set for
685 // printing, so just get those values here
686 size = reinterpret_cast<gfxOS2Surface*>(mPrintingSurface.get())->GetSize();
687 // as they are in pixels we need to scale them to app units
688 size.width = NSFloatPixelsToAppUnits(size.width, AppUnitsPerDevPixel());
689 size.height = NSFloatPixelsToAppUnits(size.height, AppUnitsPerDevPixel());
690 // still need to get the depth from the device context
691 HDC dc = GpiQueryDevice(reinterpret_cast<gfxOS2Surface*>(mPrintingSurface.get())->GetPS());
692 LONG value;
693 if (DevQueryCaps(dc, CAPS_COLOR_BITCOUNT, 1, &value))
694 mDepth = value;
695 else
696 mDepth = 8; // default to 8bpp, should be enough for printers
697 break;
699 #endif
700 default:
701 NS_ERROR("trying to print to unknown surface type");
704 if (inPoints) {
705 // For printing, CSS inches and physical inches are identical
706 // so it doesn't matter which we use here
707 mWidth = NSToCoordRound(float(size.width) * AppUnitsPerPhysicalInch() / 72);
708 mHeight = NSToCoordRound(float(size.height) * AppUnitsPerPhysicalInch() / 72);
709 } else {
710 mWidth = NSToIntRound(size.width);
711 mHeight = NSToIntRound(size.height);
715 bool nsDeviceContext::CheckDPIChange() {
716 int32_t oldDevPixels = mAppUnitsPerDevNotScaledPixel;
717 int32_t oldInches = mAppUnitsPerPhysicalInch;
719 SetDPI();
721 return oldDevPixels != mAppUnitsPerDevNotScaledPixel ||
722 oldInches != mAppUnitsPerPhysicalInch;
725 bool
726 nsDeviceContext::SetPixelScale(float aScale)
728 if (aScale <= 0) {
729 NS_NOTREACHED("Invalid pixel scale value");
730 return false;
732 int32_t oldAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
733 mPixelScale = aScale;
734 UpdateScaledAppUnits();
735 return oldAppUnitsPerDevPixel != mAppUnitsPerDevPixel;
738 void
739 nsDeviceContext::UpdateScaledAppUnits()
741 mAppUnitsPerDevPixel =
742 std::max(1, NSToIntRound(float(mAppUnitsPerDevNotScaledPixel) / mPixelScale));
743 // adjust mPixelScale to reflect appunit rounding
744 mPixelScale = float(mAppUnitsPerDevNotScaledPixel) / mAppUnitsPerDevPixel;