Bug 1593970 - scale subpixel contribution based on ClearType level setting. r=jfkthame
authorLee Salzman <lsalzman@mozilla.com>
Sat, 09 Nov 2019 13:03:47 +0000
changeset 501410 d967ac6c56aeec99c2fba3814c8550a1c5971384
parent 501409 9644ee96a51a03a8f4e50e9777fb7278f246383c
child 501411 110e47c61308a4691e521cf59a6a05a65815d870
push id36791
push usercsabou@mozilla.com
push dateSun, 10 Nov 2019 09:53:30 +0000
treeherdermozilla-central@72c52c0101cf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1593970
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1593970 - scale subpixel contribution based on ClearType level setting. r=jfkthame Differential Revision: https://phabricator.services.mozilla.com/D52441
gfx/2d/2D.h
gfx/2d/Factory.cpp
gfx/2d/ScaledFontDWrite.cpp
gfx/2d/ScaledFontDWrite.h
gfx/skia/skia/include/ports/SkTypeface_win.h
gfx/skia/skia/src/ports/SkFontHost_win.cpp
gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp
gfx/skia/skia/src/ports/SkScalerContext_win_dw.h
gfx/skia/skia/src/ports/SkTypeface_win_dw.h
gfx/thebes/gfxDWriteFonts.cpp
gfx/wr/webrender/src/gamma_lut.rs
gfx/wr/webrender/src/platform/windows/font.rs
gfx/wr/webrender_api/src/font.rs
layout/reftests/bidi/reftest.list
layout/reftests/w3c-css/received/reftest.list
testing/web-platform/meta/css/css-contain/contain-layout-017.html.ini
testing/web-platform/meta/css/css-contain/contain-layout-018.html.ini
testing/web-platform/meta/css/css-contain/contain-paint-021.html.ini
testing/web-platform/meta/css/css-contain/contain-paint-025.html.ini
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1881,17 +1881,18 @@ class GFX2D_API Factory {
   static uint64_t GetD2DVRAMUsageDrawTarget();
   static uint64_t GetD2DVRAMUsageSourceSurface();
   static void D2DCleanup();
 
   static already_AddRefed<ScaledFont> CreateScaledFontForDWriteFont(
       IDWriteFontFace* aFontFace, const gfxFontStyle* aStyle,
       const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize,
       bool aUseEmbeddedBitmap, int aRenderingMode,
-      IDWriteRenderingParams* aParams, Float aGamma, Float aContrast);
+      IDWriteRenderingParams* aParams, Float aGamma, Float aContrast,
+      Float aClearTypeLevel);
 
   static already_AddRefed<ScaledFont> CreateScaledFontForGDIFont(
       const void* aLogFont, const RefPtr<UnscaledFont>& aUnscaledFont,
       Float aSize);
 
   static void SetSystemTextQuality(uint8_t aQuality);
 
  private:
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -981,21 +981,22 @@ void Factory::D2DCleanup() {
   }
   DrawTargetD2D1::CleanupD2D();
 }
 
 already_AddRefed<ScaledFont> Factory::CreateScaledFontForDWriteFont(
     IDWriteFontFace* aFontFace, const gfxFontStyle* aStyle,
     const RefPtr<UnscaledFont>& aUnscaledFont, float aSize,
     bool aUseEmbeddedBitmap, int aRenderingMode,
-    IDWriteRenderingParams* aParams, Float aGamma, Float aContrast) {
-  return MakeAndAddRef<ScaledFontDWrite>(aFontFace, aUnscaledFont, aSize,
-                                         aUseEmbeddedBitmap,
-                                         (DWRITE_RENDERING_MODE)aRenderingMode,
-                                         aParams, aGamma, aContrast, aStyle);
+    IDWriteRenderingParams* aParams, Float aGamma, Float aContrast,
+    Float aClearTypeLevel) {
+  return MakeAndAddRef<ScaledFontDWrite>(
+      aFontFace, aUnscaledFont, aSize, aUseEmbeddedBitmap,
+      (DWRITE_RENDERING_MODE)aRenderingMode, aParams, aGamma, aContrast,
+      aClearTypeLevel, aStyle);
 }
 
 already_AddRefed<ScaledFont> Factory::CreateScaledFontForGDIFont(
     const void* aLogFont, const RefPtr<UnscaledFont>& aUnscaledFont,
     Float aSize) {
   return MakeAndAddRef<ScaledFontWin>(static_cast<const LOGFONT*>(aLogFont),
                                       aUnscaledFont, aSize);
 }
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -117,30 +117,29 @@ static inline DWRITE_FONT_STRETCH DWrite
     return DWRITE_FONT_STRETCH_EXTRA_EXPANDED;
   }
   if (aStretch == FontStretch::UltraExpanded()) {
     return DWRITE_FONT_STRETCH_ULTRA_EXPANDED;
   }
   return DWRITE_FONT_STRETCH_UNDEFINED;
 }
 
