michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsDeviceContext.h" michael@0: #include // for max michael@0: #include "gfxASurface.h" // for gfxASurface, etc michael@0: #include "gfxFont.h" // for gfxFontGroup michael@0: #include "gfxImageSurface.h" // for gfxImageSurface michael@0: #include "gfxPoint.h" // for gfxSize michael@0: #include "mozilla/Attributes.h" // for MOZ_FINAL michael@0: #include "mozilla/Preferences.h" // for Preferences michael@0: #include "mozilla/Services.h" // for GetObserverService michael@0: #include "mozilla/mozalloc.h" // for operator new michael@0: #include "nsCRT.h" // for nsCRT michael@0: #include "nsDebug.h" // for NS_NOTREACHED, NS_ASSERTION, etc michael@0: #include "nsFont.h" // for nsFont michael@0: #include "nsFontMetrics.h" // for nsFontMetrics michael@0: #include "nsIAtom.h" // for nsIAtom, do_GetAtom michael@0: #include "nsID.h" michael@0: #include "nsIDeviceContextSpec.h" // for nsIDeviceContextSpec michael@0: #include "nsILanguageAtomService.h" // for nsILanguageAtomService, etc michael@0: #include "nsIObserver.h" // for nsIObserver, etc michael@0: #include "nsIObserverService.h" // for nsIObserverService michael@0: #include "nsIScreen.h" // for nsIScreen michael@0: #include "nsIScreenManager.h" // for nsIScreenManager michael@0: #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc michael@0: #include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE michael@0: #include "nsIWidget.h" // for nsIWidget, NS_NATIVE_WINDOW michael@0: #include "nsRect.h" // for nsRect michael@0: #include "nsRenderingContext.h" // for nsRenderingContext michael@0: #include "nsServiceManagerUtils.h" // for do_GetService michael@0: #include "nsString.h" // for nsDependentString michael@0: #include "nsTArray.h" // for nsTArray, nsTArray_Impl michael@0: #include "nsThreadUtils.h" // for NS_IsMainThread michael@0: michael@0: #if !XP_MACOSX michael@0: #include "gfxPDFSurface.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: #include "gfxPSSurface.h" michael@0: #elif XP_WIN michael@0: #include "gfxWindowsSurface.h" michael@0: #elif XP_MACOSX michael@0: #include "gfxQuartzSurface.h" michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: using mozilla::services::GetObserverService; michael@0: michael@0: class nsFontCache MOZ_FINAL : public nsIObserver michael@0: { michael@0: public: michael@0: nsFontCache() { MOZ_COUNT_CTOR(nsFontCache); } michael@0: ~nsFontCache() { MOZ_COUNT_DTOR(nsFontCache); } michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: void Init(nsDeviceContext* aContext); michael@0: void Destroy(); michael@0: michael@0: nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, michael@0: gfxUserFontSet* aUserFontSet, michael@0: gfxTextPerfMetrics* aTextPerf, michael@0: nsFontMetrics*& aMetrics); michael@0: michael@0: void FontMetricsDeleted(const nsFontMetrics* aFontMetrics); michael@0: void Compact(); michael@0: void Flush(); michael@0: michael@0: protected: michael@0: nsDeviceContext* mContext; // owner michael@0: nsCOMPtr mLocaleLanguage; michael@0: nsTArray mFontMetrics; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsFontCache, nsIObserver) michael@0: michael@0: // The Init and Destroy methods are necessary because it's not michael@0: // safe to call AddObserver from a constructor or RemoveObserver michael@0: // from a destructor. That should be fixed. michael@0: void michael@0: nsFontCache::Init(nsDeviceContext* aContext) michael@0: { michael@0: mContext = aContext; michael@0: // register as a memory-pressure observer to free font resources michael@0: // in low-memory situations. michael@0: nsCOMPtr obs = GetObserverService(); michael@0: if (obs) michael@0: obs->AddObserver(this, "memory-pressure", false); michael@0: michael@0: nsCOMPtr langService; michael@0: langService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID); michael@0: if (langService) { michael@0: mLocaleLanguage = langService->GetLocaleLanguage(); michael@0: } michael@0: if (!mLocaleLanguage) { michael@0: mLocaleLanguage = do_GetAtom("x-western"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFontCache::Destroy() michael@0: { michael@0: nsCOMPtr obs = GetObserverService(); michael@0: if (obs) michael@0: obs->RemoveObserver(this, "memory-pressure"); michael@0: Flush(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFontCache::Observe(nsISupports*, const char* aTopic, const char16_t*) michael@0: { michael@0: if (!nsCRT::strcmp(aTopic, "memory-pressure")) michael@0: Compact(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, michael@0: gfxUserFontSet* aUserFontSet, michael@0: gfxTextPerfMetrics* aTextPerf, michael@0: nsFontMetrics*& aMetrics) michael@0: { michael@0: if (!aLanguage) michael@0: aLanguage = mLocaleLanguage; michael@0: michael@0: // First check our cache michael@0: // start from the end, which is where we put the most-recent-used element michael@0: michael@0: nsFontMetrics* fm; michael@0: int32_t n = mFontMetrics.Length() - 1; michael@0: for (int32_t i = n; i >= 0; --i) { michael@0: fm = mFontMetrics[i]; michael@0: if (fm->Font().Equals(aFont) && fm->GetUserFontSet() == aUserFontSet && michael@0: fm->Language() == aLanguage) { michael@0: if (i != n) { michael@0: // promote it to the end of the cache michael@0: mFontMetrics.RemoveElementAt(i); michael@0: mFontMetrics.AppendElement(fm); michael@0: } michael@0: fm->GetThebesFontGroup()->UpdateFontList(); michael@0: NS_ADDREF(aMetrics = fm); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // It's not in the cache. Get font metrics and then cache them. michael@0: michael@0: fm = new nsFontMetrics(); michael@0: NS_ADDREF(fm); michael@0: nsresult rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // the mFontMetrics list has the "head" at the end, because append michael@0: // is cheaper than insert michael@0: mFontMetrics.AppendElement(fm); michael@0: aMetrics = fm; michael@0: NS_ADDREF(aMetrics); michael@0: return NS_OK; michael@0: } michael@0: fm->Destroy(); michael@0: NS_RELEASE(fm); michael@0: michael@0: // One reason why Init() fails is because the system is running out of michael@0: // resources. e.g., on Win95/98 only a very limited number of GDI michael@0: // objects are available. Compact the cache and try again. michael@0: michael@0: Compact(); michael@0: fm = new nsFontMetrics(); michael@0: NS_ADDREF(fm); michael@0: rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mFontMetrics.AppendElement(fm); michael@0: aMetrics = fm; michael@0: return NS_OK; michael@0: } michael@0: fm->Destroy(); michael@0: NS_RELEASE(fm); michael@0: michael@0: // could not setup a new one, send an old one (XXX search a "best michael@0: // match"?) michael@0: michael@0: n = mFontMetrics.Length() - 1; // could have changed in Compact() michael@0: if (n >= 0) { michael@0: aMetrics = mFontMetrics[n]; michael@0: NS_ADDREF(aMetrics); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_POSTCONDITION(NS_SUCCEEDED(rv), michael@0: "font metrics should not be null - bug 136248"); michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: nsFontCache::FontMetricsDeleted(const nsFontMetrics* aFontMetrics) michael@0: { michael@0: mFontMetrics.RemoveElement(aFontMetrics); michael@0: } michael@0: michael@0: void michael@0: nsFontCache::Compact() michael@0: { michael@0: // Need to loop backward because the running element can be removed on michael@0: // the way michael@0: for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) { michael@0: nsFontMetrics* fm = mFontMetrics[i]; michael@0: nsFontMetrics* oldfm = fm; michael@0: // Destroy() isn't here because we want our device context to be michael@0: // notified michael@0: NS_RELEASE(fm); // this will reset fm to nullptr michael@0: // if the font is really gone, it would have called back in michael@0: // FontMetricsDeleted() and would have removed itself michael@0: if (mFontMetrics.IndexOf(oldfm) != mFontMetrics.NoIndex) { michael@0: // nope, the font is still there, so let's hold onto it too michael@0: NS_ADDREF(oldfm); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFontCache::Flush() michael@0: { michael@0: for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) { michael@0: nsFontMetrics* fm = mFontMetrics[i]; michael@0: // Destroy() will unhook our device context from the fm so that we michael@0: // won't waste time in triggering the notification of michael@0: // FontMetricsDeleted() in the subsequent release michael@0: fm->Destroy(); michael@0: NS_RELEASE(fm); michael@0: } michael@0: mFontMetrics.Clear(); michael@0: } michael@0: michael@0: nsDeviceContext::nsDeviceContext() michael@0: : mWidth(0), mHeight(0), mDepth(0), michael@0: mAppUnitsPerDevPixel(-1), mAppUnitsPerDevNotScaledPixel(-1), michael@0: mAppUnitsPerPhysicalInch(-1), michael@0: mPixelScale(1.0f), mPrintingScale(1.0f), michael@0: mFontCache(nullptr) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread"); michael@0: } michael@0: michael@0: // Note: we use a bare pointer for mFontCache so that nsFontCache michael@0: // can be an incomplete type in nsDeviceContext.h. michael@0: // Therefore we have to do all the refcounting by hand. michael@0: nsDeviceContext::~nsDeviceContext() michael@0: { michael@0: if (mFontCache) { michael@0: mFontCache->Destroy(); michael@0: NS_RELEASE(mFontCache); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsDeviceContext::GetMetricsFor(const nsFont& aFont, michael@0: nsIAtom* aLanguage, michael@0: gfxUserFontSet* aUserFontSet, michael@0: gfxTextPerfMetrics* aTextPerf, michael@0: nsFontMetrics*& aMetrics) michael@0: { michael@0: if (!mFontCache) { michael@0: mFontCache = new nsFontCache(); michael@0: NS_ADDREF(mFontCache); michael@0: mFontCache->Init(this); michael@0: } michael@0: michael@0: return mFontCache->GetMetricsFor(aFont, aLanguage, aUserFontSet, michael@0: aTextPerf, aMetrics); michael@0: } michael@0: michael@0: nsresult michael@0: nsDeviceContext::FlushFontCache(void) michael@0: { michael@0: if (mFontCache) michael@0: mFontCache->Flush(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsDeviceContext::FontMetricsDeleted(const nsFontMetrics* aFontMetrics) michael@0: { michael@0: if (mFontCache) { michael@0: mFontCache->FontMetricsDeleted(aFontMetrics); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsDeviceContext::IsPrinterSurface() michael@0: { michael@0: return mPrintingSurface != nullptr; michael@0: } michael@0: michael@0: void michael@0: nsDeviceContext::SetDPI() michael@0: { michael@0: float dpi = -1.0f; michael@0: michael@0: // PostScript, PDF and Mac (when printing) all use 72 dpi michael@0: // Use a printing DC to determine the other dpi values michael@0: if (mPrintingSurface) { michael@0: switch (mPrintingSurface->GetType()) { michael@0: case gfxSurfaceType::PDF: michael@0: case gfxSurfaceType::PS: michael@0: case gfxSurfaceType::Quartz: michael@0: dpi = 72.0f; michael@0: break; michael@0: #ifdef XP_WIN michael@0: case gfxSurfaceType::Win32: michael@0: case gfxSurfaceType::Win32Printing: { michael@0: HDC dc = reinterpret_cast(mPrintingSurface.get())->GetDC(); michael@0: int32_t OSVal = GetDeviceCaps(dc, LOGPIXELSY); michael@0: dpi = 144.0f; michael@0: mPrintingScale = float(OSVal) / dpi; michael@0: break; michael@0: } michael@0: #endif michael@0: default: michael@0: NS_NOTREACHED("Unexpected printing surface type"); michael@0: break; michael@0: } michael@0: michael@0: mAppUnitsPerDevNotScaledPixel = michael@0: NS_lround((AppUnitsPerCSSPixel() * 96) / dpi); michael@0: } else { michael@0: // A value of -1 means use the maximum of 96 and the system DPI. michael@0: // A value of 0 means use the system DPI. A positive value is used as the DPI. michael@0: // This sets the physical size of a device pixel and thus controls the michael@0: // interpretation of physical units. michael@0: int32_t prefDPI = Preferences::GetInt("layout.css.dpi", -1); michael@0: michael@0: if (prefDPI > 0) { michael@0: dpi = prefDPI; michael@0: } else if (mWidget) { michael@0: dpi = mWidget->GetDPI(); michael@0: michael@0: if (prefDPI < 0) { michael@0: dpi = std::max(96.0f, dpi); michael@0: } michael@0: } else { michael@0: dpi = 96.0f; michael@0: } michael@0: michael@0: CSSToLayoutDeviceScale scale = mWidget ? mWidget->GetDefaultScale() michael@0: : CSSToLayoutDeviceScale(1.0); michael@0: double devPixelsPerCSSPixel = scale.scale; michael@0: michael@0: mAppUnitsPerDevNotScaledPixel = michael@0: std::max(1, NS_lround(AppUnitsPerCSSPixel() / devPixelsPerCSSPixel)); michael@0: } michael@0: michael@0: NS_ASSERTION(dpi != -1.0, "no dpi set"); michael@0: michael@0: mAppUnitsPerPhysicalInch = NS_lround(dpi * mAppUnitsPerDevNotScaledPixel); michael@0: UpdateScaledAppUnits(); michael@0: } michael@0: michael@0: nsresult michael@0: nsDeviceContext::Init(nsIWidget *aWidget) michael@0: { michael@0: if (mScreenManager && mWidget == aWidget) michael@0: return NS_OK; michael@0: michael@0: mWidget = aWidget; michael@0: SetDPI(); michael@0: michael@0: if (mScreenManager) michael@0: return NS_OK; michael@0: michael@0: mScreenManager = do_GetService("@mozilla.org/gfx/screenmanager;1"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDeviceContext::CreateRenderingContext() michael@0: { michael@0: nsRefPtr printingSurface = mPrintingSurface; michael@0: #ifdef XP_MACOSX michael@0: // CreateRenderingContext() can be called (on reflow) after EndPage() michael@0: // but before BeginPage(). On OS X (and only there) mPrintingSurface michael@0: // will in this case be null, because OS X printing surfaces are michael@0: // per-page, and therefore only truly valid between calls to BeginPage() michael@0: // and EndPage(). But we can get away with fudging things here, if need michael@0: // be, by using a cached copy. michael@0: if (!printingSurface) { michael@0: printingSurface = mCachedPrintingSurface; michael@0: } michael@0: #endif michael@0: nsRefPtr pContext = new nsRenderingContext(); michael@0: michael@0: RefPtr dt = michael@0: gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(printingSurface, michael@0: gfx::IntSize(mWidth, mHeight)); michael@0: michael@0: pContext->Init(this, dt); michael@0: pContext->ThebesContext()->SetFlag(gfxContext::FLAG_DISABLE_SNAPPING); michael@0: pContext->Scale(mPrintingScale, mPrintingScale); michael@0: michael@0: return pContext.forget(); michael@0: } michael@0: michael@0: nsresult michael@0: nsDeviceContext::GetDepth(uint32_t& aDepth) michael@0: { michael@0: if (mDepth == 0) { michael@0: nsCOMPtr primaryScreen; michael@0: mScreenManager->GetPrimaryScreen(getter_AddRefs(primaryScreen)); michael@0: primaryScreen->GetColorDepth(reinterpret_cast(&mDepth)); michael@0: } michael@0: michael@0: aDepth = mDepth; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsDeviceContext::GetDeviceSurfaceDimensions(nscoord &aWidth, nscoord &aHeight) michael@0: { michael@0: if (mPrintingSurface) { michael@0: // we have a printer device michael@0: aWidth = mWidth; michael@0: aHeight = mHeight; michael@0: } else { michael@0: nsRect area; michael@0: ComputeFullAreaUsingScreen(&area); michael@0: aWidth = area.width; michael@0: aHeight = area.height; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsDeviceContext::GetRect(nsRect &aRect) michael@0: { michael@0: if (mPrintingSurface) { michael@0: // we have a printer device michael@0: aRect.x = 0; michael@0: aRect.y = 0; michael@0: aRect.width = mWidth; michael@0: aRect.height = mHeight; michael@0: } else michael@0: ComputeFullAreaUsingScreen ( &aRect ); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsDeviceContext::GetClientRect(nsRect &aRect) michael@0: { michael@0: if (mPrintingSurface) { michael@0: // we have a printer device michael@0: aRect.x = 0; michael@0: aRect.y = 0; michael@0: aRect.width = mWidth; michael@0: aRect.height = mHeight; michael@0: } michael@0: else michael@0: ComputeClientRectUsingScreen(&aRect); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsDeviceContext::InitForPrinting(nsIDeviceContextSpec *aDevice) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aDevice); michael@0: michael@0: mDeviceContextSpec = aDevice; michael@0: michael@0: nsresult rv = aDevice->GetSurfaceForPrinter(getter_AddRefs(mPrintingSurface)); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: Init(nullptr); michael@0: michael@0: CalcPrintingSize(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsDeviceContext::BeginDocument(const nsAString& aTitle, michael@0: char16_t* aPrintToFileName, michael@0: int32_t aStartPage, michael@0: int32_t aEndPage) michael@0: { michael@0: static const char16_t kEmpty[] = { '\0' }; michael@0: nsresult rv; michael@0: michael@0: rv = mPrintingSurface->BeginPrinting(aTitle, michael@0: nsDependentString(aPrintToFileName ? aPrintToFileName : kEmpty)); michael@0: michael@0: if (NS_SUCCEEDED(rv) && mDeviceContextSpec) michael@0: rv = mDeviceContextSpec->BeginDocument(aTitle, aPrintToFileName, aStartPage, aEndPage); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsDeviceContext::EndDocument(void) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (mPrintingSurface) { michael@0: rv = mPrintingSurface->EndPrinting(); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mPrintingSurface->Finish(); michael@0: } michael@0: michael@0: if (mDeviceContextSpec) michael@0: mDeviceContextSpec->EndDocument(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsDeviceContext::AbortDocument(void) michael@0: { michael@0: nsresult rv = mPrintingSurface->AbortPrinting(); michael@0: michael@0: if (mDeviceContextSpec) michael@0: mDeviceContextSpec->EndDocument(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsDeviceContext::BeginPage(void) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (mDeviceContextSpec) michael@0: rv = mDeviceContextSpec->BeginPage(); michael@0: michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: #ifdef XP_MACOSX michael@0: // We need to get a new surface for each page on the Mac, as the michael@0: // CGContextRefs are only good for one page. michael@0: mDeviceContextSpec->GetSurfaceForPrinter(getter_AddRefs(mPrintingSurface)); michael@0: #endif michael@0: michael@0: rv = mPrintingSurface->BeginPage(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsDeviceContext::EndPage(void) michael@0: { michael@0: nsresult rv = mPrintingSurface->EndPage(); michael@0: michael@0: #ifdef XP_MACOSX michael@0: // We need to release the CGContextRef in the surface here, plus it's michael@0: // not something you would want anyway, as these CGContextRefs are only michael@0: // good for one page. But we need to keep a cached reference to it, since michael@0: // CreateRenderingContext() may try to access it when mPrintingSurface michael@0: // would normally be null. See bug 665218. If we just stop nulling out michael@0: // mPrintingSurface here (and thereby make that our cached copy), we'll michael@0: // break all our null checks on mPrintingSurface. See bug 684622. michael@0: mCachedPrintingSurface = mPrintingSurface; michael@0: mPrintingSurface = nullptr; michael@0: #endif michael@0: michael@0: if (mDeviceContextSpec) michael@0: mDeviceContextSpec->EndPage(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: nsDeviceContext::ComputeClientRectUsingScreen(nsRect* outRect) michael@0: { michael@0: // we always need to recompute the clientRect michael@0: // because the window may have moved onto a different screen. In the single michael@0: // monitor case, we only need to do the computation if we haven't done it michael@0: // once already, and remember that we have because we're assured it won't change. michael@0: nsCOMPtr screen; michael@0: FindScreen (getter_AddRefs(screen)); michael@0: if (screen) { michael@0: int32_t x, y, width, height; michael@0: screen->GetAvailRect(&x, &y, &width, &height); michael@0: michael@0: // convert to device units michael@0: outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel()); michael@0: outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel()); michael@0: outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel()); michael@0: outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel()); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDeviceContext::ComputeFullAreaUsingScreen(nsRect* outRect) michael@0: { michael@0: // if we have more than one screen, we always need to recompute the clientRect michael@0: // because the window may have moved onto a different screen. In the single michael@0: // monitor case, we only need to do the computation if we haven't done it michael@0: // once already, and remember that we have because we're assured it won't change. michael@0: nsCOMPtr screen; michael@0: FindScreen ( getter_AddRefs(screen) ); michael@0: if ( screen ) { michael@0: int32_t x, y, width, height; michael@0: screen->GetRect ( &x, &y, &width, &height ); michael@0: michael@0: // convert to device units michael@0: outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel()); michael@0: outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel()); michael@0: outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel()); michael@0: outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel()); michael@0: michael@0: mWidth = outRect->width; michael@0: mHeight = outRect->height; michael@0: } michael@0: } michael@0: michael@0: // michael@0: // FindScreen michael@0: // michael@0: // Determines which screen intersects the largest area of the given surface. michael@0: // michael@0: void michael@0: nsDeviceContext::FindScreen(nsIScreen** outScreen) michael@0: { michael@0: if (mWidget && mWidget->GetNativeData(NS_NATIVE_WINDOW)) michael@0: mScreenManager->ScreenForNativeWidget(mWidget->GetNativeData(NS_NATIVE_WINDOW), michael@0: outScreen); michael@0: else michael@0: mScreenManager->GetPrimaryScreen(outScreen); michael@0: } michael@0: michael@0: void michael@0: nsDeviceContext::CalcPrintingSize() michael@0: { michael@0: if (!mPrintingSurface) michael@0: return; michael@0: michael@0: bool inPoints = true; michael@0: michael@0: gfxSize size(0, 0); michael@0: switch (mPrintingSurface->GetType()) { michael@0: case gfxSurfaceType::Image: michael@0: inPoints = false; michael@0: size = reinterpret_cast(mPrintingSurface.get())->GetSize(); michael@0: break; michael@0: michael@0: #if defined(MOZ_PDF_PRINTING) michael@0: case gfxSurfaceType::PDF: michael@0: inPoints = true; michael@0: size = reinterpret_cast(mPrintingSurface.get())->GetSize(); michael@0: break; michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: case gfxSurfaceType::PS: michael@0: inPoints = true; michael@0: size = reinterpret_cast(mPrintingSurface.get())->GetSize(); michael@0: break; michael@0: #endif michael@0: michael@0: #ifdef XP_MACOSX michael@0: case gfxSurfaceType::Quartz: michael@0: inPoints = true; // this is really only true when we're printing michael@0: size = reinterpret_cast(mPrintingSurface.get())->GetSize(); michael@0: break; michael@0: #endif michael@0: michael@0: #ifdef XP_WIN michael@0: case gfxSurfaceType::Win32: michael@0: case gfxSurfaceType::Win32Printing: michael@0: { michael@0: inPoints = false; michael@0: HDC dc = reinterpret_cast(mPrintingSurface.get())->GetDC(); michael@0: if (!dc) michael@0: dc = GetDC((HWND)mWidget->GetNativeData(NS_NATIVE_WIDGET)); michael@0: size.width = NSFloatPixelsToAppUnits(::GetDeviceCaps(dc, HORZRES)/mPrintingScale, AppUnitsPerDevPixel()); michael@0: size.height = NSFloatPixelsToAppUnits(::GetDeviceCaps(dc, VERTRES)/mPrintingScale, AppUnitsPerDevPixel()); michael@0: mDepth = (uint32_t)::GetDeviceCaps(dc, BITSPIXEL); michael@0: if (dc != reinterpret_cast(mPrintingSurface.get())->GetDC()) michael@0: ReleaseDC((HWND)mWidget->GetNativeData(NS_NATIVE_WIDGET), dc); michael@0: break; michael@0: } michael@0: #endif michael@0: michael@0: default: michael@0: NS_ERROR("trying to print to unknown surface type"); michael@0: } michael@0: michael@0: if (inPoints) { michael@0: // For printing, CSS inches and physical inches are identical michael@0: // so it doesn't matter which we use here michael@0: mWidth = NSToCoordRound(float(size.width) * AppUnitsPerPhysicalInch() / 72); michael@0: mHeight = NSToCoordRound(float(size.height) * AppUnitsPerPhysicalInch() / 72); michael@0: } else { michael@0: mWidth = NSToIntRound(size.width); michael@0: mHeight = NSToIntRound(size.height); michael@0: } michael@0: } michael@0: michael@0: bool nsDeviceContext::CheckDPIChange() { michael@0: int32_t oldDevPixels = mAppUnitsPerDevNotScaledPixel; michael@0: int32_t oldInches = mAppUnitsPerPhysicalInch; michael@0: michael@0: SetDPI(); michael@0: michael@0: return oldDevPixels != mAppUnitsPerDevNotScaledPixel || michael@0: oldInches != mAppUnitsPerPhysicalInch; michael@0: } michael@0: michael@0: bool michael@0: nsDeviceContext::SetPixelScale(float aScale) michael@0: { michael@0: if (aScale <= 0) { michael@0: NS_NOTREACHED("Invalid pixel scale value"); michael@0: return false; michael@0: } michael@0: int32_t oldAppUnitsPerDevPixel = mAppUnitsPerDevPixel; michael@0: mPixelScale = aScale; michael@0: UpdateScaledAppUnits(); michael@0: return oldAppUnitsPerDevPixel != mAppUnitsPerDevPixel; michael@0: } michael@0: michael@0: void michael@0: nsDeviceContext::UpdateScaledAppUnits() michael@0: { michael@0: mAppUnitsPerDevPixel = michael@0: std::max(1, NSToIntRound(float(mAppUnitsPerDevNotScaledPixel) / mPixelScale)); michael@0: // adjust mPixelScale to reflect appunit rounding michael@0: mPixelScale = float(mAppUnitsPerDevNotScaledPixel) / mAppUnitsPerDevPixel; michael@0: }