Bug 1799258 - Do color-management on Windows+DComp via IDCompositionFilterEffects. r=sotaro

+ Add gfx.color_management.rec709_gamma_as_srgb:true. :'(

In particular, rec709(16/255) -> srgb(31/255). Even though it's
technically correct, it's practically-speaking incorrect, since that's
not what Chrome does, nor what the web expected for years and years.

In practice, basically everyone expects gamma to just be completely
ignored.

What people expect:
* Pretend gamut is srgb(==rec709), but stretch this naively for the
  display. If you have a display-p3-gamut display, srgb:0.5 expects to
  be displayed as display:0.5, which will be display-p3:0.5 to the eyes.
* Pretend all content gammas (TFs) are srgb(!=rec790), and then bitcast this
  naively for the display. E.g. rec709(16/255) should
  display the same as srgb(16/255), not srgb(31/255). (Note: display-p3
  uses srgb gamma) But if your display has e.g. gamma=3.0, don't
  convert or compensate.

This is a formalization of what you get when you spend decades ignoring
color management, and people build things based on behavior-in-practice,
not behavior-in-theory.

Also:
+ gfx.color_management.native_srgb:true for Windows, so we don't use the
  display color profile, which no one else does.
+ Add rec2020_gamma_as_rec709, so we have a path towards maybe having
  rec2020 use its correct transfer function, rather than srgb (like
  rec709).

Differential Revision: https://phabricator.services.mozilla.com/D161857
This commit is contained in:
Kelsey Gilbert 2023-03-13 21:04:10 +00:00
Родитель 000ff9b4e5
Коммит 2a633b2502
10 изменённых файлов: 325 добавлений и 30 удалений

Просмотреть файл

@ -17,7 +17,7 @@ defaults pref(media.av1.enabled,true)
fuzzy(16-51,5234-5622) fuzzy-if(swgl,32-38,1600-91746) fuzzy-if(useDrawSnapshot,16-16,11600-11600) fuzzy-if(OSX,16-73,5212-5622) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm ../reftest_img.html?src=color_quads/720p.png
fuzzy-if(winWidget&&swgl,0-20,0-5620) fuzzy-if(winWidget&&!swgl,0-1,0-78) fuzzy-if(Android,254-255,273680-273807) fuzzy-if(OSX,0-35,0-1947) fuzzy-if(OSX&&swgl,0-67,0-5451) fuzzy-if(appleSilicon,30-48,1760-187409) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.vp9.webm ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm
fuzzy-if(winWidget,0-1,0-78) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm
skip-if(winWidget&&isCoverageBuild) fuzzy(0-16,75-1861) fuzzy-if(Android,254-255,273680-273807) fuzzy-if(OSX,30-32,187326-187407) fuzzy-if(appleSilicon,30-48,1835-187409) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.h264.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm
skip-if(winWidget&&isCoverageBuild) fuzzy(0-16,75-1941) fuzzy-if(Android,254-255,273680-273807) fuzzy-if(OSX,30-32,187326-187407) fuzzy-if(appleSilicon,30-48,1835-187409) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.h264.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm
fuzzy-if(winWidget&&swgl,0-20,0-5620) fuzzy-if(winWidget&&!swgl,0-1,0-78) fuzzy-if(Android,254-255,273680-273807) fuzzy-if(OSX,0-35,0-1947) fuzzy-if(OSX&&swgl,0-67,0-5451) fuzzy-if(appleSilicon,30-48,1760-187409) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.vp9.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm
skip-if(Android) fuzzy(16-48,8107-8818) fuzzy-if(winWidget&&swgl,31-38,8240-184080) fuzzy-if(appleSilicon,33-38,8819-11705) fuzzy-if(useDrawSnapshot,20-20,187200-187200) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.webm ../reftest_img.html?src=color_quads/720p.png

Просмотреть файл