-ScaledFontDWrite::ScaledFontDWrite(IDWriteFontFace* aFontFace,
-                                   const RefPtr<UnscaledFont>& aUnscaledFont,
-                                   Float aSize, bool aUseEmbeddedBitmap,
-                                   DWRITE_RENDERING_MODE aRenderingMode,
-                                   IDWriteRenderingParams* aParams,
-                                   Float aGamma, Float aContrast,
-                                   const gfxFontStyle* aStyle)
+ScaledFontDWrite::ScaledFontDWrite(
+    IDWriteFontFace* aFontFace, const RefPtr<UnscaledFont>& aUnscaledFont,
+    Float aSize, bool aUseEmbeddedBitmap, DWRITE_RENDERING_MODE aRenderingMode,
+    IDWriteRenderingParams* aParams, Float aGamma, Float aContrast,
+    Float aClearTypeLevel, const gfxFontStyle* aStyle)
     : ScaledFontBase(aUnscaledFont, aSize),
       mFontFace(aFontFace),
       mUseEmbeddedBitmap(aUseEmbeddedBitmap),
       mRenderingMode(aRenderingMode),
       mParams(aParams),
       mGamma(aGamma),
-      mContrast(aContrast) {
+      mContrast(aContrast),
+      mClearTypeLevel(aClearTypeLevel) {
   if (aStyle) {
     mStyle = SkFontStyle(aStyle->weight.ToIntRounded(),
                          DWriteFontStretchFromStretch(aStyle->stretch),
                          // FIXME(jwatt): also use kOblique_Slant
                          aStyle->style == FontSlantStyle::Normal()
                              ? SkFontStyle::kUpright_Slant
                              : SkFontStyle::kItalic_Slant);
   }
@@ -177,18 +176,24 @@ SkTypeface* ScaledFontDWrite::CreateSkTy
   }
 
   Float contrast = mContrast;
   // Skia doesn't support a contrast value outside of 0-1, so default to 1.0
   if (contrast < 0.0f || contrast > 1.0f) {
     contrast = 1.0f;
   }
 
+  Float clearTypeLevel = mClearTypeLevel;
+  if (clearTypeLevel < 0.0f || clearTypeLevel > 1.0f) {
+    clearTypeLevel = 1.0f;
+  }
+
   return SkCreateTypefaceFromDWriteFont(factory, mFontFace, mStyle,
-                                        (int)mRenderingMode, gamma, contrast);
+                                        (int)mRenderingMode, gamma, contrast,
+                                        clearTypeLevel);
 }
 
 void ScaledFontDWrite::SetupSkFontDrawOptions(SkFont& aFont) {
   if (ForceGDIMode()) {
     aFont.setEmbeddedBitmaps(true);
     aFont.setSubpixel(false);
   } else {
     aFont.setEmbeddedBitmaps(UseEmbeddedBitmaps());
@@ -395,17 +400,18 @@ bool UnscaledFontDWrite::GetWRFontDescri
 
 ScaledFontDWrite::InstanceData::InstanceData(
     const wr::FontInstanceOptions* aOptions,
     const wr::FontInstancePlatformOptions* aPlatformOptions)
     : mUseEmbeddedBitmap(false),
       mApplySyntheticBold(false),
       mRenderingMode(DWRITE_RENDERING_MODE_DEFAULT),
       mGamma(2.2f),
-      mContrast(1.0f) {
+      mContrast(1.0f),
+      mClearTypeLevel(1.0f) {
   if (aOptions) {
     if (aOptions->flags & wr::FontInstanceFlags_EMBEDDED_BITMAPS) {
       mUseEmbeddedBitmap = true;
     }
     if (aOptions->flags & wr::FontInstanceFlags_SYNTHETIC_BOLD) {
       mApplySyntheticBold = true;
     }
     if (aOptions->flags & wr::FontInstanceFlags_FORCE_GDI) {
@@ -414,16 +420,17 @@ ScaledFontDWrite::InstanceData::Instance
       mRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
     } else if (aOptions->flags & wr::FontInstanceFlags_NO_SYMMETRIC) {
       mRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
     }
   }
   if (aPlatformOptions) {
     mGamma = aPlatformOptions->gamma / 100.0f;
     mContrast = aPlatformOptions->contrast / 100.0f;
+    mClearTypeLevel = aPlatformOptions->cleartype_level / 100.0f;
   }
 }
 
 // Helper for ScaledFontDWrite::GetFontInstanceData: if the font has variation
 // axes, get their current values into the aOutput vector.
 static void GetVariationsFromFontFace(IDWriteFontFace* aFace,
                                       std::vector<FontVariation>* aOutput) {
   RefPtr<IDWriteFontFace5> ff5;
@@ -508,17 +515,19 @@ bool ScaledFontDWrite::GetWRFontInstance
   }
   options.bg_color = wr::ToColorU(Color());
   options.synthetic_italics =
       wr::DegreesToSyntheticItalics(GetSyntheticObliqueAngle());
 
   wr::FontInstancePlatformOptions platformOptions;
   platformOptions.gamma = uint16_t(std::round(mGamma * 100.0f));
   platformOptions.contrast =
-      uint16_t(std::round(std::min(mContrast, 1.0f) * 100.0f));
+      uint8_t(std::round(std::min(mContrast, 1.0f) * 100.0f));
+  platformOptions.cleartype_level =
+      uint8_t(std::round(std::min(mClearTypeLevel, 1.0f) * 100.0f));
 
   *aOutOptions = Some(options);
   *aOutPlatformOptions = Some(platformOptions);
 
   GetVariationsFromFontFace(mFontFace, aOutVariations);
 
   return true;
 }
@@ -643,17 +652,17 @@ already_AddRefed<ScaledFont> UnscaledFon
     } else {
       gfxWarning() << "Failed to create IDWriteFontFace5 with variations.";
     }
   }
 
   RefPtr<ScaledFontBase> scaledFont = new ScaledFontDWrite(
       face, this, aGlyphSize, instanceData.mUseEmbeddedBitmap,
       instanceData.mRenderingMode, nullptr, instanceData.mGamma,
-      instanceData.mContrast);
+      instanceData.mContrast, instanceData.mClearTypeLevel);
 
   return scaledFont.forget();
 }
 
 already_AddRefed<ScaledFont> UnscaledFontDWrite::CreateScaledFontFromWRFont(
     Float aGlyphSize, const wr::FontInstanceOptions* aOptions,
     const wr::FontInstancePlatformOptions* aPlatformOptions,
     const FontVariation* aVariations, uint32_t aNumVariations) {
@@ -663,17 +672,17 @@ already_AddRefed<ScaledFont> UnscaledFon
 }
 
 AntialiasMode ScaledFontDWrite::GetDefaultAAMode() {
   AntialiasMode defaultMode = GetSystemDefaultAAMode();
 
   switch (defaultMode) {
     case AntialiasMode::SUBPIXEL:
     case AntialiasMode::DEFAULT:
-      if (mParams && mParams->GetClearTypeLevel() == 0.0f) {
+      if (mClearTypeLevel == 0.0f) {
         defaultMode = AntialiasMode::GRAY;
       }
       break;
     case AntialiasMode::GRAY:
       if (!DoGrayscale(mFontFace, mSize)) {
         defaultMode = AntialiasMode::NONE;
       }
       break;
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -24,24 +24,26 @@ class ScaledFontDWrite final : public Sc
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontDWrite, override)
   ScaledFontDWrite(IDWriteFontFace* aFont,
                    const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize)
       : ScaledFontBase(aUnscaledFont, aSize),
         mFontFace(aFont),
         mUseEmbeddedBitmap(false),
         mRenderingMode(DWRITE_RENDERING_MODE_DEFAULT),
         mGamma(2.2f),
-        mContrast(1.0f) {}
+        mContrast(1.0f),
+        mClearTypeLevel(1.0f) {}
 
   ScaledFontDWrite(IDWriteFontFace* aFontFace,
                    const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize,
                    bool aUseEmbeddedBitmap,
                    DWRITE_RENDERING_MODE aRenderingMode,
                    IDWriteRenderingParams* aParams, Float aGamma,
-                   Float aContrast, const gfxFontStyle* aStyle = nullptr);
+                   Float aContrast, Float aClearTypeLevel,
+                   const gfxFontStyle* aStyle = nullptr);
 
   FontType GetType() const override { return FontType::DWRITE; }
 
   already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer& aBuffer,
                                           const DrawTarget* aTarget) override;
   void CopyGlyphsToBuilder(const GlyphBuffer& aBuffer, PathBuilder* aBuilder,
                            const Matrix* aTransformHint) override;
 
@@ -82,16 +84,17 @@ class ScaledFontDWrite final : public Sc
   // but we also separately need to store the gamma and contrast
   // since Skia needs to be able to access these without having
   // to use the full set of DWrite parameters (which would be
   // required to recreate an IDWriteRenderingParams) in a
   // DrawTargetRecording playback.
   RefPtr<IDWriteRenderingParams> mParams;
   Float mGamma;
   Float mContrast;
+  Float mClearTypeLevel;
 
 #ifdef USE_CAIRO_SCALED_FONT
   cairo_font_face_t* CreateCairoFontFace(
       cairo_font_options_t* aFontOptions) override;
   void PrepareCairoScaledFont(cairo_scaled_font_t* aFont) override;
 #endif
 
  private:
@@ -99,25 +102,27 @@ class ScaledFontDWrite final : public Sc
   friend class UnscaledFontDWrite;
 
   struct InstanceData {
     explicit InstanceData(ScaledFontDWrite* aScaledFont)
         : mUseEmbeddedBitmap(aScaledFont->mUseEmbeddedBitmap),
           mApplySyntheticBold(aScaledFont->HasSyntheticBold()),
           mRenderingMode(aScaledFont->mRenderingMode),
           mGamma(aScaledFont->mGamma),
-          mContrast(aScaledFont->mContrast) {}
+          mContrast(aScaledFont->mContrast),
+          mClearTypeLevel(aScaledFont->mClearTypeLevel) {}
 
     InstanceData(const wr::FontInstanceOptions* aOptions,
                  const wr::FontInstancePlatformOptions* aPlatformOptions);
 
     bool mUseEmbeddedBitmap;
     bool mApplySyntheticBold;
     DWRITE_RENDERING_MODE mRenderingMode;
     Float mGamma;
     Float mContrast;
+    Float mClearTypeLevel;
   };
 };
 
 }  // namespace gfx
 }  // namespace mozilla
 
 #endif /* MOZILLA_GFX_SCALEDFONTDWRITE_H_ */
--- a/gfx/skia/skia/include/ports/SkTypeface_win.h
+++ b/gfx/skia/skia/include/ports/SkTypeface_win.h
@@ -58,17 +58,18 @@ struct IDWriteFontFace;
  *  corresponding typeface for the specified dwrite font. The caller is responsible
  *  for calling unref() when it is finished.
  */
 SK_API SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFactory* aFactory,
                                                   IDWriteFontFace* aFontFace,
                                                   SkFontStyle aStyle,
                                                   int aRenderingMode,
                                                   float aGamma,