@ -30,6 +30,10 @@
#undef NTDDI_VERSION
#define NTDDI_VERSION NTDDI_WINBLUE
// We also need this, or dcomp.h won't give us e.g. IDCompositionDevice3:
#undef _WIN32_WINNT_WINTHRESHOLD
#define _WIN32_WINNT_WINTHRESHOLD _WIN32_WINNT
#include <d3d11.h>
#include <dcomp.h>
#include <ddraw.h>
@ -50,6 +54,7 @@ decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr;
// It should only be used within CreateDirectCompositionDevice.
decltype(DCompositionCreateDevice2)* sDcompCreateDevice2Fn = nullptr;
decltype(DCompositionCreateDevice3)* sDcompCreateDevice3Fn = nullptr;
// It should only be used within CreateDCompSurfaceHandle
decltype(DCompositionCreateSurfaceHandle)* sDcompCreateSurfaceHandleFn =
@ -117,7 +122,7 @@ bool DeviceManagerDx::LoadDcomp() {
MOZ_ASSERT(gfxVars::UseWebRenderDCompWin());
if (sDcompCreateDevice2Fn) {
return true;
return true; // Already loaded.
}
nsModuleHandle module(LoadLibrarySystem32(L"dcomp.dll"));
@ -127,6 +132,8 @@ bool DeviceManagerDx::LoadDcomp() {
sDcompCreateDevice2Fn = (decltype(DCompositionCreateDevice2)*)GetProcAddress(
module, "DCompositionCreateDevice2");
sDcompCreateDevice3Fn = (decltype(DCompositionCreateDevice3)*)GetProcAddress(
module, "DCompositionCreateDevice3");
if (!sDcompCreateDevice2Fn) {
return false;
}
@ -135,9 +142,6 @@ bool DeviceManagerDx::LoadDcomp() {
sDcompCreateSurfaceHandleFn =
(decltype(DCompositionCreateSurfaceHandle)*)::GetProcAddress(
module, "DCompositionCreateSurfaceHandle");
if (!sDcompCreateDevice2Fn) {
return false;
}
mDcompModule.steal(module);
return true;
@ -448,10 +452,16 @@ void DeviceManagerDx::CreateDirectCompositionDeviceLocked() {
HRESULT hr;
RefPtr<IDCompositionDesktopDevice> desktopDevice;
MOZ_SEH_TRY {
hr = sDcompCreateDevice2Fn(
hr = sDcompCreateDevice3Fn(
dxgiDevice.get(),
IID_PPV_ARGS(
(IDCompositionDesktopDevice**)getter_AddRefs(desktopDevice)));
if (!desktopDevice) {
hr = sDcompCreateDevice2Fn(
dxgiDevice.get(),
IID_PPV_ARGS(
(IDCompositionDesktopDevice**)getter_AddRefs(desktopDevice)));
}
}
MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { return; }

Просмотреть файл

@ -2037,15 +2037,10 @@ DeviceColor gfxPlatform::TransformPixel(const sRGBColor& in,
return DeviceColor(in.r, in.g, in.b, in.a);
}
nsTArray<uint8_t> gfxPlatform::GetPlatformCMSOutputProfileData() {
return GetPrefCMSOutputProfileData();
}
nsTArray<uint8_t> gfxPlatform::GetPrefCMSOutputProfileData() {
nsAutoCString fname;
Preferences::GetCString("gfx.color_management.display_profile", fname);
if (fname.IsEmpty()) {
const auto mirror = StaticPrefs::gfx_color_management_display_profile();
const auto fname = *mirror;
if (fname == "") {
return nsTArray<uint8_t>();
}

Просмотреть файл

@ -753,7 +753,9 @@ class gfxPlatform : public mozilla::layers::MemoryPressureListener {
* Returns a buffer containing the CMS output profile data. The way this
* is obtained is platform-specific.
*/
virtual nsTArray<uint8_t> GetPlatformCMSOutputProfileData();
virtual nsTArray<uint8_t> GetPlatformCMSOutputProfileData() {
return GetPrefCMSOutputProfileData();
}
/**
* Return information on how child processes should initialize graphics
@ -865,13 +867,15 @@ class gfxPlatform : public mozilla::layers::MemoryPressureListener {
virtual void ImportContentDeviceData(
const mozilla::gfx::ContentDeviceData& aData);
public:
/**
* Returns the contents of the file pointed to by the
* gfx.color_management.display_profile pref, if set.
* Returns an empty array if not set, or if an error occurs
*/
nsTArray<uint8_t> GetPrefCMSOutputProfileData();
static nsTArray<uint8_t> GetPrefCMSOutputProfileData();
protected:
/**
* If inside a child process and currently being initialized by the
* SetXPCOMProcessAttributes message, this can be used by subclasses to

Просмотреть файл

@ -1036,16 +1036,21 @@ nsTArray<uint8_t> gfxWindowsPlatform::GetPlatformCMSOutputProfileData() {
return result;
}
if (!mCachedOutputColorProfile.IsEmpty()) {
return mCachedOutputColorProfile.Clone();
}
return GetPlatformCMSOutputProfileData_Impl();
}
mCachedOutputColorProfile = [&] {
nsTArray<uint8_t> prefProfileData = GetPrefCMSOutputProfileData();
nsTArray<uint8_t> gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl() {
static nsTArray<uint8_t> sCached = [&] {
// Check override pref first:
nsTArray<uint8_t> prefProfileData =
gfxPlatform::GetPrefCMSOutputProfileData();
if (!prefProfileData.IsEmpty()) {
return prefProfileData;
}
// -
// Otherwise, create a dummy DC and pull from that.
HDC dc = ::GetDC(nullptr);
if (!dc) {
return nsTArray<uint8_t>();
@ -1078,7 +1083,7 @@ nsTArray<uint8_t> gfxWindowsPlatform::GetPlatformCMSOutputProfileData() {
return result;
}();
return mCachedOutputColorProfile.Clone();
return sCached.Clone();
}
void gfxWindowsPlatform::GetDLLVersion(char16ptr_t aDLLPath,

Просмотреть файл

@ -200,7 +200,13 @@ class gfxWindowsPlatform final : public gfxPlatform {
protected:
bool AccelerateLayersByDefault() override { return true; }
nsTArray<uint8_t> GetPlatformCMSOutputProfileData() override;
public:
static nsTArray<uint8_t> GetPlatformCMSOutputProfileData_Impl();
protected:
void GetPlatformDisplayInfo(mozilla::widget::InfoObject& aObj) override;
void ImportGPUDeviceData(const mozilla::gfx::GPUDeviceData& aData) override;

Просмотреть файл

@ -6,6 +6,7 @@
#include "DCLayerTree.h"
#include "gfxWindowsPlatform.h"
#include "GLContext.h"
#include "GLContextEGL.h"
#include "mozilla/gfx/DeviceManagerDx.h"
@ -28,6 +29,10 @@
#undef NTDDI_VERSION
#define NTDDI_VERSION NTDDI_WINBLUE
// We also need this, or dcomp.h won't give us e.g. IDCompositionFilterEffect:
#undef _WIN32_WINNT_WINTHRESHOLD
#define _WIN32_WINNT_WINTHRESHOLD _WIN32_WINNT
#include <d3d11.h>
#include <d3d11_1.h>
#include <dcomp.h>
@ -567,6 +572,16 @@ void DCExternalSurfaceWrapper::AttachExternalImage(
}
}
template <class ToT>
struct QI {
template <class FromT>
[[nodiscard]] static inline RefPtr<ToT> From(FromT* const from) {
RefPtr<ToT> to;
(void)from->QueryInterface(static_cast<ToT**>(getter_AddRefs(to)));
return to;
}
};
DCSurface* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage(
wr::ExternalImageId aExternalImage) {
if (mSurface) {
@ -591,14 +606,117 @@ DCSurface* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage(
mSurface = nullptr;
}
}
if (mSurface) {
// Add surface's visual which will contain video data to our root visual.
mVisual->AddVisual(mSurface->GetVisual(), TRUE, nullptr);
} else {
if (!mSurface) {
gfxCriticalNote << "Failed to create a surface for external image: "
<< gfx::hexa(texture);
return nullptr;
}
const auto textureSwgl = texture->AsRenderTextureHostSWGL();
MOZ_ASSERT(textureSwgl); // Covered above.
// Add surface's visual which will contain video data to our root visual.
const auto surfaceVisual = mSurface->GetVisual();
mVisual->AddVisual(surfaceVisual, true, nullptr);
// -
// Apply color management.
[&]() {
const auto cmsMode = GfxColorManagementMode();
if (cmsMode == CMSMode::Off) return;
const auto dcomp = mDCLayerTree->GetCompositionDevice();
const auto dcomp3 = QI<IDCompositionDevice3>::From(dcomp);
if (!dcomp3) {
NS_WARNING(
"No IDCompositionDevice3, cannot use dcomp for color management.");
return;
}
// -
const auto cspace = [&]() {
const auto rangedCspace = textureSwgl->GetYUVColorSpace();
const auto info = FromYUVRangedColorSpace(rangedCspace);
auto ret = ToColorSpace2(info.space);
if (ret == gfx::ColorSpace2::Display && cmsMode == CMSMode::All) {
ret = gfx::ColorSpace2::SRGB;
}
return ret;
}();
const bool rec709GammaAsSrgb =
StaticPrefs::gfx_color_management_rec709_gamma_as_srgb();
const bool rec2020GammaAsRec709 =
StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709();
auto cspaceDesc = color::ColorspaceDesc{};
switch (cspace) {
case gfx::ColorSpace2::Display:
return; // No color management needed!
case gfx::ColorSpace2::SRGB:
cspaceDesc.chrom = color::Chromaticities::Srgb();
cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
break;
case gfx::ColorSpace2::DISPLAY_P3:
cspaceDesc.chrom = color::Chromaticities::DisplayP3();
cspaceDesc.tf = color::PiecewiseGammaDesc::DisplayP3();
break;
case gfx::ColorSpace2::BT601_525:
cspaceDesc.chrom = color::Chromaticities::Rec601_525_Ntsc();
if (rec709GammaAsSrgb) {
cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
} else {
cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
}
break;
case gfx::ColorSpace2::BT709:
cspaceDesc.chrom = color::Chromaticities::Rec709();
if (rec709GammaAsSrgb) {
cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
} else {
cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
}
break;
case gfx::ColorSpace2::BT2020:
cspaceDesc.chrom = color::Chromaticities::Rec2020();
if (rec2020GammaAsRec709 && rec709GammaAsSrgb) {
cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
} else if (rec2020GammaAsRec709) {
cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
} else {
// Just Rec709 with slightly more precision.
cspaceDesc.tf = color::PiecewiseGammaDesc::Rec2020_12bit();
}
break;
}
const auto cprofileIn = color::ColorProfileDesc::From(cspaceDesc);
auto cprofileOut = mDCLayerTree->OutputColorProfile();
bool pretendSrgb = StaticPrefs::gfx_color_management_native_srgb();
if (pretendSrgb) {
cprofileOut = color::ColorProfileDesc::From({
color::Chromaticities::Srgb(),
color::PiecewiseGammaDesc::Srgb(),
});
}
const auto conversion = color::ColorProfileConversionDesc::From({
.src = cprofileIn,
.dst = cprofileOut,
});
// -
auto chain = ColorManagementChain::From(*dcomp3, conversion);
mCManageChain = Some(chain);
surfaceVisual->SetEffect(mCManageChain->last.get());
}();
return mSurface.get();
}
@ -1615,6 +1733,114 @@ void DCLayerTree::DestroyEGLSurface() {
}
}
// -
color::ColorProfileDesc DCLayerTree::QueryOutputColorProfile() {
// GPU process can't simply init gfxPlatform, (and we don't need most of it)
// but we do need gfxPlatform::GetCMSOutputProfile().
// So we steal what we need through the window:
const auto outputProfileData =
gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl();
const auto qcmsProfile = qcms_profile_from_memory(
outputProfileData.Elements(), outputProfileData.Length());
MOZ_ASSERT(qcmsProfile);
const auto release =
MakeScopeExit([&]() { qcms_profile_release(qcmsProfile); });
const auto ret = color::ColorProfileDesc::From(*qcmsProfile);
bool print = gfxEnv::MOZ_GL_SPEW();
if (print) {
const auto gammaGuess = color::GuessGamma(ret.linearFromTf.r);
printf_stderr(
"Display profile:\n"
" Approx Gamma: %f\n"
" XYZ-D65 Red : %f, %f, %f\n"
" XYZ-D65 Green: %f, %f, %f\n"
" XYZ-D65 Blue : %f, %f, %f\n",
gammaGuess, ret.xyzd65FromLinearRgb.at(0, 0),
ret.xyzd65FromLinearRgb.at(0, 1), ret.xyzd65FromLinearRgb.at(0, 2),
ret.xyzd65FromLinearRgb.at(1, 0), ret.xyzd65FromLinearRgb.at(1, 1),
ret.xyzd65FromLinearRgb.at(1, 2),
ret.xyzd65FromLinearRgb.at(2, 0), ret.xyzd65FromLinearRgb.at(2, 1),
ret.xyzd65FromLinearRgb.at(2, 2));
}
return ret;
}
inline D2D1_MATRIX_5X4_F to_D2D1_MATRIX_5X4_F(const color::mat4& m) {
return D2D1_MATRIX_5X4_F{{{
m.rows[0][0],
m.rows[1][0],
m.rows[2][0],
m.rows[3][0],
m.rows[0][1],
m.rows[1][1],
m.rows[2][1],
m.rows[3][1],
m.rows[0][2],
m.rows[1][2],
m.rows[2][2],
m.rows[3][2],
m.rows[0][3],
m.rows[1][3],
m.rows[2][3],
m.rows[3][3],
0,
0,
0,
0,
}}};
}
ColorManagementChain ColorManagementChain::From(
IDCompositionDevice3& dcomp,
const color::ColorProfileConversionDesc& conv) {
auto ret = ColorManagementChain{};
const auto Append = [&](const RefPtr<IDCompositionFilterEffect>& afterLast) {
if (ret.last) {
afterLast->SetInput(0, ret.last, 0);
}
ret.last = afterLast;
};
const auto MaybeAppendColorMatrix = [&](const color::mat4& m) {
RefPtr<IDCompositionColorMatrixEffect> e;
if (approx(m, color::mat4::Identity())) return e;
dcomp.CreateColorMatrixEffect(getter_AddRefs(e));
MOZ_ASSERT(e);
if (!e) return e;
e->SetMatrix(to_D2D1_MATRIX_5X4_F(m));
Append(e);
return e;
};
const auto MaybeAppendTableTransfer = [&](const color::RgbTransferTables& t) {
RefPtr<IDCompositionTableTransferEffect> e;
if (!t.r.size() && !t.g.size() && !t.b.size()) return e;
dcomp.CreateTableTransferEffect(getter_AddRefs(e));
MOZ_ASSERT(e);
if (!e) return e;
e->SetRedTable(t.r.data(), t.r.size());
e->SetGreenTable(t.g.data(), t.g.size());
e->SetBlueTable(t.b.data(), t.b.size());
Append(e);
return e;
};
ret.srcRgbFromSrcYuv = MaybeAppendColorMatrix(conv.srcRgbFromSrcYuv);
ret.srcLinearFromSrcTf = MaybeAppendTableTransfer(conv.srcLinearFromSrcTf);
ret.dstLinearFromSrcLinear =
MaybeAppendColorMatrix(color::mat4(conv.dstLinearFromSrcLinear));
ret.dstTfFromDstLinear = MaybeAppendTableTransfer(conv.dstTfFromDstLinear);
return ret;
}
ColorManagementChain::~ColorManagementChain() = default;
} // namespace wr
} // namespace mozilla

Просмотреть файл

@ -12,6 +12,7 @@
#include <vector>
#include <windows.h>
#include "Colorspaces.h"
#include "GLTypes.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/layers/OverlayInfo.h"
@ -27,7 +28,11 @@ struct ID3D11VideoContext;
struct ID3D11VideoProcessor;
struct ID3D11VideoProcessorEnumerator;
struct ID3D11VideoProcessorOutputView;
struct IDCompositionColorMatrixEffect;
struct IDCompositionFilterEffect;
struct IDCompositionTableTransferEffect;
struct IDCompositionDevice2;
struct IDCompositionDevice3;
struct IDCompositionSurface;
struct IDCompositionTarget;
struct IDCompositionVisual2;
@ -66,6 +71,23 @@ struct GpuOverlayInfo {
UINT mRgb10a2OverlaySupportFlags = 0;
};
// -
struct ColorManagementChain {
RefPtr<IDCompositionColorMatrixEffect> srcRgbFromSrcYuv;
RefPtr<IDCompositionTableTransferEffect> srcLinearFromSrcTf;
RefPtr<IDCompositionColorMatrixEffect> dstLinearFromSrcLinear;
RefPtr<IDCompositionTableTransferEffect> dstTfFromDstLinear;
RefPtr<IDCompositionFilterEffect> last;
static ColorManagementChain From(IDCompositionDevice3& dcomp,
const color::ColorProfileConversionDesc&);
~ColorManagementChain();
};
// -
/**
* DCLayerTree manages direct composition layers.
* It does not manage gecko's layers::Layer.
@ -210,6 +232,19 @@ class DCLayerTree {
bool mPendingCommit;
static color::ColorProfileDesc QueryOutputColorProfile();
mutable Maybe<color::ColorProfileDesc> mOutputColorProfile;
public:
const color::ColorProfileDesc& OutputColorProfile() const {
if (!mOutputColorProfile) {
mOutputColorProfile = Some(QueryOutputColorProfile());
}
return *mOutputColorProfile;
}
protected:
static UniquePtr<GpuOverlayInfo> sGpuOverlayInfo;
};
@ -322,6 +357,7 @@ class DCExternalSurfaceWrapper : public DCSurface {
UniquePtr<DCSurface> mSurface;
const bool mIsOpaque;
Maybe<ColorManagementChain> mCManageChain;
};
class DCSurfaceVideo : public DCSurface {

Просмотреть файл

@ -5767,6 +5767,11 @@
#endif
mirror: always
- name: gfx.color_management.display_profile
type: DataMutexString
value: ""
mirror: always # But be warned: We cache the result.
- name: gfx.color_management.force_srgb
type: RelaxedAtomicBool
value: false
@ -5774,7 +5779,7 @@
- name: gfx.color_management.native_srgb
type: RelaxedAtomicBool
#if defined(XP_MACOSX)
#if defined(XP_MACOSX) || defined(XP_WIN)
value: true
#else
value: false
@ -5799,6 +5804,16 @@
value: 0
mirror: always
- name: gfx.color_management.rec709_gamma_as_srgb
type: RelaxedAtomicBool
value: true # Tragic backwards compat.
mirror: always
- name: gfx.color_management.rec2020_gamma_as_rec709
type: RelaxedAtomicBool
value: true # Match naive behavior, but hopefully we can stop soon!
mirror: always
- name: gfx.compositor.clearstate
type: RelaxedAtomicBool
value: false

Просмотреть файл

@ -449,8 +449,6 @@ pref("formhelper.autozoom.force-disable.test-only", false);
pref("gfx.hidpi.enabled", 2);
#endif
pref("gfx.color_management.display_profile", "");
pref("gfx.downloadable_fonts.enabled", true);
pref("gfx.downloadable_fonts.fallback_delay", 3000);
pref("gfx.downloadable_fonts.fallback_delay_short", 100);