-                                                  float aContrast);
+                                                  float aContrast,
+                                                  float aClearTypeLevel);
 
 SK_API sk_sp<SkFontMgr> SkFontMgr_New_GDI();
 SK_API sk_sp<SkFontMgr> SkFontMgr_New_DirectWrite(IDWriteFactory* factory = NULL,
                                                   IDWriteFontCollection* collection = NULL);
 SK_API sk_sp<SkFontMgr> SkFontMgr_New_DirectWrite(IDWriteFactory* factory,
                                                   IDWriteFontCollection* collection,
                                                   IDWriteFontFallback* fallback);
 
--- a/gfx/skia/skia/src/ports/SkFontHost_win.cpp
+++ b/gfx/skia/skia/src/ports/SkFontHost_win.cpp
@@ -362,19 +362,22 @@ SkTypeface* SkCreateTypefaceFromLOGFONT(
 /***
  * This guy is public.
  */
 SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFactory* aFactory,
                                            IDWriteFontFace* aFontFace,
                                            SkFontStyle aStyle,
                                            int aRenderingMode,
                                            float aGamma,
-                                           float aContrast)
+                                           float aContrast,
+                                           float aClearTypeLevel)
 {
-  return DWriteFontTypeface::Create(aFactory, aFontFace, aStyle, (DWRITE_RENDERING_MODE)aRenderingMode, aGamma, aContrast);
+  return DWriteFontTypeface::Create(aFactory, aFontFace, aStyle,
+                                    (DWRITE_RENDERING_MODE)aRenderingMode,
+                                    aGamma, aContrast, aClearTypeLevel);
 }
 
 /**
  *  The created SkTypeface takes ownership of fontMemResource.
  */
 sk_sp<SkTypeface> SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
     LOGFONT lf = origLF;
     make_canonical(&lf);
--- a/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp
+++ b/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp
@@ -242,16 +242,17 @@ SkScalerContext_DW::SkScalerContext_DW(s
                                        const SkDescriptor* desc)
         : SkScalerContext(std::move(typefaceRef), effects, desc)
         , fGlyphCount(-1) {
 
     DWriteFontTypeface* typeface = this->getDWriteTypeface();
     fIsColorFont = typeface->fFactory2 &&
                    typeface->fDWriteFontFace2 &&
                    typeface->fDWriteFontFace2->IsColorFont();
+    fClearTypeLevel = int(typeface->GetClearTypeLevel() * 256);
 
     // In general, all glyphs should use DWriteFontFace::GetRecommendedRenderingMode
     // except when bi-level rendering is requested or there are embedded
     // bi-level bitmaps (and the embedded bitmap flag is set and no rotation).
     //
     // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do
     // this. As a result, determine the actual size of the text and then see if
     // there are any embedded bi-level bitmaps of that size. If there are, then
@@ -335,17 +336,16 @@ SkScalerContext_DW::SkScalerContext_DW(s
                 range.fVersion >= 1) ||
                realTextSize > SkIntToScalar(20) || !is_hinted(this, typeface)) {
         fTextSizeRender = realTextSize;
         fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
         fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
         fTextSizeMeasure = realTextSize;
         fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
 
-        DWriteFontTypeface* typeface = static_cast<DWriteFontTypeface*>(getTypeface());
         switch (typeface->GetRenderingMode()) {
         case DWRITE_RENDERING_MODE_NATURAL:
         case DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC:
             fRenderingMode = typeface->GetRenderingMode();
             break;
         default:
             if (IDWriteRenderingParams* params = sk_get_dwrite_default_rendering_params()) {
                 typeface->fDWriteFontFace->GetRecommendedRenderingMode(
@@ -911,33 +911,35 @@ void SkScalerContext_DW::RGBToA8(const u
         }
         dst = SkTAddOffset<uint8_t>(dst, dstRB);
     }
 }
 
 template<bool APPLY_PREBLEND, bool RGB>
 void SkScalerContext_DW::RGBToLcd16(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
                                     const uint8_t* tableR, const uint8_t* tableG,
-                                    const uint8_t* tableB) {
+                                    const uint8_t* tableB, int clearTypeLevel) {
     const size_t dstRB = glyph.rowBytes();
     const int width = glyph.width();
     uint16_t* SK_RESTRICT dst = static_cast<uint16_t*>(glyph.fImage);
 
     for (int y = 0; y < glyph.height(); y++) {
         for (int i = 0; i < width; i++) {
-            U8CPU r, g, b;
+            int r, g, b;
             if (RGB) {
                 r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
                 g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
                 b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
             } else {
                 b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
                 g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
                 r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
             }
+            r = g + (((r - g) * clearTypeLevel) >> 8);
+            b = g + (((b - g) * clearTypeLevel) >> 8);
             dst[i] = SkPack888ToRGB16(r, g, b);
         }
         dst = SkTAddOffset<uint16_t>(dst, dstRB);
     }
 }
 
 const void* SkScalerContext_DW::drawDWMask(const SkGlyph& glyph,
                                            DWRITE_RENDERING_MODE renderingMode,
@@ -1187,25 +1189,25 @@ void SkScalerContext_DW::generateImage(c
             } else {
                 RGBToA8<false>(src, glyph, fPreBlend.fG);
             }
         }
     } else {
         SkASSERT(SkMask::kLCD16_Format == glyph.fMaskFormat);
         if (fPreBlend.isApplicable()) {
             if (fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag) {
-                RGBToLcd16<true, false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+                RGBToLcd16<true, false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB, fClearTypeLevel);
             } else {
-                RGBToLcd16<true, true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+                RGBToLcd16<true, true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB, fClearTypeLevel);
             }
         } else {
             if (fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag) {
-                RGBToLcd16<false, false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+                RGBToLcd16<false, false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB, fClearTypeLevel);
             } else {
-                RGBToLcd16<false, true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+                RGBToLcd16<false, true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB, fClearTypeLevel);
             }
         }
     }
 }
 
 bool SkScalerContext_DW::generatePath(SkGlyphID glyph, SkPath* path) {
     SkASSERT(path);
 
--- a/gfx/skia/skia/src/ports/SkScalerContext_win_dw.h
+++ b/gfx/skia/skia/src/ports/SkScalerContext_win_dw.h
@@ -48,17 +48,18 @@ private:
 
     template<bool APPLY_PREBLEND>
     static void RGBToA8(const uint8_t* SK_RESTRICT src,
                         const SkGlyph& glyph,
                         const uint8_t* table8);
 
     template<bool APPLY_PREBLEND, bool RGB>
     static void RGBToLcd16(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
-                           const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB);
+                           const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB,
+                           int clearTypeLevel);
 
     const void* drawDWMask(const SkGlyph& glyph,
                            DWRITE_RENDERING_MODE renderingMode,
                            DWRITE_TEXTURE_TYPE textureType);
 
     HRESULT getBoundingBox(SkGlyph* glyph,
                            DWRITE_RENDERING_MODE renderingMode,
                            DWRITE_TEXTURE_TYPE textureType,
@@ -94,11 +95,12 @@ private:
     SkScalar fTextSizeMeasure;
     int fGlyphCount;
     DWRITE_RENDERING_MODE fRenderingMode;
     DWRITE_TEXTURE_TYPE fTextureType;
     DWRITE_MEASURING_MODE fMeasuringMode;
     DWRITE_TEXT_ANTIALIAS_MODE fAntiAliasMode;
     DWRITE_GRID_FIT_MODE fGridFitMode;
     bool fIsColorFont;
+    int fClearTypeLevel;
 };
 
 #endif
--- a/gfx/skia/skia/src/ports/SkTypeface_win_dw.h
+++ b/gfx/skia/skia/src/ports/SkTypeface_win_dw.h
@@ -55,16 +55,17 @@ private:
         , fDWriteFontCollectionLoader(SkSafeRefComPtr(fontCollectionLoader))
         , fDWriteFontFileLoader(SkSafeRefComPtr(fontFileLoader))
         , fDWriteFontFamily(SkSafeRefComPtr(fontFamily))
         , fDWriteFont(SkSafeRefComPtr(font))
         , fDWriteFontFace(SkRefComPtr(fontFace))
         , fRenderingMode(DWRITE_RENDERING_MODE_DEFAULT)
         , fGamma(2.2f)
         , fContrast(1.0f)
+        , fClearTypeLevel(1.0f)
     {
         if (!SUCCEEDED(fDWriteFontFace->QueryInterface(&fDWriteFontFace1))) {
             // IUnknown::QueryInterface states that if it fails, punk will be set to nullptr.
             // http://blogs.msdn.com/b/oldnewthing/archive/2004/03/26/96777.aspx
             SkASSERT_RELEASE(nullptr == fDWriteFontFace1.get());
         }
         if (!SUCCEEDED(fDWriteFontFace->QueryInterface(&fDWriteFontFace2))) {
             SkASSERT_RELEASE(nullptr == fDWriteFontFace2.get());
@@ -102,29 +103,32 @@ public:
                                    fontFileLoader, fontCollectionLoader));
     }
 
     static DWriteFontTypeface* Create(IDWriteFactory* factory,
                                       IDWriteFontFace* fontFace,
                                       SkFontStyle aStyle,
                                       DWRITE_RENDERING_MODE aRenderingMode,
                                       float aGamma,
-                                      float aContrast) {
+                                      float aContrast,
+                                      float aClearTypeLevel) {
         DWriteFontTypeface* typeface =
                 new DWriteFontTypeface(aStyle, factory, fontFace,
                                        nullptr, nullptr,
                                        nullptr, nullptr);
         typeface->fRenderingMode = aRenderingMode;
         typeface->fGamma = aGamma;
         typeface->fContrast = aContrast;
+        typeface->fClearTypeLevel = aClearTypeLevel;
         return typeface;
     }
 
     bool ForceGDI() const { return fRenderingMode == DWRITE_RENDERING_MODE_GDI_CLASSIC; }
     DWRITE_RENDERING_MODE GetRenderingMode() const { return fRenderingMode; }
+    float GetClearTypeLevel() const { return fClearTypeLevel; }
 
 protected:
     void weak_dispose() const override {
         if (fDWriteFontCollectionLoader.get()) {
             HRV(fFactory->UnregisterFontCollectionLoader(fDWriteFontCollectionLoader.get()));
         }
         if (fDWriteFontFileLoader.get()) {
             HRV(fFactory->UnregisterFontFileLoader(fDWriteFontFileLoader.get()));
@@ -156,11 +160,12 @@ protected:
     size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override;
     sk_sp<SkData> onCopyTableData(SkFontTableTag) const override;
 
 private:
     typedef SkTypeface INHERITED;
     DWRITE_RENDERING_MODE fRenderingMode;
     float fGamma;
     float fContrast;
+    float fClearTypeLevel;
 };
 
 #endif
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -599,31 +599,33 @@ already_AddRefed<ScaledFont> gfxDWriteFo
             UsingClearType()
                 ? (forceGDI ? gfxWindowsPlatform::TEXT_RENDERING_GDI_CLASSIC
                             : gfxWindowsPlatform::TEXT_RENDERING_NORMAL)
                 : gfxWindowsPlatform::TEXT_RENDERING_NO_CLEARTYPE);
 
     DWRITE_RENDERING_MODE renderingMode = params->GetRenderingMode();
     FLOAT gamma = params->GetGamma();
     FLOAT contrast = params->GetEnhancedContrast();
+    FLOAT clearTypeLevel = params->GetClearTypeLevel();
     if (forceGDI || renderingMode == DWRITE_RENDERING_MODE_GDI_CLASSIC) {
       renderingMode = DWRITE_RENDERING_MODE_GDI_CLASSIC;
       gamma = GetSystemGDIGamma();
       contrast = 0.0f;
     }
 
     bool useEmbeddedBitmap =
         (renderingMode == DWRITE_RENDERING_MODE_DEFAULT ||
          renderingMode == DWRITE_RENDERING_MODE_GDI_CLASSIC) &&
         fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize));
 
     const gfxFontStyle* fontStyle = GetStyle();
     mAzureScaledFont = Factory::CreateScaledFontForDWriteFont(
         mFontFace, fontStyle, GetUnscaledFont(), GetAdjustedSize(),
-        useEmbeddedBitmap, (int)renderingMode, params, gamma, contrast);
+        useEmbeddedBitmap, (int)renderingMode, params, gamma, contrast,
+        clearTypeLevel);
     if (!mAzureScaledFont) {
       return nullptr;
     }
     InitializeScaledFont();
     mAzureScaledFontUsedClearType = UsingClearType();
   }
 
   RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
--- a/gfx/wr/webrender/src/gamma_lut.rs
+++ b/gfx/wr/webrender/src/gamma_lut.rs
@@ -306,16 +306,43 @@ impl GammaLut {
             let (b, g, r) = (table_b[pixel[0] as usize], table_g[pixel[1] as usize], table_r[pixel[2] as usize]);
             pixel[0] = b;
             pixel[1] = g;
             pixel[2] = r;
             pixel[3] = max(max(b, g), r);
         }
     }
 
+    // Assumes pixels are in BGRA format. Assumes pixel values are in linear space already.
+    pub fn preblend_scaled(&self, pixels: &mut [u8], color: ColorU, percent: u8) {
+        if percent >= 100 {
+            self.preblend(pixels, color);
+            return;
+        }
+
+        let table_r = self.get_table(color.r);
+        let table_g = self.get_table(color.g);
+        let table_b = self.get_table(color.b);
+        let scale = (percent as i32 * 256) / 100;
+
+        for pixel in pixels.chunks_mut(4) {
+            let (mut b, g, mut r) = (
+                table_b[pixel[0] as usize] as i32,
+                table_g[pixel[1] as usize] as i32,
+                table_r[pixel[2] as usize] as i32,
+            );
+            b = g + (((b - g) * scale) >> 8);
+            r = g + (((r - g) * scale) >> 8);
+            pixel[0] = b as u8;
+            pixel[1] = g as u8;
+            pixel[2] = r as u8;
+            pixel[3] = max(max(b, g), r) as u8;
+        }
+    }
+
     #[cfg(target_os="macos")]
     pub fn coregraphics_convert_to_linear(&self, pixels: &mut [u8]) {
         for pixel in pixels.chunks_mut(4) {
             pixel[0] = self.cg_inverse_gamma[pixel[0] as usize];
             pixel[1] = self.cg_inverse_gamma[pixel[1] as usize];
             pixel[2] = self.cg_inverse_gamma[pixel[2] as usize];
         }
     }
--- a/gfx/wr/webrender/src/platform/windows/font.rs
+++ b/gfx/wr/webrender/src/platform/windows/font.rs
@@ -67,17 +67,17 @@ struct FontFace {
     file: dwrote::FontFile,
     index: u32,
     face: dwrote::FontFace,
 }
 
 pub struct FontContext {
     fonts: FastHashMap<FontKey, FontFace>,
     variations: FastHashMap<(FontKey, dwrote::DWRITE_FONT_SIMULATIONS, Vec<FontVariation>), dwrote::FontFace>,
-    gamma_luts: FastHashMap<(u16, u16), GammaLut>,
+    gamma_luts: FastHashMap<(u16, u8), GammaLut>,
 }
 
 // DirectWrite is safe to use on multiple threads and non-shareable resources are
 // all hidden inside their font context.
 unsafe impl Send for FontContext {}
 
 fn dwrite_texture_type(render_mode: FontRenderMode) -> dwrote::DWRITE_TEXTURE_TYPE {
     match render_mode {
@@ -547,26 +547,32 @@ impl FontContext {
         if width == 0 || height == 0 {
             return Err(GlyphRasterError::LoadFailed);
         }
 
         let pixels = analysis.create_alpha_texture(texture_type, bounds).or(Err(GlyphRasterError::LoadFailed))?;
         let mut bgra_pixels = self.convert_to_bgra(&pixels, texture_type, font.render_mode, bitmaps,
                                                    font.flags.contains(FontInstanceFlags::SUBPIXEL_BGR));
 
-        let FontInstancePlatformOptions { gamma, contrast, .. } = font.platform_options.unwrap_or_default();
+        let FontInstancePlatformOptions { gamma, contrast, cleartype_level, .. } =
+            font.platform_options.unwrap_or_default();
         let gamma_lut = self.gamma_luts
             .entry((gamma, contrast))
             .or_insert_with(||
                 GammaLut::new(
                     contrast as f32 / 100.0,
                     gamma as f32 / 100.0,
                     gamma as f32 / 100.0,
                 ));
-        gamma_lut.preblend(&mut bgra_pixels, font.color);
+        if bitmaps || texture_type == dwrote::DWRITE_TEXTURE_ALIASED_1x1 ||
+           font.render_mode != FontRenderMode::Subpixel {
+            gamma_lut.preblend(&mut bgra_pixels, font.color);
+        } else {
+            gamma_lut.preblend_scaled(&mut bgra_pixels, font.color, cleartype_level);
+        }
 
         let format = if bitmaps {
             GlyphFormat::Bitmap
         } else if texture_type == dwrote::DWRITE_TEXTURE_ALIASED_1x1 {
             font.get_alpha_glyph_format()
         } else {
             font.get_glyph_format()
         };
--- a/gfx/wr/webrender_api/src/font.rs
+++ b/gfx/wr/webrender_api/src/font.rs
@@ -283,25 +283,27 @@ impl Default for FontInstanceOptions {
     }
 }
 
 #[cfg(target_os = "windows")]
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
 pub struct FontInstancePlatformOptions {
     pub gamma: u16, // percent
-    pub contrast: u16, // percent
+    pub contrast: u8, // percent
+    pub cleartype_level: u8, // percent
 }
 
 #[cfg(target_os = "windows")]
 impl Default for FontInstancePlatformOptions {
     fn default() -> FontInstancePlatformOptions {
         FontInstancePlatformOptions {
             gamma: 180, // Default DWrite gamma
             contrast: 100,
+            cleartype_level: 100,
         }
     }
 }
 
 #[cfg(target_os = "macos")]
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
 pub struct FontInstancePlatformOptions {
--- a/layout/reftests/bidi/reftest.list
+++ b/layout/reftests/bidi/reftest.list
@@ -1,17 +1,17 @@
 include dirAuto/reftest.list
 include numeral/reftest.list
 fuzzy-if(cocoaWidget,0-1,0-1) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == bdi-element.html bdi-element-ref.html # Bug 1392106
 == bidi-000.html bidi-000-ref.html
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == bidi-001.html bidi-001-ref.html # Bug 1392106
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == bidi-001-j.html bidi-001-ref.html # Bug 1392106
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == bidi-001-v.html bidi-001-ref.html # Bug 1392106
 fuzzy-if(Android,0-1,0-1) == bidi-002.html bidi-002-ref.html
-fuzzy-if(Android,0-1,0-1) == bidi-003.html bidi-003-ref.html
+fuzzy-if(Android||winWidget,0-1,0-1) == bidi-003.html bidi-003-ref.html
 fuzzy-if(gtkWidget,0-255,0-17) == bidi-004.html bidi-004-ref.html # inconsistency in the Hebrew font that gets used
 fuzzy-if(gtkWidget,0-255,0-17) == bidi-004-j.html bidi-004-ref.html # inconsistency in the Hebrew font that gets used
 == bidi-005.html bidi-005-ref.html
 random-if(cocoaWidget) == bidi-006.html bidi-006-ref.html # bug 734313
 random-if(cocoaWidget) == bidi-006-j.html bidi-006-ref.html # bug 734313
 fuzzy-if(winWidget,0-1,0-1) == bidiSVG-01.svg bidiSVG-01-ref.svg
 fuzzy-if(Android,0-1,0-1) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == bidiSVG-02.svg bidiSVG-02-ref.svg # Bug 1392106
 fuzzy-if(Android,0-253,0-77) == bidiSVG-03.svg bidiSVG-03-ref.svg
@@ -109,17 +109,17 @@ fuzzy-if(webrender,0-137,0-1) == 562169-
 == 588739-2.html 588739-ref.html
 == 588739-3.html 588739-ref.html
 == 612843-1.html 612843-1-ref.html
 fuzzy-if(Android,0-1,0-1) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 613149-1a.html 613149-1-ref.html # Bug 1392106
 fuzzy-if(Android,0-1,0-1) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 613149-1b.html 613149-1-ref.html # Bug 1392106
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 613149-2a.html 613149-2-ref.html # Bug 1392106
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fuzzy-if(Android,0-24,0-1) == 613149-2b.html 613149-2-ref.html # Bug 1392106
 == 613157-1.html 613157-1-ref.html
-fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)&&!layersGPUAccelerated&&!azureSkia,0-255,0-6) == 613157-2.html 613157-2-ref.html
+fuzzy-if(winWidget,0-1,0-1) fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)&&!layersGPUAccelerated&&!azureSkia,0-255,0-6) == 613157-2.html 613157-2-ref.html
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 662288-1.html 662288-1-ref.html # Bug 1392106
 == 670226-1.html 670226-1-ref.html
 == 676245-1.html 676245-1-ref.html
 fuzzy-if(skiaContent,0-1,0-3) == 698291-1.html 698291-1-ref.html
 == 698706-1.html 698706-1-ref.html
 == 704837-1.html 704837-1-ref.html
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 712600-1.html 712600-1-ref.html # Bug 1392106
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 712600-2.html 712600-2-ref.html # Bug 1392106
--- a/layout/reftests/w3c-css/received/reftest.list
+++ b/layout/reftests/w3c-css/received/reftest.list
@@ -149,17 +149,17 @@ fuzzy(0-127,0-500) == css-multicol/multi
 skip == css-multicol/multicol-rule-shorthand-2.xht css-multicol/multicol-rule-shorthand-2-ref.xht
 fuzzy(0-106,0-354) == css-multicol/multicol-rule-solid-000.xht css-multicol/multicol-rule-solid-000-ref.xht
 fails-if(!OSX) random-if(OSX) == css-multicol/multicol-rule-stacking-001.xht css-multicol/multicol-rule-stacking-ref.xht
 fails-if(!OSX||webrender) random-if(OSX) == css-multicol/multicol-shorthand-001.xht css-multicol/multicol-rule-ref.xht
 pref(layout.css.column-span.enabled,true) == css-multicol/multicol-span-000.xht css-multicol/multicol-span-000-ref.xht
 pref(layout.css.column-span.enabled,true) == css-multicol/multicol-span-all-001.xht css-multicol/multicol-span-all-001-ref.xht
 pref(layout.css.column-span.enabled,true) == css-multicol/multicol-span-all-002.xht css-multicol/multicol-span-all-002-ref.xht
 pref(layout.css.column-span.enabled,true) fails-if(!OSX) random-if(OSX) == css-multicol/multicol-span-all-003.xht css-multicol/multicol-count-002-ref.xht
-pref(layout.css.column-span.enabled,true) fuzzy-if(winWidget,40-47,8-8) fuzzy-if(OSX,0-27,0-11) == css-multicol/multicol-span-all-block-sibling-003.xht css-multicol/multicol-span-all-block-sibling-3-ref.xht
+pref(layout.css.column-span.enabled,true) fuzzy-if(winWidget,16-47,8-8) fuzzy-if(OSX,0-27,0-11) == css-multicol/multicol-span-all-block-sibling-003.xht css-multicol/multicol-span-all-block-sibling-3-ref.xht
 pref(layout.css.column-span.enabled,true) fails-if(geckoview) == css-multicol/multicol-span-all-margin-001.xht css-multicol/multicol-span-all-margin-001-ref.xht # Bug 1558509 for GV
 pref(layout.css.column-span.enabled,true) fails-if(geckoview) == css-multicol/multicol-span-all-margin-002.xht css-multicol/multicol-span-all-margin-002-ref.xht # Bug 1558509 for GV
 pref(layout.css.column-span.enabled,true) fails == css-multicol/multicol-span-all-margin-bottom-001.xht css-multicol/multicol-span-all-margin-bottom-001-ref.xht # Upstream version patched in bug 1496275. It should pass after importing wpt in bug 1584324.
 pref(layout.css.column-span.enabled,true) fails-if(geckoview) == css-multicol/multicol-span-all-margin-nested-001.xht css-multicol/multicol-span-all-margin-nested-001-ref.xht # Bug 1558509 for GV
 pref(layout.css.column-span.enabled,true) fails-if(geckoview) == css-multicol/multicol-span-all-margin-nested-002.xht css-multicol/multicol-span-all-margin-nested-001-ref.xht # Bug 1558509 for GV
 pref(layout.css.column-span.enabled,true) == css-multicol/multicol-span-all-margin-nested-firstchild-001.xht css-multicol/multicol-span-all-margin-nested-firstchild-ref.xht
 pref(layout.css.column-span.enabled,true) == css-multicol/multicol-span-float-001.xht css-multicol/multicol-span-float-001-ref.xht
 pref(layout.css.column-span.enabled,true) fails == css-multicol/multicol-span-none-001.xht css-multicol/multicol-span-none-001-ref.xht # Upstream version patched in bug 1495875. It should pass after importing wpt in bug 1584324.
--- a/testing/web-platform/meta/css/css-contain/contain-layout-017.html.ini
+++ b/testing/web-platform/meta/css/css-contain/contain-layout-017.html.ini
@@ -1,4 +1,4 @@
 [contain-layout-017.html]
   fuzzy:
-    if os == "win": maxDifference=40-47;totalPixels=2
+    if os == "win": maxDifference=16-47;totalPixels=2
     if os == "mac": maxDifference=13;totalPixels=2
--- a/testing/web-platform/meta/css/css-contain/contain-layout-018.html.ini
+++ b/testing/web-platform/meta/css/css-contain/contain-layout-018.html.ini
@@ -1,4 +1,4 @@
 [contain-layout-018.html]
   fuzzy:
-    if os == "win": maxDifference=40-47;totalPixels=2
+    if os == "win": maxDifference=16-47;totalPixels=2
     if os == "mac": maxDifference=13;totalPixels=2
--- a/testing/web-platform/meta/css/css-contain/contain-paint-021.html.ini
+++ b/testing/web-platform/meta/css/css-contain/contain-paint-021.html.ini
@@ -1,4 +1,4 @@
 [contain-paint-021.html]
   fuzzy:
-    if os == "win": maxDifference=40-47;totalPixels=2
+    if os == "win": maxDifference=16-47;totalPixels=2
     if os == "mac": maxDifference=13;totalPixels=2
--- a/testing/web-platform/meta/css/css-contain/contain-paint-025.html.ini
+++ b/testing/web-platform/meta/css/css-contain/contain-paint-025.html.ini
@@ -1,4 +1,4 @@
 [contain-paint-025.html]
   fuzzy:
-    if os == "win": maxDifference=40-47;totalPixels=2
+    if os == "win": maxDifference=16-47;totalPixels=2
     if os == "mac": maxDifference=13;totalPixels=2